API: Phoenix railing proxy, API key auth for /api/v1/*, schema export, docs, migrations, tests
- Phoenix API Railing: proxy to PHOENIX_RAILING_URL, tenant me routes - Tenant-auth: X-API-Key support for /api/v1/* (api_keys table) - Migration 026: api_keys table; 025 sovereign stack marketplace - GET /graphql/schema, GET /graphql-playground, api/docs OpenAPI - Integration tests: phoenix-railing.test.ts - docs/api/API_VERSIONING: /api/v1/ railing alignment - docs/phoenix/PORTAL_RAILING_WIRING Made-with: Cursor
This commit is contained in:
141
api/scripts/verify-sovereign-stack.ts
Normal file
141
api/scripts/verify-sovereign-stack.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Verification script for Sovereign Stack marketplace services
|
||||
* Verifies that all services are properly registered in the marketplace
|
||||
*/
|
||||
|
||||
import 'dotenv/config'
|
||||
import { getDb } from '../src/db/index.js'
|
||||
import { logger } from '../src/lib/logger.js'
|
||||
|
||||
async function verifySovereignStackServices() {
|
||||
const db = getDb()
|
||||
|
||||
try {
|
||||
logger.info('Verifying Sovereign Stack marketplace services...')
|
||||
|
||||
// 1. Verify Phoenix publisher exists
|
||||
const publisherResult = await db.query(
|
||||
`SELECT * FROM publishers WHERE name = 'phoenix-cloud-services'`
|
||||
)
|
||||
|
||||
if (publisherResult.rows.length === 0) {
|
||||
throw new Error('Phoenix publisher not found. Please run migration 025 first.')
|
||||
}
|
||||
|
||||
const publisher = publisherResult.rows[0]
|
||||
logger.info(`✓ Phoenix publisher found: ${publisher.display_name} (${publisher.id})`)
|
||||
logger.info(` Verified: ${publisher.verified}`)
|
||||
logger.info(` Website: ${publisher.website_url || 'N/A'}`)
|
||||
|
||||
// 2. Verify all 9 services exist
|
||||
const expectedServices = [
|
||||
'phoenix-ledger-service',
|
||||
'phoenix-identity-service',
|
||||
'phoenix-wallet-registry',
|
||||
'phoenix-tx-orchestrator',
|
||||
'phoenix-messaging-orchestrator',
|
||||
'phoenix-voice-orchestrator',
|
||||
'phoenix-event-bus',
|
||||
'phoenix-audit-service',
|
||||
'phoenix-observability'
|
||||
]
|
||||
|
||||
const servicesResult = await db.query(
|
||||
`SELECT p.*, pub.display_name as publisher_name
|
||||
FROM products p
|
||||
JOIN publishers pub ON p.publisher_id = pub.id
|
||||
WHERE pub.name = 'phoenix-cloud-services'
|
||||
ORDER BY p.name`
|
||||
)
|
||||
|
||||
const foundServices = servicesResult.rows.map(row => row.slug)
|
||||
logger.info(`\n✓ Found ${servicesResult.rows.length} Phoenix services:`)
|
||||
|
||||
for (const service of servicesResult.rows) {
|
||||
logger.info(` - ${service.name} (${service.slug})`)
|
||||
logger.info(` Category: ${service.category}`)
|
||||
logger.info(` Status: ${service.status}`)
|
||||
logger.info(` Featured: ${service.featured}`)
|
||||
}
|
||||
|
||||
// Check for missing services
|
||||
const missingServices = expectedServices.filter(slug => !foundServices.includes(slug))
|
||||
if (missingServices.length > 0) {
|
||||
logger.warn(`\n⚠ Missing services: ${missingServices.join(', ')}`)
|
||||
logger.warn('Please run: pnpm db:seed:sovereign-stack')
|
||||
} else {
|
||||
logger.info(`\n✓ All ${expectedServices.length} expected services found!`)
|
||||
}
|
||||
|
||||
// 3. Verify categories are available
|
||||
const categoriesResult = await db.query(
|
||||
`SELECT DISTINCT category FROM products WHERE publisher_id = $1`,
|
||||
[publisher.id]
|
||||
)
|
||||
|
||||
const categories = categoriesResult.rows.map(row => row.category)
|
||||
logger.info(`\n✓ Services span ${categories.length} categories:`)
|
||||
categories.forEach(cat => logger.info(` - ${cat}`))
|
||||
|
||||
// 4. Verify product versions exist
|
||||
const versionsResult = await db.query(
|
||||
`SELECT COUNT(*) as count
|
||||
FROM product_versions pv
|
||||
JOIN products p ON pv.product_id = p.id
|
||||
JOIN publishers pub ON p.publisher_id = pub.id
|
||||
WHERE pub.name = 'phoenix-cloud-services'`
|
||||
)
|
||||
|
||||
const versionCount = parseInt(versionsResult.rows[0].count)
|
||||
logger.info(`\n✓ Found ${versionCount} product versions`)
|
||||
|
||||
// 5. Verify pricing models exist
|
||||
const pricingResult = await db.query(
|
||||
`SELECT COUNT(*) as count
|
||||
FROM pricing_models pm
|
||||
JOIN products p ON pm.product_id = p.id
|
||||
JOIN publishers pub ON p.publisher_id = pub.id
|
||||
WHERE pub.name = 'phoenix-cloud-services'`
|
||||
)
|
||||
|
||||
const pricingCount = parseInt(pricingResult.rows[0].count)
|
||||
logger.info(`✓ Found ${pricingCount} pricing models`)
|
||||
|
||||
// 6. Summary
|
||||
logger.info('\n' + '='.repeat(60))
|
||||
logger.info('VERIFICATION SUMMARY')
|
||||
logger.info('='.repeat(60))
|
||||
logger.info(`Publisher: ${publisher.display_name} (${publisher.verified ? '✓ Verified' : '✗ Not verified'})`)
|
||||
logger.info(`Services: ${foundServices.length}/${expectedServices.length}`)
|
||||
logger.info(`Categories: ${categories.length}`)
|
||||
logger.info(`Versions: ${versionCount}`)
|
||||
logger.info(`Pricing Models: ${pricingCount}`)
|
||||
|
||||
if (missingServices.length === 0 && versionCount >= expectedServices.length && pricingCount >= expectedServices.length) {
|
||||
logger.info('\n✅ All Sovereign Stack services verified successfully!')
|
||||
return true
|
||||
} else {
|
||||
logger.warn('\n⚠ Some services may need to be seeded. Run: pnpm db:seed:sovereign-stack')
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Verification error', { error })
|
||||
throw error
|
||||
} finally {
|
||||
await db.end()
|
||||
}
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
verifySovereignStackServices()
|
||||
.then(success => {
|
||||
process.exit(success ? 0 : 1)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Failed to verify Sovereign Stack services', { error })
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
|
||||
export { verifySovereignStackServices }
|
||||
Reference in New Issue
Block a user