tangled mirror of catsky-🐱 Soothing soft social-app fork with all the niche toggles! (Unofficial); for issues and PRs please put them on github:NekoDrone/catsky-social

Start migrating blink. Also add better shutdown handling for ogcard

Changed files
+97 -27
.github
bskylink
src
bskyogcard
src
+55 -22
bskylink/src/bin.ts
··· 1 import {Database, envToCfg, httpLogger, LinkService, readEnv} from './index.js' 2 async function main() { 3 - const env = readEnv() 4 - const cfg = envToCfg(env) 5 - if (cfg.db.migrationUrl) { 6 - const migrateDb = Database.postgres({ 7 - url: cfg.db.migrationUrl, 8 - schema: cfg.db.schema, 9 - }) 10 - await migrateDb.migrateToLatestOrThrow() 11 - await migrateDb.close() 12 - } 13 14 - const link = await LinkService.create(cfg) 15 16 - if (link.ctx.cfg.service.safelinkEnabled) { 17 - link.ctx.safelinkClient.runFetchEvents() 18 } 19 - 20 - await link.start() 21 - httpLogger.info('link service is running') 22 - process.on('SIGTERM', async () => { 23 - httpLogger.info('link service is stopping') 24 - await link.destroy() 25 - httpLogger.info('link service is stopped') 26 - }) 27 } 28 29 - main()
··· 1 import {Database, envToCfg, httpLogger, LinkService, readEnv} from './index.js' 2 + 3 async function main() { 4 + try { 5 + httpLogger.info('Starting blink service') 6 + 7 + const env = readEnv() 8 + const cfg = envToCfg(env) 9 + 10 + httpLogger.info( 11 + { 12 + port: cfg.service.port, 13 + safelinkEnabled: cfg.service.safelinkEnabled, 14 + hasDbUrl: !!cfg.db.url, 15 + hasDbMigrationUrl: !!cfg.db.migrationUrl, 16 + }, 17 + 'Configuration loaded', 18 + ) 19 + 20 + if (cfg.db.migrationUrl) { 21 + httpLogger.info('Running database migrations') 22 + const migrateDb = Database.postgres({ 23 + url: cfg.db.migrationUrl, 24 + schema: cfg.db.schema, 25 + }) 26 + await migrateDb.migrateToLatestOrThrow() 27 + await migrateDb.close() 28 + httpLogger.info('Database migrations completed') 29 + } 30 + 31 + httpLogger.info('Creating LinkService') 32 + const link = await LinkService.create(cfg) 33 + 34 + if (link.ctx.cfg.service.safelinkEnabled) { 35 + httpLogger.info('Starting Safelink client') 36 + link.ctx.safelinkClient.runFetchEvents() 37 + } 38 39 + await link.start() 40 + httpLogger.info('Link service is running') 41 42 + process.on('SIGTERM', async () => { 43 + httpLogger.info('Link service is stopping') 44 + await link.destroy() 45 + httpLogger.info('Link service is stopped') 46 + }) 47 + } catch (error) { 48 + httpLogger.error( 49 + { 50 + error: String(error), 51 + stack: error instanceof Error ? error.stack : undefined, 52 + }, 53 + 'Failed to start blink service', 54 + ) 55 + process.exit(1) 56 } 57 } 58 59 + main().catch(error => { 60 + console.error('Unhandled startup error:', error) 61 + process.exit(1) 62 + })
+10
bskylink/src/db/index.ts
··· 34 35 static postgres(opts: PgOptions): Database { 36 const {schema, url, txLockNonce} = opts 37 const pool = 38 opts.pool ?? 39 new Pg.Pool({
··· 34 35 static postgres(opts: PgOptions): Database { 36 const {schema, url, txLockNonce} = opts 37 + log.info( 38 + { 39 + schema, 40 + poolSize: opts.poolSize, 41 + poolMaxUses: opts.poolMaxUses, 42 + poolIdleTimeoutMs: opts.poolIdleTimeoutMs, 43 + }, 44 + 'Creating database connection', 45 + ) 46 + 47 const pool = 48 opts.pool ?? 49 new Pg.Pool({
+26 -4
bskyogcard/src/index.ts
··· 62 // Start main application server 63 this.server = this.app.listen(this.ctx.cfg.service.port) 64 this.server.keepAliveTimeout = 90000 65 - this.terminator = createHttpTerminator({server: this.server}) 66 await events.once(this.server, 'listening') 67 68 // Start separate metrics server ··· 73 }) 74 75 this.metricsServer = metricsApp.listen(this.ctx.cfg.service.metricsPort) 76 - this.metricsTerminator = createHttpTerminator({server: this.metricsServer}) 77 await events.once(this.metricsServer, 'listening') 78 } 79 80 async destroy() { 81 this.ctx.abortController.abort() 82 - await this.terminator?.terminate() 83 - await this.metricsTerminator?.terminate() 84 } 85 }
··· 62 // Start main application server 63 this.server = this.app.listen(this.ctx.cfg.service.port) 64 this.server.keepAliveTimeout = 90000 65 + this.terminator = createHttpTerminator({ 66 + server: this.server, 67 + gracefulTerminationTimeout: 15000, // 15s timeout for in-flight requests 68 + }) 69 await events.once(this.server, 'listening') 70 71 // Start separate metrics server ··· 76 }) 77 78 this.metricsServer = metricsApp.listen(this.ctx.cfg.service.metricsPort) 79 + this.metricsTerminator = createHttpTerminator({ 80 + server: this.metricsServer, 81 + gracefulTerminationTimeout: 2000, // 2s timeout for metrics server 82 + }) 83 await events.once(this.metricsServer, 'listening') 84 } 85 86 async destroy() { 87 + const startTime = Date.now() 88 + 89 this.ctx.abortController.abort() 90 + 91 + const shutdownPromises = [] 92 + 93 + if (this.terminator) { 94 + shutdownPromises.push(this.terminator.terminate()) 95 + } 96 + 97 + if (this.metricsTerminator) { 98 + shutdownPromises.push(this.metricsTerminator.terminate()) 99 + } 100 + 101 + await Promise.all(shutdownPromises) 102 + 103 + const elapsed = Date.now() - startTime 104 + const {httpLogger} = await import('./logger.js') 105 + httpLogger.info(`Graceful shutdown completed in ${elapsed}ms`) 106 } 107 }