diff --git a/.github/workflows/_container.yml b/.github/workflows/_container.yml index 6c7366be4..f6f494266 100644 --- a/.github/workflows/_container.yml +++ b/.github/workflows/_container.yml @@ -122,6 +122,8 @@ jobs: compose=(docker compose --project-name "$COMPOSE_PROJECT_NAME" \ -f deploy/docker-compose.yml -f deploy/docker-compose.smoke.yml) "${compose[@]}" up -d --no-build --wait postgres redis + test "$("${compose[@]}" exec -T -e PGPASSWORD="$CPR_POSTGRES_PASSWORD" postgres \ + psql -At -U codex_proxy -d codex_proxy -c "SHOW TimeZone;" | tr -d '\r')" = Asia/Shanghai "${compose[@]}" exec -T -e PGPASSWORD="$CPR_POSTGRES_PASSWORD" postgres \ psql -v ON_ERROR_STOP=1 -U codex_proxy -d codex_proxy -c \ "CREATE TABLE cpr_ci_persistence_probe (value text PRIMARY KEY); INSERT INTO cpr_ci_persistence_probe VALUES ('persisted');" @@ -151,6 +153,8 @@ jobs: test "$status" = 204 test "$("${compose[@]}" exec -T codex-proxy-rs id -u | tr -d '\r')" = 10001 test "$("${compose[@]}" exec -T codex-proxy-rs id -g | tr -d '\r')" = 10001 + test "$("${compose[@]}" exec -T codex-proxy-rs date +%z | tr -d '\r')" = +0800 + test "$("${compose[@]}" exec -T codex-proxy-rs cat /etc/timezone | tr -d '\r')" = Asia/Shanghai "${compose[@]}" exec -T codex-proxy-rs sh -c \ 'touch /app/data/.ci-write /app/logs/.ci-write && rm /app/data/.ci-write /app/logs/.ci-write' diff --git a/deploy/Dockerfile b/deploy/Dockerfile index e79ca05a3..af3ea1b7b 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -51,8 +51,12 @@ COPY --from=release-asset-builder /out / FROM debian:bookworm-slim@sha256:60eac759739651111db372c07be67863818726f754804b8707c90979bda511df AS runtime-base +ENV TZ=Asia/Shanghai + RUN apt-get update \ - && apt-get install -y --no-install-recommends ca-certificates curl \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates curl tzdata \ + && ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime \ + && printf '%s\n' "${TZ}" > /etc/timezone \ && groupadd --system --gid 10001 cpr \ && useradd --system --uid 10001 --gid cpr --home-dir /app --shell /usr/sbin/nologin cpr \ && install -d -o cpr -g cpr /app/bin /app/data /app/logs /app/web \ diff --git a/deploy/README.md b/deploy/README.md index 2889ea4f3..2897396f0 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -26,6 +26,9 @@ chmod 0600 deploy/.env 应用日志同时写入 `docker logs` 与 `.runtime/logs`。Compose 对应用、PostgreSQL、Redis 统一启用 `json-file` 的 `10m × 5` 轮转;应用文件日志还受配置中的自然日、单文件大小和文件总数限制。 +Runtime 镜像和 PostgreSQL 的显示、日志时区固定为 `Asia/Shanghai`。PostgreSQL 时间列仍使用 +`timestamptz` 保存绝对时间点,修改时区只影响查询与日志的显示,不需要迁移或改写已有数据。 + `deploy/.env` 是 Docker 部署的密钥配置文件,已被 Git 忽略且必须保持 mode `0600`。其中 `CPR_ADMIN_DEFAULT_PASSWORD`、`CPR_POSTGRES_PASSWORD`、`CPR_REDIS_PASSWORD` 三项均为必填, 留空时 Compose 会在启动前失败。管理员密码必须至少 12 位且不能是常见弱口令。 diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 9b09fdb92..460c808a0 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -10,7 +10,16 @@ services: postgres: image: postgres:18-bookworm@sha256:d9c83446333daec3f0588cc709adb80c26090b7f9f0f7ec8d43c243385d79818 restart: unless-stopped + command: + [ + "postgres", + "-c", + "timezone=Asia/Shanghai", + "-c", + "log_timezone=Asia/Shanghai", + ] environment: + TZ: Asia/Shanghai POSTGRES_DB: codex_proxy POSTGRES_USER: codex_proxy POSTGRES_PASSWORD: ${CPR_POSTGRES_PASSWORD:?CPR_POSTGRES_PASSWORD must be set in deploy/.env} diff --git a/docs/architecture.md b/docs/architecture.md index f428ab831..06436e030 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -445,6 +445,8 @@ Responses 协议没有按事件序号续传未完成 response 的请求字段。 PostgreSQL 是权威持久化存储。 +时间列统一使用 `timestamptz`,应用以 UTC 时间点读写。Compose 将 PostgreSQL 的会话显示时区和日志时区设为 `Asia/Shanghai`,因此直接查询时显示 `+08:00`;这不会改写已有数据,也不会改变数据库内部保存的绝对时间。API 输出、中国自然日统计和日志轮转仍由 `infra::time` 显式定义,不依赖宿主机时区。 + | 数据 | 表 | | --- | --- | | 管理员 | `admin_users` |