mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-10-21 02:56:18 +00:00
Closes the "backend has zero CI + manual SSH deploy" audit finding for the indexer. Part B — CI-runnable tests (the integration-tests/*.live.test.js need a live chain and are excluded, not stubbed): extract injectable cores (indexer.js: DB+HTTP+decode, no @pezkuwi import; council.js: createApp factory for the council/KYC routes). New node:test suite (19/19, fully offline) — in-memory sqlite, dependency-injected chain stub, an in-memory fake-supabase, and REAL @pezkuwi keyring signatures proving 401 bad-sig / 400 msg-mismatch / 200 valid / 409 dup / 403 non-member / threshold auto-execute. Backend job now runs npm ci + npm test and is in ci-gate (failing tests block merge). Fixed a latent bug: block indexing now awaits each insert (was fire-and-forget forEach). Added runtime deps server.js imported but were missing from package.json (@supabase/supabase-js, pino, pino-http) — server.js could not have `npm ci`-run before. Part C — Dockerfile (non-root, HEALTHCHECK /health, sqlite state on a /data VOLUME kept out of the image) + backend deploy pipeline mirroring the web one: build+push to GHCR, cosign keyless sign + verify, ssh deploy with the DB volume preserved across deploys, /health poll, auto-rollback to previous SHA, same Telegram CEO approval gate, main/tags only, never fork PRs. New secrets documented in backend/DEPLOY.md (BACKEND_VPS_HOST/USER/ SSH_KEY). DB_PATH env (default ./transactions.db) lets prod point at the volume. Note for owner: if server.js (council/KYC bootstrap) is legacy/unused, its newly-added deps can be dropped instead.
43 lines
1.5 KiB
JavaScript
43 lines
1.5 KiB
JavaScript
// Indexer service bootstrap.
|
|
//
|
|
// Thin wiring layer: connects to the live chain and feeds decoded blocks into
|
|
// the injectable core in ./indexer.js (which holds all the DB + HTTP logic and
|
|
// is unit-tested offline). Keeping the @pezkuwi/api connection isolated here
|
|
// means the testable core never touches the network.
|
|
|
|
import { ApiPromise, WsProvider } from '@pezkuwi/api'
|
|
import dotenv from 'dotenv'
|
|
import { initDb, indexBlock, createApp } from './indexer.js'
|
|
|
|
dotenv.config()
|
|
|
|
const port = process.env.PORT || 3001
|
|
const WS_ENDPOINT = process.env.WS_ENDPOINT || 'wss://rpc.pezkuwichain.io'
|
|
|
|
// Start Indexing
|
|
async function startIndexer (db) {
|
|
console.log(`Connecting to Pezkuwi Node: ${WS_ENDPOINT}`)
|
|
const provider = new WsProvider(WS_ENDPOINT)
|
|
const api = await ApiPromise.create({ provider })
|
|
|
|
console.log('Connected! Listening for new blocks...')
|
|
|
|
api.rpc.chain.subscribeNewHeads(async (header) => {
|
|
const blockNumber = header.number.toNumber()
|
|
const blockHash = await api.rpc.chain.getBlockHash(blockNumber)
|
|
const signedBlock = await api.rpc.chain.getBlock(blockHash)
|
|
await indexBlock(db, signedBlock, blockNumber)
|
|
})
|
|
}
|
|
|
|
// Launch
|
|
// DB_PATH lets the deploy point the stateful sqlite file at a mounted volume
|
|
// (e.g. /data/transactions.db); defaults to the historical ./transactions.db.
|
|
const db = await initDb(process.env.DB_PATH || './transactions.db')
|
|
startIndexer(db)
|
|
|
|
const app = createApp(db)
|
|
app.listen(port, () => {
|
|
console.log(`Indexer API running at http://localhost:${port}`)
|
|
})
|