1a7abfd059
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
## 交付内容
- 客户端新增 `project_snapshot` 模块:项目扫描与排除口径、增量索引、差异对比、上传编排、状态查询。
- 触发:工作区窗口存活期间的周期定时器 + 工作区窗口关闭(`CloseRequested`);应用退出只在有界预算内等待在途同步收尾,不重复发起。
- 服务端新增两条登录态路由 `POST /api/agc/project-snapshots/files` 与 `/manifest`,由 api-server 用服务端凭据写入私有前缀 `agc/project-snapshots/v1/{user}/{project}/`;客户端不持有 OSS 凭据、不直连 OSS。
- `platform-oss` 新增内部对象精确写入与探测、项目快照对象键构造;修复 `head_internal_object` 读 HEAD 响应长度恒为 0(会导致服务端"已存在即跳过"永不生效)。
- `shared-contracts` 新增 `agc_project_snapshots` DTO 与项目 ID、相对路径、摘要校验。
- 真实 OSS 存储层冒烟示例 + 客户端真实链路冒烟用例(`#[ignore]`,env 驱动)。
- 登记 `check-config.mjs` 的 native-only 命令白名单,恢复 `npm run agc` 可启动。
## 验证证据
- 存储层:`cargo run -p platform-oss --example agc_project_snapshot_live_smoke` 对真实 `agc-dev` 完成写入 → 读回(contentLength 32)→ 清单写入 → 探针清理。
- HTTP 层:登录态下 7 项校验(401 无 token、400 路径穿越/长度不一致/非法摘要/空正文/重复路径清单)全部符合预期且不写对象。
- 客户端链路(真实项目 gameagent-033b6cf3…):第一次 `synced uploaded=6 uploadedBytes=36694 remoteSkipped=6`,紧接着第二次 `no-op uploaded=0`。
- GUI 触发:`trigger=periodic ... uploaded=6`;把周期设为 600 秒排除干扰、改一个文件后关窗得到 `trigger=project-close ... revision=2 uploaded=1`,索引摘要由 `28d837cd84f8ab62` 推进到 `bff34e2e336d901f`(同步在进程退出前完成)。
- 门禁:AGC `project_snapshot` 14 passed(+1 ignored live)、api-server `project_snapshots` 3 passed、platform-oss 39 passed、shared-contracts 87 passed、`cargo fmt --check`(两处)、`check:encoding`、`check:doc-index`、`git diff --check`、`check-config.mjs` 全部通过。
## 已知未决(不阻塞本里程碑)
- 远端对象只增不减:没有删除路径,也没有 bucket 生命周期规则;要收口需要先定保留语义(清单引用 GC 还是 OSS 生命周期)。
- 部署环境需确认 `GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_*` 或回退的 `ALIYUN_OSS_*` 具备目标 bucket 私有前缀的 `PutObject` 权限;未配置时接口返回 503、客户端失败关闭(不推进索引)。
- 缺少按用户/项目的配额与限流(error_reports 有每小时提交上限的先例,本功能没有)。
- 没有界面入口与状态可见性:`read_local_project_snapshot_state` 已注册但未接 UI,失败只写本机日志。
## 不做
- 不做云端下载/恢复、跨设备合并、版本回滚。
- 不改 `/api/external/v1` 与 External OpenAPI,不新增 SpacetimeDB 表。
---------
Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/400
198 lines
9.9 KiB
Bash
198 lines
9.9 KiB
Bash
# 复制到 /etc/genarrative/api-server.env 后再填入真实生产值。
|
||
# 该文件只能保存在生产服务器,不进入构建产物,不提交真实密钥。
|
||
|
||
GENARRATIVE_ENV=production
|
||
GENARRATIVE_API_HOST=127.0.0.1
|
||
GENARRATIVE_API_PORT=8082
|
||
GENARRATIVE_API_LOG=info,tower_http=info
|
||
GENARRATIVE_API_LISTEN_BACKLOG=1024
|
||
GENARRATIVE_API_WORKER_THREADS=4
|
||
# api 只监听 HTTP;外部生成 worker 用独立进程设置为 external-generation-worker 后横向扩缩。
|
||
GENARRATIVE_PROCESS_ROLE=api
|
||
# 默认 queue 进入 external_generation_job;本地/小流量同步排查可显式设 inline。
|
||
GENARRATIVE_EXTERNAL_GENERATION_MODE=queue
|
||
GENARRATIVE_EXTERNAL_GENERATION_WORKER_ID=
|
||
GENARRATIVE_EXTERNAL_GENERATION_WORKER_CONCURRENCY=2
|
||
GENARRATIVE_EXTERNAL_GENERATION_WORKER_POLL_INTERVAL_MS=2000
|
||
GENARRATIVE_EXTERNAL_GENERATION_WORKER_LEASE_SECONDS=600
|
||
GENARRATIVE_EXTERNAL_GENERATION_WORKER_JOB_TIMEOUT_SECONDS=900
|
||
GENARRATIVE_EXTERNAL_GENERATION_WORKER_LONG_JOB_TIMEOUT_SECONDS=1800
|
||
# 父流程只访问同机 BgFilter worker;内部 Token 只通过受保护文件共享,不写明文 env。
|
||
GENARRATIVE_BGFILTER_WORKER_BASE_URL=http://127.0.0.1:8083
|
||
GENARRATIVE_BGFILTER_INTERNAL_TOKEN_FILE=/etc/genarrative/secrets/bgfilter-worker.token
|
||
GENARRATIVE_BGFILTER_WORKER_CONNECT_TIMEOUT_MS=2000
|
||
GENARRATIVE_API_MAX_CONCURRENT_REQUESTS=512
|
||
GENARRATIVE_API_ADMIN_MAX_CONCURRENT_REQUESTS=16
|
||
GENARRATIVE_API_SHUTDOWN_OUTBOX_FLUSH_TIMEOUT_MS=5000
|
||
GENARRATIVE_TRACKING_OUTBOX_ENABLED=true
|
||
GENARRATIVE_TRACKING_OUTBOX_DIR=/var/lib/genarrative/tracking-outbox
|
||
GENARRATIVE_TRACKING_OUTBOX_BATCH_SIZE=500
|
||
GENARRATIVE_TRACKING_OUTBOX_FLUSH_INTERVAL_MS=1000
|
||
GENARRATIVE_TRACKING_OUTBOX_MAX_BYTES=268435456
|
||
GENARRATIVE_WALLET_REFUND_OUTBOX_ENABLED=true
|
||
GENARRATIVE_WALLET_REFUND_OUTBOX_DIR=/var/lib/genarrative/wallet-refund-outbox
|
||
GENARRATIVE_WALLET_REFUND_OUTBOX_BATCH_SIZE=100
|
||
GENARRATIVE_WALLET_REFUND_OUTBOX_FLUSH_INTERVAL_MS=1000
|
||
GENARRATIVE_WALLET_REFUND_OUTBOX_MAX_BYTES=67108864
|
||
# N 与单图估时:父子共同派生 attempt = N×est×2、callBudget = 2×attempt+1s 的公式输入;
|
||
# 单一来源放共享 env,专属 worker env 不重复定义。旧固定 REQUEST_TIMEOUT_MS 已删除。
|
||
GENARRATIVE_BGFILTER_WORKER_CONCURRENCY=16
|
||
GENARRATIVE_EDITOR_BGFILTER_SINGLE_IMAGE_ESTIMATE_MS=5000
|
||
GENARRATIVE_EDITOR_BGFILTER_BASE_URL=http://58.87.105.82/bgfilter
|
||
GENARRATIVE_EDITOR_BGFILTER_TOKEN=
|
||
# BgFilter 失败后的中间兜底:阿里云通用抠图(SegmentCommonImage)。AccessKey 留空则跳过该层,
|
||
# BgFilter 失败直接本地 editor_green_screen 去背;填入后恢复 BgFilter→阿里云→本地三级兜底。
|
||
# AccessKey 也可复用标准 SDK 命名 ALIBABA_CLOUD_ACCESS_KEY_ID / ALIBABA_CLOUD_ACCESS_KEY_SECRET。
|
||
GENARRATIVE_ALIYUN_MATTING_ENABLED=true
|
||
GENARRATIVE_ALIYUN_MATTING_ENDPOINT=imageseg.cn-shanghai.aliyuncs.com
|
||
GENARRATIVE_ALIYUN_MATTING_ACCESS_KEY_ID=
|
||
GENARRATIVE_ALIYUN_MATTING_ACCESS_KEY_SECRET=
|
||
GENARRATIVE_ALIYUN_MATTING_REQUEST_TIMEOUT_MS=30000
|
||
GENARRATIVE_OTEL_ENABLED=true
|
||
OTEL_SERVICE_NAME=genarrative-api
|
||
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
|
||
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.namespace=genarrative
|
||
|
||
GENARRATIVE_ADMIN_USERNAME=
|
||
GENARRATIVE_ADMIN_PASSWORD=
|
||
GENARRATIVE_ADMIN_TOKEN_TTL_SECONDS=14400
|
||
GENARRATIVE_INTERNAL_API_SECRET=CHANGE_ME_FOR_PRODUCTION
|
||
|
||
GENARRATIVE_JWT_ISSUER=genarrative-production
|
||
GENARRATIVE_JWT_SECRET=CHANGE_ME_FOR_PRODUCTION
|
||
GENARRATIVE_JWT_ACCESS_TOKEN_TTL_SECONDS=7200
|
||
AUTH_REFRESH_COOKIE_NAME=genarrative_refresh_session
|
||
AUTH_REFRESH_SESSION_TTL_DAYS=30
|
||
AUTH_REFRESH_COOKIE_PATH=/api/auth
|
||
AUTH_REFRESH_COOKIE_SAME_SITE=Lax
|
||
AUTH_REFRESH_COOKIE_SECURE=true
|
||
GENARRATIVE_DEV_PASSWORD_ENTRY_AUTO_REGISTER_ENABLED=false
|
||
GENARRATIVE_ENABLE_IMAGE_EDITOR_AGENT_SIDEBAR=false
|
||
|
||
GENARRATIVE_SPACETIME_SERVER_URL=http://127.0.0.1:3101
|
||
GENARRATIVE_SPACETIME_DATABASE=genarrative-prod
|
||
GENARRATIVE_SPACETIME_TOKEN=
|
||
# 只保存受保护文件路径;不要把 bootstrap secret 明文写入生产 env。
|
||
GENARRATIVE_SPACETIME_RUNTIME_SERVICE_BOOTSTRAP_SECRET_FILE=/var/lib/genarrative/spacetime/runtime-service-bootstrap-secret.txt
|
||
# HTTP 角色使用 8 条无 read-model 订阅的调用连接,并额外创建 1 条共享缓存读连接。
|
||
GENARRATIVE_SPACETIME_POOL_SIZE=8
|
||
GENARRATIVE_SPACETIME_PROCEDURE_TIMEOUT_SECONDS=45
|
||
|
||
GENARRATIVE_LLM_PROVIDER=openai-compatible
|
||
GENARRATIVE_LLM_BASE_URL=https://api.tiantoken.com/v1
|
||
GENARRATIVE_LLM_API_KEY=
|
||
GENARRATIVE_LLM_MODEL=gpt-5.4-mini
|
||
# LLM Router 正式账号链路:production 固定使用官方地址/模型;管理员 Token 只读受保护文件。
|
||
GENARRATIVE_LLM_ROUTER_BASE_URL=https://router.genarrative.world/v1
|
||
GENARRATIVE_LLM_ROUTER_PROVISIONING_SECRET_FILE=/etc/genarrative/secrets/llm-router-provisioning.secret
|
||
GENARRATIVE_LLM_ROUTER_API_KEY_ENCRYPTION_SECRET_FILE=/etc/genarrative/secrets/llm-router-api-key-encryption.secret
|
||
GENARRATIVE_LLM_ROUTER_ADMIN_TOKEN_FILE=/etc/genarrative/secrets/llm-router-admin.token
|
||
TIANTOKEN_BASE_URL=https://api.tiantoken.com
|
||
TIANTOKEN_API_KEY=
|
||
TIANTOKEN_IMAGE_REQUEST_TIMEOUT_MS=1000000
|
||
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.cn
|
||
VECTOR_ENGINE_API_KEY=
|
||
VECTOR_ENGINE_AUDIO_REQUEST_TIMEOUT_MS=180000
|
||
ELEVENLABS_BASE_URL=https://api.elevenlabs.io
|
||
ELEVENLABS_API_KEY=
|
||
ELEVENLABS_REQUEST_TIMEOUT_MS=180000
|
||
|
||
HYPER3D_BASE_URL=https://api.hyper3d.com/api/v2
|
||
HYPER3D_API_KEY=
|
||
HYPER3D_MODEL_REQUEST_TIMEOUT_MS=180000
|
||
|
||
VOLCENGINE_SPEECH_API_KEY=
|
||
VOLCENGINE_SPEECH_APP_ID=
|
||
VOLCENGINE_SPEECH_ACCESS_KEY=
|
||
VOLCENGINE_SPEECH_ASR_RESOURCE_ID=volc.seedasr.sauc.concurrent
|
||
VOLCENGINE_SPEECH_TTS_RESOURCE_ID=seed-tts-2.0
|
||
VOLCENGINE_SPEECH_REQUEST_TIMEOUT_MS=180000
|
||
VOLCENGINE_SPEECH_ASR_WS_URL=wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async
|
||
VOLCENGINE_SPEECH_TTS_BIDIRECTION_WS_URL=wss://openspeech.bytedance.com/api/v3/tts/bidirection
|
||
VOLCENGINE_SPEECH_TTS_SSE_URL=https://openspeech.bytedance.com/api/v3/tts/unidirectional/sse
|
||
|
||
ARK_CHARACTER_VIDEO_BASE_URL=
|
||
ARK_CHARACTER_VIDEO_API_KEY=
|
||
ARK_CHARACTER_VIDEO_MODEL=
|
||
ARK_CHARACTER_VIDEO_REQUEST_TIMEOUT_MS=420000
|
||
|
||
DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
|
||
DASHSCOPE_API_KEY=
|
||
DASHSCOPE_SCENE_IMAGE_MODEL=
|
||
DASHSCOPE_REFERENCE_IMAGE_MODEL=
|
||
DASHSCOPE_COVER_IMAGE_MODEL=
|
||
DASHSCOPE_IMAGE_REQUEST_TIMEOUT_MS=150000
|
||
DASHSCOPE_CHARACTER_VISUAL_MODEL=
|
||
DASHSCOPE_CHARACTER_IMAGE_SEQUENCE_MODEL=
|
||
DASHSCOPE_CHARACTER_REFERENCE_VIDEO_MODEL=
|
||
DASHSCOPE_CHARACTER_MOTION_TRANSFER_MODEL=
|
||
DASHSCOPE_CHARACTER_VIDEO_REQUEST_TIMEOUT_MS=420000
|
||
|
||
SMS_AUTH_ENABLED=false
|
||
SMS_AUTH_PROVIDER=aliyun
|
||
ALIYUN_SMS_ACCESS_KEY_ID=
|
||
ALIYUN_SMS_ACCESS_KEY_SECRET=
|
||
ALIYUN_SMS_ENDPOINT=dysmsapi.aliyuncs.com
|
||
ALIYUN_SMS_SIGN_NAME=北京亓盒网络科技
|
||
ALIYUN_SMS_TEMPLATE_CODE=SMS_506245486
|
||
ALIYUN_SMS_TEMPLATE_PARAM_KEY=code
|
||
ALIYUN_SMS_COUNTRY_CODE=86
|
||
|
||
WECHAT_AUTH_ENABLED=false
|
||
WECHAT_AUTH_PROVIDER=real
|
||
WECHAT_APP_ID=
|
||
WECHAT_APP_SECRET=
|
||
WECHAT_CALLBACK_PATH=/api/auth/wechat/callback
|
||
WECHAT_REDIRECT_PATH=/
|
||
WECHAT_AUTHORIZE_ENDPOINT=https://open.weixin.qq.com/connect/qrconnect
|
||
WECHAT_ACCESS_TOKEN_ENDPOINT=https://api.weixin.qq.com/sns/oauth2/access_token
|
||
WECHAT_USER_INFO_ENDPOINT=https://api.weixin.qq.com/sns/userinfo
|
||
WECHAT_STATE_TTL_MINUTES=15
|
||
WECHAT_MINIPROGRAM_MESSAGE_TOKEN=
|
||
WECHAT_MINIPROGRAM_MESSAGE_ENCODING_AES_KEY=
|
||
# 真实微信支付必须开启退款主动查单与账单对账;生产发布脚本会复核该值。
|
||
WECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true
|
||
|
||
ALIYUN_OSS_BUCKET=
|
||
ALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com
|
||
ALIYUN_OSS_ACCESS_KEY_ID=
|
||
ALIYUN_OSS_ACCESS_KEY_SECRET=
|
||
ALIYUN_OSS_READ_EXPIRE_SECONDS=600
|
||
ALIYUN_OSS_POST_EXPIRE_SECONDS=600
|
||
ALIYUN_OSS_POST_MAX_SIZE_BYTES=20971520
|
||
ALIYUN_OSS_SUCCESS_ACTION_STATUS=200
|
||
|
||
# AGC 项目定时快照上传目标。对象只落在服务端私有前缀
|
||
# agc/project-snapshots/v1/{user}/{project}/ 下;AccessKey 为空时回退 ALIYUN_OSS_ACCESS_KEY_*,
|
||
# 因此回退凭据必须对目标 bucket 具备该前缀的 PutObject/GetObject/DeleteObject 权限。
|
||
# bucket 未单独配置时默认 agc-dev,未配置凭据时 api-server 跳过该客户端,
|
||
# 接口返回 503 且客户端失败关闭(不写空对象、不推进本地索引)。
|
||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_BUCKET=agc-dev
|
||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ENDPOINT=oss-rg-china-mainland.aliyuncs.com
|
||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ACCESS_KEY_ID=
|
||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ACCESS_KEY_SECRET=
|
||
|
||
# SpacetimeDB 数据目录 OSS 冷备份配置。可由 cron / Jenkins 调用发布包内 scripts/database-backup-to-oss.mjs。
|
||
GENARRATIVE_DATABASE_BACKUP_DATA_DIR=/stdb
|
||
GENARRATIVE_DATABASE_BACKUP_WORK_DIR=/var/lib/genarrative/database-backups
|
||
GENARRATIVE_DATABASE_BACKUP_OSS_BUCKET=
|
||
GENARRATIVE_DATABASE_BACKUP_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com
|
||
GENARRATIVE_DATABASE_BACKUP_OSS_PREFIX=database-backups
|
||
GENARRATIVE_DATABASE_BACKUP_KEEP_LOCAL=false
|
||
# 可选:files 为逐文件 CAS + catalog,不生成 tar.gz;archive 保留旧全量压缩包兼容行为。
|
||
GENARRATIVE_DATABASE_BACKUP_STORAGE_FORMAT=archive
|
||
# files 模式并行 PUT/HEAD 数量,必须为 1-64;默认 16。
|
||
GENARRATIVE_DATABASE_BACKUP_FILES_CONCURRENCY=16
|
||
# 可选:仅限制备份进程上传总带宽,单位 bytes/s;为空或 0 表示不限速。
|
||
GENARRATIVE_DATABASE_BACKUP_UPLOAD_MAX_BYTES_PER_SECOND=
|
||
# 可选:显式要求备份工作目录所在文件系统至少保留的可用空间;为空时按数据目录大小 + 安全余量估算。
|
||
GENARRATIVE_DATABASE_BACKUP_MIN_FREE_BYTES=
|
||
# archive history 模式持久化已验真 full baseline 与追加批次;files 模式改用 work-dir 下的 files state。
|
||
GENARRATIVE_DATABASE_BACKUP_BASELINE_STATE=/var/lib/genarrative/database-backups/genarrative-prod-history-state.json
|
||
# 仅 archive history 首次从 uploadStatus=uploaded 的 full manifest 初始化 state 时设置;初始化后可留空。
|
||
GENARRATIVE_DATABASE_BACKUP_BASELINE_MANIFEST=
|
||
# 可选:定时 / publish 前备份使用独立最小权限 AccessKey;为空时回退 ALIYUN_OSS_ACCESS_KEY_*。
|
||
GENARRATIVE_DATABASE_BACKUP_OSS_ACCESS_KEY_ID=
|
||
GENARRATIVE_DATABASE_BACKUP_OSS_ACCESS_KEY_SECRET=
|
||
GENARRATIVE_DATABASE_BACKUP_STOP_SERVICE=spacetimedb.service
|