/** * Recovery script: deploy the original saas-voc-gateway.js to restore cloud functions. * The gateway is a single consolidated function that handles all actions via routing. */ import 'dotenv/config'; import { readFile } from 'node:fs/promises'; import { randomUUID } from 'node:crypto'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; const required = ['PARSE_SERVER_URL', 'PARSE_APP_ID', 'PARSE_MASTER_KEY']; for (const name of required) if (!process.env[name]?.trim()) throw new Error(`${name} is required`); const registryBase = (process.env.FUNCTION_REGISTRY_SERVER_URL || process.env.PARSE_SERVER_URL).replace(/\/+$/, ''); const registryAppId = process.env.FUNCTION_REGISTRY_APP_ID || process.env.PARSE_APP_ID; const registryMasterKey = process.env.FUNCTION_REGISTRY_MASTER_KEY || process.env.PARSE_MASTER_KEY; const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': registryAppId, 'X-Parse-Master-Key': registryMasterKey }; const __dirname = dirname(fileURLToPath(import.meta.url)); // Each ObjectId -> function name, path, description const FUNCTIONS = [ { objectId: 'BzgrlznLbV', name: 'vContext', path: '/v-context', desc: 'Context & Workspace actions' }, { objectId: 'Yp1RkzNmXK', name: 'vDomesticVoc', path: '/v-domestic-voc', desc: 'Domestic VOC, data-source, import, audit' }, { objectId: 'Hd4FmnOpQR', name: 'vSync', path: '/v-sync', desc: 'Sync enqueue, jobs, retry, cancel' }, { objectId: 'Tk7WvXyZAb', name: 'vCompetitor', path: '/v-competitor', desc: 'Competitor overview, refresh, history, alerts, tasks' }, { objectId: 'Qm2BnDeFgH', name: 'vListing', path: '/v-listing', desc: 'Listing product, overview, products' }, { objectId: 'Lr3CsEfGhI', name: 'vListingJob', path: '/v-listing-job', desc: 'Listing score-job, version' }, { objectId: 'Nv4DtGhIjK', name: 'vAnalysis', path: '/v-analysis', desc: 'Analysis, insight-decision' }, { objectId: 'Pw5EuHiJkL', name: 'vActions', path: '/v-actions', desc: 'Action, alert CRUD' }, { objectId: 'Qx6FvIjKlM', name: 'vKnowledge', path: '/v-knowledge', desc: 'Knowledge products' }, { objectId: 'Ry7GwJkLmN', name: 'vAI', path: '/v-ai', desc: 'AI status, prompts, chat' }, { objectId: 'Sz8HxKlMnO', name: 'vUpstream', path: '/v-upstream', desc: 'Upstream Amazon, Sorftime, Tikhub, Domestic' }, ]; async function deployFunction(fn) { const code = await readFile(resolve(__dirname, '..', 'cloud-functions', 'saas-voc-gateway.js'), 'utf8'); const url = `${registryBase}/classes/Function/${fn.objectId}`; const payload = { name: fn.name, desc: fn.desc, type: 'standalone', path: fn.path, paramList: [{ name: 'params', type: 'Object', required: true }], code, respType: 'json', respJson: { success: true, data: null, requestId: randomUUID() }, isDeleted: false, }; const response = await fetch(url, { method: 'PUT', headers, body: JSON.stringify(payload) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Deploy ${fn.name} failed: HTTP ${response.status} - ${errorText}`); } const result = await response.json(); return { ok: true, objectId: result.objectId }; } async function main() { console.log('Recovering all cloud functions with original gateway...\n'); for (const fn of FUNCTIONS) { try { const result = await deployFunction(fn); console.log(` ✓ ${fn.name} (${fn.objectId})`); } catch (err) { console.log(` ✗ ${fn.name}: ${err.message}`); } } console.log('\nDone. Test with: POST to /api/functions with {"id":"","params":{"action":"..."}}'); } main();