| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import {fileURLToPath} from 'node:url';
- const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
- const hash = value => crypto.createHash('sha256').update(value).digest('hex');
- const block = text => {
- const start = text.indexOf(' async function resolve(ownerId, key, account');
- const end = text.indexOf(' const originalSave =', start);
- if (start < 0 || end < 0) throw Error('SESSION_IDENTITY_BLOCK_MISSING');
- return text.slice(start, end).replaceAll('\r\n', '\n');
- };
- const endpoint = {id: 'yKpUay6RDG', name: 'ksfStartWorkshopSession',
- beforeSha256: 'c3791b03e1183bdb1373569be0c0ebf99fe4c3b22e4261618bacb379278900c0',
- firstFixSha256: 'd843f6380d9b871f89bbaff8d687c5ec91493d4fc859bec263b12f34628372f3',
- afterSha256: '1db6ea3a59edb6ea842b3927ed67c3874f1b4fae58b6dfc816eb8816e782a0a4'};
- export function sessionIdentityFunction(code) {
- const previous = fs.readFileSync(path.join(root, 'deploy/account-binding-short-links-20261112/previous-account-binding.mjs'));
- if (hash(previous) !== 'd02811b864e8cc75e5e061c9120ac769c09af8cb3b421e9c7757c723841dc2b4') throw Error('SESSION_IDENTITY_BASELINE_CHANGED');
- const firstFix = fs.readFileSync(path.join(root, 'deploy/account-binding-short-links-20261112/session-identity-first-fix.txt'), 'utf8');
- if (hash(firstFix) !== 'c5a99c34408b269828ff3c496b6fc22111df4d05ccefd2776dbdd237114957e9') throw Error('SESSION_IDENTITY_BASELINE_CHANGED');
- const candidates = [block(previous.toString('utf8')), firstFix];
- const matches = candidates.filter(candidate => code.split(candidate).length === 2);
- if (matches.length !== 1) throw Error('SESSION_IDENTITY_BLOCK_MISMATCH');
- const before = matches[0];
- const next = block(fs.readFileSync(path.join(root, 'server/src/account-binding.mjs'), 'utf8'));
- const patched = code.replace(before, next);
- new Function(patched);
- return patched;
- }
- export async function deployAccountSessionIdentity({request, apply = false, outDir = path.join(root, 'work/chat-single-reset-20261112')}) {
- const row = await request('GET', '/classes/Function/' + endpoint.id);
- if (row.name !== endpoint.name || row.path !== '/' + endpoint.name) throw Error('SESSION_IDENTITY_ENDPOINT_MISMATCH');
- if (hash(row.code) === endpoint.afterSha256) return {ok: true, applied: false, alreadyApplied: true, endpoint: endpoint.name, afterSha256: endpoint.afterSha256};
- if (![endpoint.beforeSha256,endpoint.firstFixSha256].includes(hash(row.code))) throw Error('UNKNOWN_SESSION_IDENTITY_FUNCTION_VERSION');
- const code = sessionIdentityFunction(row.code);
- if (hash(code) !== endpoint.afterSha256) throw Error('SESSION_IDENTITY_PAYLOAD_CHANGED');
- const receipt = {ok: true, applied: false, endpoint: endpoint.name, objectId: endpoint.id, beforeSha256: hash(row.code), afterSha256: hash(code), schemaChanges: 0, permissionsChanged: false};
- fs.mkdirSync(outDir, {recursive: true});
- fs.writeFileSync(path.join(outDir, 'session-identity-deploy-plan.json'), JSON.stringify(receipt, null, 2));
- if (!apply) return receipt;
- const backup = path.join(outDir, 'session-identity-function-before-' + crypto.randomUUID() + '.private.json');
- fs.writeFileSync(backup, JSON.stringify(row, null, 2), {flag: 'wx', mode: 0o600});
- const current = await request('GET', '/classes/Function/' + endpoint.id);
- if (current.code !== row.code || JSON.stringify(current.ACL) !== JSON.stringify(row.ACL)) throw Error('CONCURRENT_SESSION_IDENTITY_CHANGE');
- let written = false;
- try {
- await request('PUT', '/classes/Function/' + endpoint.id, {code}); written = true;
- const read = await request('GET', '/classes/Function/' + endpoint.id);
- if (read.code !== code || read.name !== row.name || read.path !== row.path || JSON.stringify(read.ACL) !== JSON.stringify(row.ACL)) throw Error('SESSION_IDENTITY_READBACK_MISMATCH');
- receipt.applied = true; receipt.readbackVerified = true; receipt.backup = backup;
- fs.writeFileSync(path.join(outDir, 'session-identity-deploy-receipt.json'), JSON.stringify(receipt, null, 2));
- return receipt;
- } catch (error) {
- receipt.ok = false; receipt.error = /^[A-Z_]+$/.test(error.message) ? error.message : 'SESSION_IDENTITY_UPDATE_FAILED';
- if (written) {
- const read = await request('GET', '/classes/Function/' + endpoint.id);
- if (read.code === code) {
- await request('PUT', '/classes/Function/' + endpoint.id, {code: row.code});
- receipt.rolledBack = (await request('GET', '/classes/Function/' + endpoint.id)).code === row.code;
- }
- }
- fs.writeFileSync(path.join(outDir, 'session-identity-deploy-failure.json'), JSON.stringify(receipt, null, 2));
- throw Object.assign(Error(receipt.error), {receipt});
- }
- }
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
- if (!['plan', 'apply'].includes(process.argv[2]) || process.argv.length !== 3) throw Error('USAGE_PLAN_OR_APPLY');
- const {request} = await import('../server/scripts/platform-client.mjs');
- try { console.log(JSON.stringify(await deployAccountSessionIdentity({request, apply: process.argv[2] === 'apply'}))); }
- catch (error) { console.error(JSON.stringify(error.receipt || {ok: false, error: /^[A-Z_]+$/.test(error.message) ? error.message : 'SESSION_IDENTITY_DEPLOY_FAILED'})); process.exitCode = 1; }
- }
|