fix(deploy): verify desktop migration and bound private image sync
This commit is contained in:
parent
971e17fcd8
commit
70369dbdb2
@ -1254,7 +1254,7 @@ verify_detail_generator_schema() {
|
||||
local name="$1"
|
||||
docker exec -i "$name" node <<'NODE'
|
||||
const fs = require('fs'), crypto = require('crypto'), { PrismaClient } = require('@prisma/client');
|
||||
const migrations = ['20261012_detail_generator', '20261012_ai_model_registry', '20261013_ai_model_capabilities', '20261013_ai_model_verifications'];
|
||||
const migrations = ['20261012_detail_generator', '20261012_ai_model_registry', '20261013_ai_model_capabilities', '20261013_ai_model_verifications', '20261020090000_desktop_client_workspace'];
|
||||
const required = { detail_generator_projects: ['id','owner_id','document','revision','deleted_at'], detail_generator_assets: ['id','project_id','owner_id','storage_key','byte_size'], detail_generator_jobs: ['id','project_id','owner_id','status','idempotency_key','fingerprint','project_revision','payload','checkpoint','lease_token','lease_expires_at'], detail_generator_brand_profiles: ['id','name','profile','revision'] };
|
||||
const modelFields = {
|
||||
ai_model_providers: { id:'text', name:'text', base_url:'text', enabled:'boolean', credential_source:'text', secret_ciphertext:'text?', secret_nonce:'text?', secret_auth_tag:'text?', legacy_binding:'boolean', revision:'integer', synced_at:'timestamp with time zone?', created_at:'timestamp with time zone', updated_at:'timestamp with time zone' },
|
||||
@ -1646,6 +1646,7 @@ const expectedMigrationNames = new Set([
|
||||
'20261012_ai_model_registry',
|
||||
'20261013_ai_model_capabilities',
|
||||
'20261013_ai_model_verifications',
|
||||
'20261020090000_desktop_client_workspace',
|
||||
]);
|
||||
if (migrations.length !== expectedMigrationNames.size
|
||||
|| migrations.some((item) => !expectedMigrationNames.has(item && item.name)
|
||||
@ -2229,6 +2230,23 @@ http {
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
location ~ "^/api/desktop/projects/desktop/[A-Za-z0-9_-]{1,80}/assets/[^/]+\$" {
|
||||
client_max_body_size 21m;
|
||||
proxy_request_buffering off;
|
||||
proxy_pass http://app_backend;
|
||||
proxy_hide_header X-Material-Proxy-Target;
|
||||
add_header X-Material-Proxy-Target ${target_container} always;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host \$http_host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$forwarded_proto;
|
||||
proxy_read_timeout 120s;
|
||||
proxy_send_timeout 120s;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
location ~ "^/api/detail-generator/projects/[0-9a-fA-F-]{36}/assets\$" {
|
||||
client_max_body_size 17m;
|
||||
proxy_request_buffering off;
|
||||
|
||||
@ -83,6 +83,7 @@ test('migration deployment gate recognizes the latest approved schema migration'
|
||||
assert.match(gate, /20261012_ai_model_registry/)
|
||||
assert.match(gate, /20261013_ai_model_capabilities/)
|
||||
assert.match(gate, /20261013_ai_model_verifications/)
|
||||
assert.match(gate, /20261020090000_desktop_client_workspace/)
|
||||
})
|
||||
|
||||
test('direct replacement runs database and dependency gates before switching traffic', () => {
|
||||
@ -291,7 +292,7 @@ test('AI model cutover gate checks every migration and rejects missing, mistyped
|
||||
}
|
||||
const accepted = await run()
|
||||
assert.equal(accepted.exitCode, 0)
|
||||
assert.deepEqual(accepted.checked, ['20261012_detail_generator', '20261012_ai_model_registry', '20261013_ai_model_capabilities', '20261013_ai_model_verifications'])
|
||||
assert.deepEqual(accepted.checked, ['20261012_detail_generator', '20261012_ai_model_registry', '20261013_ai_model_capabilities', '20261013_ai_model_verifications', '20261020090000_desktop_client_workspace'])
|
||||
for (const migration of ['20261012_ai_model_registry', '20261013_ai_model_capabilities', '20261013_ai_model_verifications']) assert.equal((await run(columns, migration)).exitCode, 1)
|
||||
assert.equal((await run(columns.filter(row => row.column_name !== 'capability_metadata'))).exitCode, 1)
|
||||
assert.equal((await run(columns.map(row => row.column_name === 'capability_metadata' ? { ...row, data_type: 'text' } : row))).exitCode, 1)
|
||||
@ -316,6 +317,17 @@ test('detail uploads keep multipart headroom without expanding all proxy routes'
|
||||
assert.match(deploySource(), /location ~ "\^\/api\/detail-generator\/projects\/\[0-9a-fA-F-\]\{36\}\/assets\\\$" \{\n\s+client_max_body_size 17m;\n\s+proxy_request_buffering off;/)
|
||||
})
|
||||
|
||||
test('desktop private image sync supports the route 20MiB limit without widening other endpoints', () => {
|
||||
const source=deploySource()
|
||||
const marker='location ~ "^/api/desktop/projects/desktop/[A-Za-z0-9_-]{1,80}/assets/[^/]+\\$" {'
|
||||
const start=source.indexOf(marker)
|
||||
assert.ok(start>=0)
|
||||
const location=source.slice(start,source.indexOf('\n }',start))
|
||||
assert.match(location,/client_max_body_size 21m;/)
|
||||
assert.match(location,/proxy_request_buffering off;/)
|
||||
assert.match(location,/proxy_set_header Host \\\$http_host;/)
|
||||
})
|
||||
|
||||
test('brand knowledge upload stays bounded in the route and stable proxy', () => {
|
||||
const deploySource = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8')
|
||||
.replace(/\r\n/g, '\n')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user