From 93511ae4c6628adf030910d925c9d8da839345f6 Mon Sep 17 00:00:00 2001 From: Noah Saso <6721426+NoahSaso@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:23:36 -0400 Subject: [PATCH] feat: add package version to /up, /status, and /health responses --- src/server/routes/index.ts | 2 ++ src/server/routes/indexer/getStatus.ts | 3 +++ src/server/routes/indexer/up.ts | 5 +++++ src/server/test/indexer/getStatus.test.ts | 2 ++ src/server/test/indexer/health.test.ts | 1 + src/server/test/indexer/version.test.ts | 26 +++++++++++++++++++++++ src/version.ts | 10 +++++++++ 7 files changed, 49 insertions(+) create mode 100644 src/server/test/indexer/version.test.ts create mode 100644 src/version.ts diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts index cfec3b85..3f48bca0 100644 --- a/src/server/routes/index.ts +++ b/src/server/routes/index.ts @@ -2,6 +2,7 @@ import Router from '@koa/router' import Koa from 'koa' import { Config } from '@/types' +import { version } from '@/version' import { accountRouter } from './account' import { setUpDocs } from './docs' @@ -31,6 +32,7 @@ export const setUpRouter = async ( ctx.status = 200 ctx.body = { status: 'ok', + version, timestamp: new Date().toISOString(), } }) diff --git a/src/server/routes/indexer/getStatus.ts b/src/server/routes/indexer/getStatus.ts index 22f8f37e..06ac8b90 100644 --- a/src/server/routes/indexer/getStatus.ts +++ b/src/server/routes/indexer/getStatus.ts @@ -4,9 +4,11 @@ import { DefaultContext, DefaultState } from 'koa' import { State } from '@/db' import { SerializedBlock } from '@/types' import { serializeBlock } from '@/utils' +import { version } from '@/version' type GetStatusResponse = | { + version: string chainId: string latestBlock: SerializedBlock lastStakingBlockHeightExported: string | null @@ -35,6 +37,7 @@ export const getStatus: Router.Middleware< ctx.status = 200 ctx.body = { + version, chainId: state.chainId, latestBlock: serializeBlock(state.latestBlock), lastStakingBlockHeightExported: diff --git a/src/server/routes/indexer/up.ts b/src/server/routes/indexer/up.ts index 7c289a4a..fc22b17f 100644 --- a/src/server/routes/indexer/up.ts +++ b/src/server/routes/indexer/up.ts @@ -4,6 +4,7 @@ import { DefaultContext, DefaultState } from 'koa' import { ConfigManager } from '@/config' import { State } from '@/db' import { getStargateClient } from '@/utils' +import { version } from '@/version' type UpBlock = { height: number @@ -13,6 +14,7 @@ type UpBlock = { type UpResponse = | { + version: string chainId: string remoteBlock: UpBlock localBlock: UpBlock | { error: string } | null @@ -25,6 +27,7 @@ type UpResponse = } } | { + version: string error: string } @@ -120,6 +123,7 @@ export const up: Router.Middleware< } catch (err) { ctx.status = 500 ctx.body = { + version, error: err instanceof Error ? err.message : `${err}`, } return @@ -156,6 +160,7 @@ export const up: Router.Middleware< ctx.status = caughtUp ? 200 : 412 ctx.body = { + version, chainId: state.chainId, remoteBlock, localBlock, diff --git a/src/server/test/indexer/getStatus.test.ts b/src/server/test/indexer/getStatus.test.ts index e1da0919..3af5965e 100644 --- a/src/server/test/indexer/getStatus.test.ts +++ b/src/server/test/indexer/getStatus.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest' import { State } from '@/db' import { serializeBlock } from '@/utils' +import { version } from '@/version' import { app } from './app' @@ -16,6 +17,7 @@ describe('GET /status', () => { .expect(200) .expect('Content-Type', /json/) .expect({ + version, chainId: state.chainId, latestBlock: serializeBlock(state.latestBlock), lastStakingBlockHeightExported: diff --git a/src/server/test/indexer/health.test.ts b/src/server/test/indexer/health.test.ts index 51a57e0f..f91449c2 100644 --- a/src/server/test/indexer/health.test.ts +++ b/src/server/test/indexer/health.test.ts @@ -10,6 +10,7 @@ describe('health', () => { .expect(200) .expect((res) => { expect(res.body.status).toBe('ok') + expect(res.body.version).toBeTypeOf('string') expect(res.body.timestamp).toBeTypeOf('string') }) }) diff --git a/src/server/test/indexer/version.test.ts b/src/server/test/indexer/version.test.ts new file mode 100644 index 00000000..47c20bd8 --- /dev/null +++ b/src/server/test/indexer/version.test.ts @@ -0,0 +1,26 @@ +import request from 'supertest' +import { describe, expect, it } from 'vitest' + +import { version } from '@/version' + +import { app } from './app' + +describe('version in responses', () => { + it('/health includes version', async () => { + await request(app.callback()) + .get('/health') + .expect(200) + .expect((res) => { + expect(res.body.version).toBe(version) + }) + }) + + it('/status includes version', async () => { + await request(app.callback()) + .get('/status') + .expect(200) + .expect((res) => { + expect(res.body.version).toBe(version) + }) + }) +}) diff --git a/src/version.ts b/src/version.ts new file mode 100644 index 00000000..0dda36fd --- /dev/null +++ b/src/version.ts @@ -0,0 +1,10 @@ +import { readFileSync } from 'fs' +import { join } from 'path' + +// Read version from package.json at startup. Using readFileSync since this +// runs once at import time, not per-request. +const pkg = JSON.parse( + readFileSync(join(__dirname, '../package.json'), 'utf-8') +) + +export const version: string = pkg.version