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)); const cloudFunctionsDir = resolve(__dirname, '..', 'cloud-functions'); // Cloud function definitions const CLOUD_FUNCTIONS = [ { name: 'vContext', path: '/v-context', file: 'vContext.js', desc: 'SaaS VOC context and workspace management' }, { name: 'vDomesticVoc', path: '/v-domestic-voc', file: 'vDomesticVoc.js', desc: 'SaaS VOC domestic VOC data' }, { name: 'vSync', path: '/v-sync', file: 'vSync.js', desc: 'SaaS VOC sync job management' }, { name: 'vCompetitor', path: '/v-competitor', file: 'vCompetitor.js', desc: 'SaaS VOC competitor monitor' }, { name: 'vListing', path: '/v-listing', file: 'vListing.js', desc: 'SaaS VOC listing scores' }, { name: 'vListingJob', path: '/v-listing-job', file: 'vListingJob.js', desc: 'SaaS VOC listing score jobs' }, { name: 'vAnalysis', path: '/v-analysis', file: 'vAnalysis.js', desc: 'SaaS VOC analysis and insights' }, { name: 'vActions', path: '/v-actions', file: 'vActions.js', desc: 'SaaS VOC actions and alerts' }, { name: 'vKnowledge', path: '/v-knowledge', file: 'vKnowledge.js', desc: 'SaaS VOC product knowledge' }, { name: 'vAI', path: '/v-ai', file: 'vAI.js', desc: 'SaaS VOC AI gateway' }, { name: 'vUpstream', path: '/v-upstream', file: 'vUpstream.js', desc: 'SaaS VOC upstream API proxy' }, ]; async function deployFunction(func) { const filePath = resolve(cloudFunctionsDir, func.file); const code = await readFile(filePath, 'utf8'); const query = new URL(`${registryBase}/classes/Function`); query.searchParams.set('where', JSON.stringify({ path: func.path })); const existingResponse = await fetch(query, { headers }); if (!existingResponse.ok) throw new Error(`Function lookup failed for ${func.name}: HTTP ${existingResponse.status}`); const existing = await existingResponse.json(); const payload = { name: func.name, desc: func.desc, type: 'standalone', path: func.path, paramList: [{ name: 'params', type: 'Object', required: true }], code, respType: 'json', respJson: { success: true, data: null, requestId: randomUUID() }, isDeleted: false, }; const target = existing.results?.[0]; const url = target ? `${registryBase}/classes/Function/${target.objectId}` : `${registryBase}/classes/Function`; const method = target ? 'PUT' : 'POST'; const response = await fetch(url, { method, headers, body: JSON.stringify(payload), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Function deployment failed for ${func.name}: HTTP ${response.status} - ${errorText}`); } const result = await response.json(); return { ok: true, name: func.name, path: func.path, objectId: target?.objectId || result.objectId, action: target ? 'updated' : 'created', }; } async function main() { console.log(`Deploying ${CLOUD_FUNCTIONS.length} cloud functions...`); const results = []; for (const func of CLOUD_FUNCTIONS) { try { const result = await deployFunction(func); console.log(`✓ ${result.name} (${result.action}): ${result.objectId}`); results.push(result); } catch (error) { console.error(`✗ ${func.name}: ${error.message}`); results.push({ ok: false, name: func.name, error: error.message }); } } const failed = results.filter((r) => !r.ok); if (failed.length > 0) { console.error(`\n${failed.length} function(s) failed to deploy`); process.exit(1); } console.log(`\nAll ${results.length} cloud functions deployed successfully!`); console.log(`\nCloud function paths:`); results.forEach((r) => console.log(` ${r.name}: ${r.path}`)); } main().catch((error) => { console.error('Deployment failed:', error); process.exitCode = 1; });