diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 16e3dfb5..b21c2ed0 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -98,6 +98,8 @@ jobs: docker exec edgeever-ci test -f /data/edgeever.sqlite docker exec edgeever-ci test -d /data/resources docker exec edgeever-ci test -s /app/scripts/self-hosted-server.js + docker exec edgeever-ci test -s /app/scripts/self-hosted-server.mjs + docker exec edgeever-ci cmp -s /app/scripts/self-hosted-server.js /app/scripts/self-hosted-server.mjs docker exec edgeever-ci test ! -e /app/node_modules docker exec edgeever-ci test ! -e /app/apps/api docker exec edgeever-ci test ! -e /app/packages @@ -224,6 +226,8 @@ jobs: echo "${health}" | jq -e '.ok == true and .runtime == "self-hosted-bun" and .authMode == "required"' curl --fail --silent --show-error http://127.0.0.1:8787/ | grep -F '' docker exec "${container_name}" test -s /app/scripts/self-hosted-server.js + docker exec "${container_name}" test -s /app/scripts/self-hosted-server.mjs + docker exec "${container_name}" cmp -s /app/scripts/self-hosted-server.js /app/scripts/self-hosted-server.mjs docker exec "${container_name}" test ! -e /app/node_modules docker exec "${container_name}" test ! -e /app/apps/api docker exec "${container_name}" test ! -e /app/packages diff --git a/Dockerfile b/Dockerfile index e77ed1f8..243b7a95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,7 +51,10 @@ COPY --from=build /app/apps/web/dist ./apps/web/dist COPY migrations ./migrations COPY LICENSE ./LICENSE -RUN mkdir -p /data && chown -R bun:bun /data +# NAS/GUI upgrades often keep the v1.62 command bun scripts/self-hosted-server.mjs. +RUN mkdir -p /data \ + && ln -s self-hosted-server.js /app/scripts/self-hosted-server.mjs \ + && chown -R bun:bun /data USER bun VOLUME ["/data"] EXPOSE 8787 diff --git a/docs/deploy-docker.md b/docs/deploy-docker.md index 5a82398c..a3a09674 100644 --- a/docs/deploy-docker.md +++ b/docs/deploy-docker.md @@ -174,6 +174,12 @@ before it starts accepting traffic. Take a backup first. Application rollback does not reverse a database migration; restore the pre-upgrade volume backup when a data rollback is required. +The image default command is `bun scripts/self-hosted-server.js`. It also keeps +`scripts/self-hosted-server.mjs` as an alias, because NAS and GUI panels often +reuse the command saved from v1.62 and earlier. If logs show +`Module not found "scripts/self-hosted-server.mjs"`, pull a release that includes +that alias, or clear the custom command so the image default is used. + To move between Cloudflare and Docker, use EdgeEver's full backup/export and restore flow. Do not copy a live D1 database file or rewrite migration history. diff --git a/docs/deploy-docker.zh-CN.md b/docs/deploy-docker.zh-CN.md index edcc9db6..e024082b 100644 --- a/docs/deploy-docker.zh-CN.md +++ b/docs/deploy-docker.zh-CN.md @@ -159,6 +159,12 @@ docker compose ps 回退应用镜像不会逆向撤销数据库 migration;需要回退数据时,应恢复升级前的 卷备份。 +镜像默认命令是 `bun scripts/self-hosted-server.js`,同时保留 +`scripts/self-hosted-server.mjs` 作为别名,因为 NAS/GUI 面板常会沿用 v1.62 +及更早版本保存的启动命令。如果日志出现 +`Module not found "scripts/self-hosted-server.mjs"`,请拉取包含该别名的版本, +或清空自定义命令以使用镜像默认入口。 + 在 Cloudflare 与 Docker 之间迁移时,请使用 EdgeEver 的完整备份/导出与恢复 流程。不要复制在线 D1 数据库文件,也不要改写 migration 历史。 diff --git a/docs/self-hosting-architecture.md b/docs/self-hosting-architecture.md index 0443e763..8fd350a0 100644 --- a/docs/self-hosting-architecture.md +++ b/docs/self-hosting-architecture.md @@ -66,6 +66,9 @@ ambiguous. AES-GCM ciphertext. - Make `/data` the only required persistent application path so NAS users can back up one volume. +- Keep the production entry at `scripts/self-hosted-server.js`, and keep + `scripts/self-hosted-server.mjs` as an alias for NAS/GUI command overrides + saved from v1.62 and earlier. - Support `EDGE_EVER_AUTH_USERNAME`, `EDGE_EVER_AUTH_PASSWORD`, and session settings without Cloudflare-specific naming assumptions in the container entrypoint. diff --git a/docs/self-hosting-architecture.zh-CN.md b/docs/self-hosting-architecture.zh-CN.md index 3164f7bf..ab0c362e 100644 --- a/docs/self-hosting-architecture.zh-CN.md +++ b/docs/self-hosting-architecture.zh-CN.md @@ -49,6 +49,9 @@ S3 兼容对象存储两种后端。 - 继续追加 `migrations/*.sql`,禁止为 Docker 分叉数据库结构。 - 根密钥必须通过环境变量或 Docker secrets 注入,不能写入镜像或数据库。自托管运行入口可以把副本写在 `/data`(SQLite 之外),以便 NAS/GUI 升级丢掉容器环境变量后仍能加密凭据。Cloudflare 继续只使用 Worker Secret。对象存储 Secret 与个人 AI 模型 API Key 默认从已有实例认证 Secret 派生各自用途的专用密钥;高级 AI 密钥轮换场景可选用 `EDGE_EVER_CREDENTIALS_ENCRYPTION_KEY` 覆盖。所有凭据在数据库中只能保存为 AES-GCM 密文。 - 将 `/data` 作为唯一必需的应用持久化路径,方便 NAS 用户备份一个卷。 +- 生产入口保持 `scripts/self-hosted-server.js`,并保留 + `scripts/self-hosted-server.mjs` 别名,以便 NAS/GUI 继续使用 v1.62 及更早版本 + 保存的启动命令。 - 容器入口需要支持 `EDGE_EVER_AUTH_USERNAME`、`EDGE_EVER_AUTH_PASSWORD` 和 会话配置,同时不能把 Cloudflare 专有配置当作前置条件。 - 登录暴力破解防护必须使用应用层的 SQLite/D1 兼容存储实现;Cloudflare diff --git a/scripts/docker-release.test.mjs b/scripts/docker-release.test.mjs index a8a61d43..4142bef2 100644 --- a/scripts/docker-release.test.mjs +++ b/scripts/docker-release.test.mjs @@ -82,6 +82,9 @@ describe("Docker release contract", () => { expect(runtimeStage).toContain( "COPY --from=build /app/dist/self-hosted/self-hosted-server.js ./scripts/self-hosted-server.js", ); + expect(runtimeStage).toContain( + "ln -s self-hosted-server.js /app/scripts/self-hosted-server.mjs", + ); expect(runtimeStage).toContain('org.opencontainers.image.title="EdgeEver"'); expect(runtimeStage).toContain('org.opencontainers.image.licenses="AGPL-3.0-only"'); expect(runtimeStage).not.toContain("node_modules"); @@ -96,6 +99,18 @@ describe("Docker release contract", () => { expect(dockerfile).toContain("ARG EDGE_EVER_BUILD_ID=unknown"); expect(dockerfile).toContain("EDGE_EVER_BUILD_ID=${EDGE_EVER_BUILD_ID}"); expect(runtimeStage).toContain('CMD ["bun", "scripts/self-hosted-server.js"]'); + expect(readProjectFile("docs/deploy-docker.md")).toContain( + 'Module not found "scripts/self-hosted-server.mjs"', + ); + expect(readProjectFile("docs/deploy-docker.zh-CN.md")).toContain( + 'Module not found "scripts/self-hosted-server.mjs"', + ); + expect(readProjectFile("docs/self-hosting-architecture.md")).toContain( + "scripts/self-hosted-server.mjs", + ); + expect(readProjectFile("docs/self-hosting-architecture.zh-CN.md")).toContain( + "scripts/self-hosted-server.mjs", + ); }); test("keeps authentication explicit in Compose", () => { @@ -165,6 +180,11 @@ describe("Docker release contract", () => { expect(workflow).toContain("docker buildx imagetools inspect"); expect(workflow).toContain('docker pull "${GHCR_IMAGE_NAME}:${{ steps.image.outputs.audit_tag }}"'); expect(workflow).toContain("container_name=edgeever-published-audit"); + expect(workflow).toContain("test -s /app/scripts/self-hosted-server.js"); + expect(workflow).toContain("test -s /app/scripts/self-hosted-server.mjs"); + expect(workflow).toContain( + "cmp -s /app/scripts/self-hosted-server.js /app/scripts/self-hosted-server.mjs", + ); expect(workflow).toContain("test ! -e /app/node_modules"); expect(workflow).toContain("test ! -e /app/apps/api"); expect(workflow).toContain("test ! -e /app/packages");