| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import { fileURLToPath } from 'node:url';
- import { request, invoke, appId, functionsUrl } from '../server/scripts/platform-client.mjs';
- const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
- const OUT = path.join(ROOT, 'work/workshop-longterm-authorization-20261108-r3');
- const EXPECTED_ID = 'MqKRUv9JQc';
- const BEFORE_EXPIRY = '2026-11-15T01:48:23.854Z';
- const LONGTERM_EXPIRY = '9999-12-31T00:00:00.000Z';
- const SCOPES = ['source:write', 'ingest:write', 'data:read', 'report:ingest', 'report:publish', 'noise:write', 'noise:read'];
- const sha = bytes => crypto.createHash('sha256').update(bytes).digest('hex');
- const must = (value, code) => { if (!value) throw Object.assign(new Error(code), { code }); };
- const expiry = row => row.expiresAt?.iso || row.expiresAt;
- const safe = row => ({ objectId: row.objectId, enabled: row.enabled, expiresAt: expiry(row), scopes: row.scopes, projectIds: row.projectIds, updatedAt: row.updatedAt });
- async function main() {
- const apply = process.argv[2] === '--apply';
- must(process.argv.length <= 3 && (!process.argv[2] || apply), 'USAGE');
- const env = {};
- for (const line of fs.readFileSync('F:/文档/康师傅数字人受限连接.env', 'utf8').split(/\r?\n/)) {
- const m = line.match(/^(KSF_SERVICE_TOKEN|KSF_FUNCTIONS_URL|KSF_PARSE_APP_ID)=(.*)$/);
- if (m) env[m[1]] = m[2].trim();
- }
- must(env.KSF_SERVICE_TOKEN && env.KSF_FUNCTIONS_URL === functionsUrl && env.KSF_PARSE_APP_ID === appId, 'SERVICE_CONFIG_MISMATCH');
- const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId, 'X-Ksf-Service-Token': env.KSF_SERVICE_TOKEN };
- const where = { tokenHash: sha(env.KSF_SERVICE_TOKEN) };
- const getRow = async () => {
- const rows = (await request('GET', '/classes/KsfServiceCredential?' + new URLSearchParams({ where: JSON.stringify(where), limit: '2' }))).results;
- must(rows?.length === 1 && rows[0].objectId === EXPECTED_ID && rows[0].enabled === true, 'CREDENTIAL_IDENTITY_MISMATCH');
- must(JSON.stringify(rows[0].scopes) === JSON.stringify(SCOPES) && JSON.stringify(rows[0].projectIds) === '["*"]', 'CREDENTIAL_SCOPE_CHANGED');
- return rows[0];
- };
- const before = await getRow();
- fs.mkdirSync(OUT, { recursive: true });
- const receiptFile = path.join(OUT, 'server-longterm-receipt.json');
- const planFile = path.join(OUT, 'longterm-plan.json');
- if (expiry(before) === LONGTERM_EXPIRY) {
- must(fs.existsSync(receiptFile), 'LONGTERM_STATE_REQUIRES_PRIOR_RECEIPT');
- const probe = await invoke('ksfGetIngestContract', { requestId: 'longterm-auth-recheck-20261108-r3' }, headers);
- must(probe.http === 200 && probe.body.result?.ok === true, 'LONGTERM_AUTH_RECHECK_FAILED');
- console.log(JSON.stringify({ ok: true, alreadyApplied: true, expiresAt: expiry(before), authorizationRecordWrites: 0 }));
- return;
- }
- must(expiry(before) === BEFORE_EXPIRY, 'UNKNOWN_AUTHORIZATION_VERSION');
- const pre = await invoke('ksfGetIngestContract', { requestId: 'longterm-auth-before-20261108-r3' }, headers);
- must(pre.http === 200 && pre.body.result?.ok === true, 'EXISTING_AUTHORIZATION_NOT_WORKING');
- if (!apply) {
- const plan = { schema: 'ksf.workshop.longterm-authorization-plan.v1', plannedAt: new Date().toISOString(),
- before: safe(before), beforeFingerprint: sha(JSON.stringify(before)), expiresAt: LONGTERM_EXPIRY,
- authorizationMode: 'long-term-with-explicit-expiry', fieldsToChange: ['expiresAt'],
- userRequest: '不能永久吗', task: 'Replace weekly renewal with long-lived, revocable authorization',
- serviceIdentityPreserved: true, scopesPreserved: true, tokenPreserved: true, revocable: true,
- authorizationRecordWrites: 0, businessRecordWrites: 0, paidCalls: 0 };
- fs.writeFileSync(planFile, JSON.stringify(plan, null, 2) + '\n');
- console.log(JSON.stringify(plan, null, 2)); return;
- }
- must(fs.existsSync(planFile), 'LONGTERM_PLAN_REQUIRED');
- const plan = JSON.parse(fs.readFileSync(planFile));
- must(plan.beforeFingerprint === sha(JSON.stringify(before)) && Date.now() - Date.parse(plan.plannedAt) < 3600000, 'STALE_OR_CONCURRENT_PLAN');
- const receipt = { schema: 'ksf.workshop.longterm-authorization-receipt.v1', startedAt: new Date().toISOString(), before: safe(before),
- requestedExpiresAt: LONGTERM_EXPIRY, authorizationMode: 'long-term-with-explicit-expiry', revocable: true,
- authorizationRecordWrites: 0, businessRecordWrites: 0, paidCalls: 0,
- serverLongtermAuthorizationVerified: false, agentLongtermEnvelopeVerified: false,
- originalAgentAuthorizationStillWorksUntil: BEFORE_EXPIRY };
- const save = () => fs.writeFileSync(receiptFile, JSON.stringify(receipt, null, 2) + '\n');
- save();
- try {
- receipt.mutationAttempted = true; save();
- await request('PUT', '/classes/KsfServiceCredential/' + EXPECTED_ID, { expiresAt: { __type: 'Date', iso: LONGTERM_EXPIRY } });
- receipt.authorizationRecordWrites = 1;
- const after = await getRow();
- must(expiry(after) === LONGTERM_EXPIRY && after.tokenHash === before.tokenHash, 'LONGTERM_READBACK_MISMATCH');
- receipt.after = safe(after); receipt.serviceIdentityPreserved = after.objectId === before.objectId; receipt.tokenPreserved = true; receipt.scopesPreserved = true;
- const post = await invoke('ksfGetIngestContract', { requestId: 'longterm-auth-after-20261108-r3' }, headers);
- must(post.http === 200 && post.body.result?.ok === true, 'LONGTERM_AUTH_PROBE_FAILED');
- receipt.contract = { http: post.http, ok: true, allowedClassCount: post.body.result.data?.allowedClasses?.length };
- receipt.serverLongtermAuthorizationVerified = true; receipt.finishedAt = new Date().toISOString(); save();
- console.log(JSON.stringify(receipt, null, 2));
- } catch (error) {
- receipt.error = { code: /^[A-Z][A-Z0-9_]+$/.test(error.code || '') ? error.code : 'LONGTERM_SWITCH_CHECK_FAILED' }; save(); throw error;
- } finally { delete env.KSF_SERVICE_TOKEN; }
- }
- main().catch(error => { console.error(JSON.stringify({ ok: false, code: /^[A-Z][A-Z0-9_]+$/.test(error.code || '') ? error.code : 'LONGTERM_AUTHORIZATION_STOPPED',
- message: '长期授权调整停止;保留原状态,不输出凭据或原始异常。' })); process.exitCode = 1; });
|