UNPKG

spaps

Version:

Sweet Potato Authentication & Payment Service CLI - Docker Compose orchestrator for local Python/FastAPI SPAPS server with built-in admin middleware

250 lines (221 loc) 7.21 kB
const { DEFAULT_PORT } = require('./config'); const { getServerRuntime, readSelfServicePassword } = require('./local-runtime'); const { resolveAuthApiKey } = require('./auth/api-key'); function normalizeRuntimeArgs(input = DEFAULT_PORT) { if (typeof input === 'number') { return { port: input, serverUrl: null, cwd: process.cwd(), }; } if (input && typeof input === 'object') { return { port: Number(input.port) || DEFAULT_PORT, serverUrl: input.serverUrl || resolveEnvServerUrl(), cwd: input.cwd || process.cwd(), }; } return { port: DEFAULT_PORT, serverUrl: null, cwd: process.cwd(), }; } function resolveEnvServerUrl() { const raw = String(process.env.SPAPS_API_URL || '').trim(); return raw ? raw : null; } function buildProvisioningCommand(template, port) { return `SELF_SERVICE_PASSWORD=your-password npx spaps create demo-${template === 'node' ? 'api' : 'app'} --template ${template} --port ${port}`; } function buildLocalModeInstructions(runtime) { return { success: true, server: { running: true, url: runtime.url, docs: runtime.docs, local_mode_active: true, environment: runtime.local_mode.environment, spaps_local_mode_env: runtime.local_mode.spaps_local_mode_env, }, auth: { local_mode: true, api_key_required: false, mode_source: '/health/local-mode', test_users: runtime.local_mode.test_users, test_application: runtime.local_mode.test_application, hints: runtime.local_mode.hints, }, summary: [ 'Install the SDK with `npm install spaps-sdk`.', `Point the client at ${runtime.url}.`, 'Use the local-mode hints from `/health/local-mode` if you need a specific test persona.', ], instructions: { install_sdk: { description: 'Install the SDK', command: 'npm install spaps-sdk', }, create_client: { description: 'Create a client for local mode', filename: 'test-spaps.js', content: `const { SPAPSClient } = require('spaps-sdk'); const spaps = new SPAPSClient({ apiUrl: '${runtime.url}' }); async function main() { const status = await fetch('${runtime.url}/health/local-mode').then((res) => res.json()); console.log(status.data); } main().catch(console.error); `, }, verify_server: { description: 'Confirm local mode status', command: `curl -s ${runtime.url}/health/local-mode | jq '.'`, }, }, }; } function buildProvisionedModeInstructions(runtime, port) { const hasPassword = Boolean(readSelfServicePassword()); return { success: true, server: { running: true, url: runtime.url, docs: runtime.docs, local_mode_active: false, environment: runtime.local_mode.environment, spaps_local_mode_env: runtime.local_mode.spaps_local_mode_env, }, auth: { local_mode: false, api_key_required: true, mode_source: '/health/local-mode', header: 'X-API-Key', env: 'SPAPS_API_KEY', self_service_password_env: 'SELF_SERVICE_PASSWORD', }, summary: [ 'Install the SDK with `npm install spaps-sdk`.', 'Provision a local application before testing authenticated flows.', `Send \`X-API-Key\` when /health/local-mode reports \`local_mode_active: false\`.`, ], instructions: { install_sdk: { description: 'Install the SDK', command: 'npm install spaps-sdk', }, provision_app: { description: hasPassword ? 'Provision a local browser app' : 'Set SELF_SERVICE_PASSWORD, then provision a local browser app', command: buildProvisioningCommand('react', port), }, create_client: { description: 'Create a client that uses a provisioned key', filename: 'test-spaps.js', content: `const { createServerClient } = require('spaps-sdk'); const apiUrl = process.env.SPAPS_API_URL || '${runtime.url}'; const apiKey = process.env.SPAPS_API_KEY; if (!apiKey) { throw new Error('Set SPAPS_API_KEY before testing against a non-local-mode server.'); } const spaps = createServerClient(apiKey, { apiUrl }); async function main() { const result = await spaps.auth.signInWithPassword({ email: 'user@example.com', password: 'correct-horse-battery-staple', }); console.log(result.user.id); } main().catch(console.error); `, }, verify_server: { description: 'Confirm whether the server is in local mode', command: `curl -s ${runtime.url}/health/local-mode | jq '.'`, }, }, }; } async function getQuickStartInstructions(input = DEFAULT_PORT) { const { port, serverUrl } = normalizeRuntimeArgs(input); const runtime = await getServerRuntime({ port, serverUrl }); if (!runtime.running) { return { success: false, server: runtime, summary: [ `Start the local server with \`npx spaps local --port ${port}\`.`, 'Provision a starter application after the server is healthy.', ], instructions: { start_server: { description: 'Start the local server', command: `npx spaps local --port ${port}`, }, }, }; } if (runtime.local_mode.active) { return buildLocalModeInstructions(runtime); } return buildProvisionedModeInstructions(runtime, port); } async function getServerStatus(input = DEFAULT_PORT) { const { port, serverUrl } = normalizeRuntimeArgs(input); return getServerRuntime({ port, serverUrl }); } async function runQuickTest(input = DEFAULT_PORT) { const { port, serverUrl, cwd } = normalizeRuntimeArgs(input); const runtime = await getServerRuntime({ port, serverUrl }); const results = [ { test: 'server_status', success: runtime.running, details: runtime, }, ]; if (!runtime.running) { return { success: false, summary: '0/1 tests passed', results, next_steps: [runtime.start_command || `Check the server at ${runtime.url} and rerun spaps verify.`], }; } results.push({ test: 'local_mode_contract', success: runtime.local_mode.known, details: runtime.local_mode, fix: runtime.local_mode.known ? null : `curl -s ${runtime.url}/health/local-mode`, }); if (!runtime.local_mode.active) { const apiKey = resolveAuthApiKey({ cwd }); const hasKey = Boolean(apiKey.apiKey); results.push({ test: 'api_key_wiring', success: hasKey, message: hasKey ? `API key is configured via ${apiKey.source}` : 'SPAPS_API_KEY is required when local mode is disabled', fix: hasKey ? null : buildProvisioningCommand('node', port), }); } const allSuccess = results.every((result) => result.success); return { success: allSuccess, summary: `${results.filter((result) => result.success).length}/${results.length} tests passed`, results, next_steps: allSuccess ? [`See docs at ${runtime.docs}`] : ['Fix the failing checks above and re-run: spaps verify --json'], }; } module.exports = { getQuickStartInstructions, getServerStatus, runQuickTest, };