build-release.mjs 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import crypto from 'node:crypto';
  4. import { fileURLToPath } from 'node:url';
  5. export function buildIdentityFunctions(base, { uploadBases = [] } = {}) {
  6. const directory = path.dirname(fileURLToPath(import.meta.url));
  7. const source = ['wecom-directory.mjs', 'employee-handlers.mjs'].map((name) => fs.readFileSync(path.join(directory, name), 'utf8').replace(/^import .*;\r?\n/gm, '').replace(/^export /gm, '')).join('\n');
  8. const hook = 'attachWorkshopHandlers(app,{store,data:DATA});';
  9. if (base.name !== 'ksfGetPublicBoard' || typeof base.code !== 'string' || base.code.split(hook).length !== 2 || base.code.includes('attachWecomEmployeeHandlers')) throw Error('UNEXPECTED_PUBLIC_FUNCTION');
  10. const overlay = (row) => {
  11. if (typeof row.code !== 'string' || row.code.split(hook).length !== 2 || row.code.includes('attachWecomEmployeeHandlers')) throw Error('UNEXPECTED_UPLOAD_FUNCTION');
  12. return source + '\n' + row.code.replace(hook, hook + '\n attachWecomEmployeeHandlers(app,{store,settings});');
  13. };
  14. const extended = overlay(base);
  15. const call = 'runDatabaseFunction("ksfGetPublicBoard",request,response,';
  16. if (extended.split(call).length !== 2) throw Error('UNEXPECTED_FUNCTION_ENTRY');
  17. const result = [
  18. { name: 'ksfGetPublicBoard', objectId: base.objectId, path: base.path, priorSha256: crypto.createHash('sha256').update(base.code).digest('hex'), code: extended },
  19. { name: 'ksfSyncWecomEmployee', path: '/ksfSyncWecomEmployee', priorSha256: null, code: extended.replace(call, 'runDatabaseFunction("ksfSyncWecomEmployee",request,response,') },
  20. ];
  21. if (uploadBases.length && (uploadBases.length !== 2 || new Set(uploadBases.map((r) => r.name)).size !== 2 || uploadBases.some((r) => !['ksfStartWorkshopSession', 'ksfSaveWorkshopTask'].includes(r.name)))) throw Error('UNEXPECTED_UPLOAD_BASES');
  22. for (const row of uploadBases) {
  23. if (row.code.split('runDatabaseFunction(' + JSON.stringify(row.name) + ',request,response,').length !== 2) throw Error('UNEXPECTED_FUNCTION_ENTRY');
  24. result.push({ name: row.name, objectId: row.objectId, path: row.path, priorSha256: crypto.createHash('sha256').update(row.code).digest('hex'), code: overlay(row) });
  25. }
  26. for (const entry of result) { new Function(entry.code); entry.sha256 = crypto.createHash('sha256').update(entry.code).digest('hex'); }
  27. return result;
  28. }
  29. if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
  30. const args = process.argv.slice(2);
  31. if (![4, 6].includes(args.length) || args[0] !== '--base' || args[2] !== '--out' || args.length === 6 && args[4] !== '--upload-bases') throw Error('Use --base PRIVATE_FUNCTION_SNAPSHOT --out NEW_ABSOLUTE_DIRECTORY [--upload-bases PRIVATE_UPLOAD_SNAPSHOTS]');
  32. const out = args[3];
  33. if (!path.isAbsolute(out) || fs.existsSync(out)) throw Error('NEW_ABSOLUTE_DIRECTORY_REQUIRED');
  34. const entries = buildIdentityFunctions(JSON.parse(fs.readFileSync(args[1], 'utf8')), { uploadBases: args[5] ? JSON.parse(fs.readFileSync(args[5], 'utf8')) : [] });
  35. fs.mkdirSync(out, { recursive: true });
  36. for (const entry of entries) fs.writeFileSync(path.join(out, entry.name + '.js'), entry.code);
  37. fs.writeFileSync(path.join(out, 'manifest.json'), JSON.stringify({ version: 'ksf.wecom-identity.20261103.v1', functions: entries.map(({ code, ...rest }) => rest) }, null, 2));
  38. console.log(JSON.stringify({ ok: true, functions: entries.length, out, productionWrites: 0 }));
  39. }