diff --git a/README.md b/README.md index 01371ef..501a092 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,18 @@ # DSH Runtime Release +## runtime-7.3:域名访问设置适配 + +本次待发布修订为 runtime-7.3。构建时对 DSH 0.2.0-rc.2 的设置 UI 应用精确补丁,使设置视图与表单使用 host 持久化;不修改 connection.isLoopback、可信主机、Origin 或认证逻辑。版本或目标语句不匹配时构建失败,升级上游版本必须重新审核补丁。 + +这是构建期适配,不是普通插件。补丁本身不识别面板登录状态,该镜像的设置 UI 会对所有访问地址尝试远程设置操作,因此只能部署在受认证、校验实例归属的面板入口之后,不得绕过认证直接公开 runtime 端口。权限由服务端保证。 + +发布前须在非 localhost 域名验证提供商目录、配置与凭据保存、刷新和重启持久化,以及未登录、跨用户和非法 Origin 的拒绝行为。单元测试不代替这些验收;现有实例需升级镜像才生效,不覆盖用户 profile 或创建空 patch。 + `dockerfile` 分支只维护发布清单和 runtime 镜像构建文件,不包含管理面板源码或部署配置。 ## 发布清单 -`release.json` 是发布入口:`schema_version` 为清单格式版本,`release_id` 标识一次发布,`version` 为精确 DSH 版本,`deployment_schema` 为运行布局版本,`platforms` 为支持的平台。当前待发布配置为 `runtime-7.2`、DSH `0.2.0-rc.2`、布局 7、`linux/amd64` 和 `linux/arm64`(aarch64,不含 32 位 ARM)。 +`release.json` 是发布入口:`schema_version` 为清单格式版本,`release_id` 标识一次发布,`version` 为精确 DSH 版本,`deployment_schema` 为运行布局版本,`platforms` 为支持的平台。当前待发布配置为 `runtime-7.3`、DSH `0.2.0-rc.2`、布局 7、`linux/amd64` 和 `linux/arm64`(aarch64,不含 32 位 ARM)。 ARM64 验收状态:已确认 Node/Caddy 基础镜像提供 ARM64 版本,但尚未完成 ARM64 runtime 构建及运行验收。清单声明会开放面板的对应平台构建入口,不等于兼容性证明;验收完成前不要推送发布。 diff --git a/image/Dockerfile b/image/Dockerfile index bc09b8f..3a87a6c 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -13,6 +13,8 @@ RUN node /usr/local/lib/dsh-mgr/install-free-search.mjs COPY install-workspace.mjs /usr/local/lib/dsh-mgr/install-workspace.mjs RUN node /usr/local/lib/dsh-mgr/install-workspace.mjs COPY init-profile.mjs /usr/local/lib/dsh-mgr/init-profile.mjs +COPY install-host-settings.mjs /usr/local/lib/dsh-mgr/install-host-settings.mjs +RUN node /usr/local/lib/dsh-mgr/install-host-settings.mjs COPY --from=proxy /usr/bin/caddy /usr/bin/caddy # Strip upstream file capabilities without granting any runtime capabilities. # A fresh file copy omits security.capability xattrs; port 8080 needs none. diff --git a/image/install-host-settings.mjs b/image/install-host-settings.mjs new file mode 100644 index 0000000..e9cffe8 --- /dev/null +++ b/image/install-host-settings.mjs @@ -0,0 +1,27 @@ +import { readFileSync, writeFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +export const target = 'const persistence = ctx.remote.$host.isLoopback ? "host" : "memory";'; +const replacement = 'const persistence = "host"; /* dsh-mgr: authenticated remote settings */'; + +// Deliberately scoped to the settings UI, not connection.isLoopback or auth. +// Fail closed on upstream changes instead of silently shipping a broken UI. +export function patchSettings(source, version) { + if (version !== '0.2.0-rc.2') throw new Error('unsupported DSH version for host settings patch'); + if (source.split(target).length !== 2 || source.includes(replacement)) { + throw new Error('expected exactly one unpatched settings persistence selector'); + } + return source.replace(target, replacement); +} + +export function install(root = '/usr/local/lib/node_modules/@deepseek-ai/dsh') { + const { version } = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')); + const file = join(root, 'node_modules/@deepseek-ai/dsh-client-ui-settings/lib/client.js'); + const source = readFileSync(file, 'utf8'); + writeFileSync(file, patchSettings(source, version)); +} + +if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) { + install(); +} diff --git a/image/install-host-settings.test.mjs b/image/install-host-settings.test.mjs new file mode 100644 index 0000000..781a4e7 --- /dev/null +++ b/image/install-host-settings.test.mjs @@ -0,0 +1,32 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { runInNewContext } from 'node:vm'; +import { readFileSync } from 'node:fs'; +import { patchSettings, target } from './install-host-settings.mjs'; + +test('settings use host persistence on domains without changing loopback facts', () => { + for (const isLoopback of [false, true]) { + const ctx = { remote: { $host: { isLoopback } } }; + const source = '(function(ctx) { ' + target + ' return persistence; })(ctx)'; + assert.equal(runInNewContext(patchSettings(source, '0.2.0-rc.2'), { ctx }), 'host'); + assert.equal(ctx.remote.$host.isLoopback, isLoopback); + } +}); + +test('reject unsupported versions, changed selectors, duplicates and repeat installation', () => { + assert.throws(() => patchSettings(target, '0.2.0-rc.3'), /unsupported DSH version/); + for (const source of ['', target.replace('isLoopback', 'isLocal'), target + target, + patchSettings(target, '0.2.0-rc.2')]) { + assert.throws(() => patchSettings(source, '0.2.0-rc.2'), /exactly one/); + } +}); + +test('patch leaves surrounding code unchanged and is wired into the image', () => { + const source = 'before; ' + target + ' after;'; + const result = patchSettings(source, '0.2.0-rc.2'); + assert.ok(result.startsWith('before; ')); + assert.ok(result.endsWith(' after;')); + const dockerfile = readFileSync(new URL('./Dockerfile', import.meta.url), 'utf8'); + assert.ok(dockerfile.includes('COPY install-host-settings.mjs /usr/local/lib/dsh-mgr/install-host-settings.mjs')); + assert.ok(dockerfile.indexOf('RUN node /usr/local/lib/dsh-mgr/install-host-settings.mjs') > dockerfile.indexOf('npm install --global --foreground-scripts')); +}); diff --git a/release.json b/release.json index abf6384..09d51b2 100644 --- a/release.json +++ b/release.json @@ -1,6 +1,6 @@ { "schema_version": 1, - "release_id": "runtime-7.2", + "release_id": "runtime-7.3", "version": "0.2.0-rc.2", "deployment_schema": 7, "platforms": ["linux/amd64", "linux/arm64"]