UNPKG

spaps

Version:

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

201 lines (179 loc) 5.98 kB
const chalk = require('chalk'); const { DEFAULT_PORT } = require('./config'); const { getServerRuntime } = require('./local-runtime'); const { resolveServerUrl } = require('./auth/client'); const { getCredentials, CREDENTIALS_PATH } = require('./auth/credentials'); const { findUpContract } = require('./auth/client-id'); const { resolveAuthApiKey } = require('./auth/api-key'); function resolveAppContext({ cwd, runtime }) { if (typeof process.env.SPAPS_CLI_CLIENT_ID === 'string' && process.env.SPAPS_CLI_CLIENT_ID.trim()) { return { clientId: process.env.SPAPS_CLI_CLIENT_ID.trim(), source: 'SPAPS_CLI_CLIENT_ID', path: null, }; } const contractHit = findUpContract(cwd); if (contractHit) { return contractHit; } const runtimeClientId = runtime?.local_mode?.test_application?.slug || null; if (runtimeClientId) { return { clientId: runtimeClientId, source: '/health/local-mode', path: null, }; } return { clientId: null, source: null, path: null, }; } function isLocalhostUrl(url) { try { const parsed = new URL(url); return parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1'; } catch { return false; } } function formatSource(source, filePath) { if (!source) { return null; } return filePath ? `${source} (${filePath})` : source; } function formatMode(runtime) { if (!runtime.running) { return 'server unreachable'; } if (!runtime.local_mode?.known) { return 'mode unknown'; } return runtime.local_mode.active ? 'local mode active' : 'application key required'; } function buildRecommendedNext({ operator, runtime, port }) { if (!operator.connected) { return { command: 'spaps connect', reason: `No saved operator session for ${operator.server_url}.`, }; } if (!runtime.running) { return { command: isLocalhostUrl(runtime.url) ? `spaps local${port !== DEFAULT_PORT ? ` --port ${port}` : ''}` : 'spaps verify', reason: isLocalhostUrl(runtime.url) ? `No SPAPS runtime is reachable at ${runtime.url}.` : `No SPAPS runtime is reachable at ${runtime.url}; verify the configured server URL.`, }; } return { command: 'spaps verify', reason: runtime.local_mode?.active ? 'Confirm the local auth path and runtime hints end to end.' : 'Confirm the provisioned auth path and API key wiring end to end.', }; } async function buildHomeView({ port = DEFAULT_PORT, serverUrl = null, cwd = process.cwd() } = {}) { const resolvedPort = Number(port) || DEFAULT_PORT; const resolvedServerUrl = resolveServerUrl({ port: resolvedPort, serverUrl }); const runtime = await getServerRuntime({ port: resolvedPort, serverUrl: resolvedServerUrl }); const storedCreds = getCredentials(resolvedServerUrl); const envAccessToken = typeof process.env.SPAPS_ACCESS_TOKEN === 'string' && process.env.SPAPS_ACCESS_TOKEN.trim() ? process.env.SPAPS_ACCESS_TOKEN.trim() : null; const appContext = resolveAppContext({ cwd, runtime }); const apiKey = resolveAuthApiKey({ cwd }); const operator = { server_url: resolvedServerUrl, connected: Boolean(envAccessToken || storedCreds?.access_token), source: envAccessToken ? 'SPAPS_ACCESS_TOKEN' : storedCreds?.access_token ? 'credentials' : null, credentials_path: CREDENTIALS_PATH, user_id: storedCreds?.user_id || null, client_id: storedCreds?.client_id || null, expires_at: storedCreds?.expires_at || null, }; const app = { client_id: appContext.clientId, client_id_source: appContext.source, client_id_path: appContext.path, api_key_configured: Boolean(apiKey.apiKey), api_key_source: apiKey.source, api_key_path: apiKey.path, api_key_env: apiKey.envVar || null, }; return { success: true, command: 'home', operator, app, runtime, recommended_next: buildRecommendedNext({ operator, runtime, port: resolvedPort, }), }; } function renderHomeView(view, { logo = '' } = {}) { if (logo) { console.log(logo); } console.log(chalk.bold('Operator')); console.log(' Server:', chalk.cyan(view.operator.server_url)); console.log( ' Session:', view.operator.connected ? chalk.green(`connected via ${view.operator.source || 'credentials'}`) : chalk.yellow('not connected') ); if (view.operator.client_id) { console.log(' Session app:', chalk.cyan(view.operator.client_id)); } if (view.operator.user_id) { console.log(' User:', chalk.cyan(view.operator.user_id)); } console.log(); console.log(chalk.bold('App')); if (view.app.client_id) { console.log(' Client id:', chalk.cyan(view.app.client_id)); if (view.app.client_id_source) { console.log(chalk.gray(` source: ${formatSource(view.app.client_id_source, view.app.client_id_path)}`)); } } else { console.log(' Client id:', chalk.yellow('not detected')); } console.log( ' API key:', view.app.api_key_configured ? chalk.green(`configured via ${formatSource(view.app.api_key_source, view.app.api_key_path)}`) : chalk.yellow('not found') ); console.log(); console.log(chalk.bold('Runtime')); if (!view.runtime.running) { console.log(' Status:', chalk.red('unreachable')); console.log(' Target:', chalk.cyan(view.runtime.url)); if (view.runtime.error?.message) { console.log(chalk.gray(` ${view.runtime.error.message}`)); } } else { console.log(' Status:', chalk.green('running')); console.log(' URL:', chalk.cyan(view.runtime.url)); console.log(' Mode:', chalk.cyan(formatMode(view.runtime))); } console.log(); console.log(chalk.bold('Next')); console.log(' ' + chalk.cyan(view.recommended_next.command)); console.log(chalk.gray(` ${view.recommended_next.reason}`)); console.log(); } module.exports = { buildHomeView, renderHomeView, };