From 6797dbada27873b14c064e238589e61ad3ecb67c Mon Sep 17 00:00:00 2001 From: asepharyana Date: Sun, 6 Sep 2026 01:13:48 +0700 Subject: [PATCH] fix(health): resolve app version in Nix bundle at runtime The health endpoint was returning version 0.0.0 because readPackageVersion() tried import.meta.require on a JSON path that Bun cannot bundle and the package.json never shipped into the Nix store (installPhase only copied dist/, home.html, schema.sql). - flake.nix installPhase now copies package.json into $out/share/teleuploader/ - readPackageVersion() reads import.meta.dir/../package.json (bundle) or process.cwd()/package.json (dev) via readFileSync at runtime Verified local resolution to 1.2.1; after this fixes deploy it will expose the live semver on GET /health. --- flake.nix | 3 +++ src/env.ts | 35 +++++++++++++++++------------------ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/flake.nix b/flake.nix index c7ce227..1506f41 100644 --- a/flake.nix +++ b/flake.nix @@ -61,6 +61,9 @@ cp -r dist $out/share/teleuploader/dist cp src/home.html $out/share/teleuploader/ 2>/dev/null || true cp schema.sql $out/share/teleuploader/ 2>/dev/null || true + # Carry package.json into the store so runtime version lookup in + # env.ts readPackageVersion() resolves (dist/../package.json). + cp package.json $out/share/teleuploader/package.json # Wrap with bun from Nix store (dependency sharing!) # Note: NO --chdir — systemd WorkingDirectory controls this diff --git a/src/env.ts b/src/env.ts index 42d92ef..fb35aa1 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,3 +1,4 @@ +import { readFileSync } from 'node:fs'; import logger from './shared/logger/index'; import { TELEGRAM_CHUNK_SIZE_MAX_BYTES } from './shared/utils/validation'; @@ -143,25 +144,23 @@ if (telegramChunkSizeBytes > TELEGRAM_CHUNK_SIZE_MAX_BYTES) { * value always matches the deployed build. */ const readPackageVersion = (): string => { - try { - const candidates = [ - // node_modules resolution is only available when installed as a dep - import.meta.dir ? `${import.meta.dir}/../package.json` : undefined, - `${process.cwd()}/package.json`, - ]; - for (const candidate of candidates) { - if (!candidate) continue; - try { - const pkg = JSON.parse(import.meta.require(candidate).default ?? '{}') as { - version?: string; - }; - if (pkg.version) return pkg.version; - } catch { - // try next candidate - } + // Candidates in priority order. In the Nix bundle the app lives at + // $out/share/teleuploader/dist/index.js, so the package.json that was + // copied alongside it is one level up from import.meta.dir. In a dev + // checkout it sits in the repo root (process.cwd()). Bun bundles JSON + // imports statically, so we read the file at runtime instead. + const candidates = [ + import.meta.dir ? `${import.meta.dir}/../package.json` : undefined, + `${process.cwd()}/package.json`, + ]; + for (const candidate of candidates) { + if (!candidate) continue; + try { + const parsed = JSON.parse(readFileSync(candidate, 'utf8')) as { version?: string }; + if (parsed.version) return parsed.version; + } catch { + // try next candidate } - } catch { - // fall through } return '0.0.0'; };