mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 07:53:09 +00:00
refactor(server): inject the transit base URL and the kitinerary paths as config tokens
The ownership rule says a boot-stable value that only Nest providers read belongs to a registerAs token, yet TransitService froze TRANSIT_API_URL in a module-top readEnv() const and KitineraryExtractorService read KITINERARY_EXTRACTOR_PATH and PATH through readEnv() in its probe. Both now come from tokens: transitConfig and kitineraryConfig, derived in boot-derive.ts and dropped from deriveIntegrations, so readEnv() no longer reads those variables at all. TransitModule and KitineraryExtractorModule import AppConfigModule, which keeps them buildable on their own in tests. Behaviour is unchanged: the same defaults, the same trailing-slash strip and PATH split, now frozen per built app instead of at import. The app-config README names each boot-stable variable that stays on readEnv() together with the reader the container cannot inject into.
This commit is contained in:
@@ -101,12 +101,31 @@ Every variable has exactly one owner, and the owner decides how it is read:
|
||||
`boot-derive.ts`): boot-stable values that only Nest classes need. The token
|
||||
is snapshotted per built app and injected (`@Inject(storageConfig.KEY)`, or
|
||||
`app.get(httpConfig.KEY)` for the pre-init Express layer). Today:
|
||||
`httpConfig` (TRUST_PROXY, HSTS_INCLUDE_SUBDOMAINS, HTTP_KEEP_ALIVE_TIMEOUT_MS) and `storageConfig`
|
||||
(TREK_PLACE_PHOTO_DIR).
|
||||
`httpConfig` (TRUST_PROXY, HSTS_INCLUDE_SUBDOMAINS, HTTP_KEEP_ALIVE_TIMEOUT_MS),
|
||||
`storageConfig` (TREK_PLACE_PHOTO_DIR), `transitConfig` (TRANSIT_API_URL,
|
||||
for TransitService) and `kitineraryConfig` (KITINERARY_EXTRACTOR_PATH and
|
||||
PATH, for KitineraryExtractorService's binary probe). A module whose provider
|
||||
injects one imports `AppConfigModule`, so it also builds on its own in a test.
|
||||
- **`readEnv()` / `RuntimeEnvService`** (`derive.ts`): everything else, both
|
||||
the runtime-toggled values and the boot-stable ones that code outside the
|
||||
container needs (config.ts, the database, src/mcp, the SSRF guard), which
|
||||
freeze them in module-top consts.
|
||||
the runtime-toggled values and the boot-stable ones that code outside a Nest
|
||||
provider reads, which freezes them in module-top consts. Each of those has a
|
||||
reader the container cannot inject into:
|
||||
- PORT, HOST: `index.ts`, before the app exists.
|
||||
- SESSION_DURATION(_REMEMBER), DEFAULT_LANGUAGE, ENCRYPTION_KEY: `src/config.ts`,
|
||||
a plain module evaluated at process start.
|
||||
- MCP_*: `src/mcp/`, the process-wide session state.
|
||||
- TREK_PLUGIN_RPC_*/LOG_*/MAX_RSS_MB: the plugin host and supervisor, which
|
||||
are deliberately not Nest (the sandbox boundary).
|
||||
- TREK_PLUGIN_REGISTRY_URL: `PluginRegistryService` is a provider, but several
|
||||
plugin suites build it by hand with a trailing `@Optional()` UnitOfWork, so
|
||||
a required token parameter cannot go in without reordering that signature.
|
||||
- TREK_WIKI_DIR: `nest/help/wiki.ts`, a plain module the help MCP tools call.
|
||||
- BACKUP_*: `nest/backup/backup-archive.ts`, which the first-start restore
|
||||
(`boot-restore.ts`, called from `index.ts`) runs before the database opens.
|
||||
- LOG_LEVEL: the logger, imported everywhere.
|
||||
- ALLOW_INTERNAL_NETWORK, ALLOW_LINK_LOCAL_IPS: the SSRF guard in `utils/`.
|
||||
- TREK_DB_FILE, TREK_DB_JOURNAL_MODE, TREK_DB_SYNCHRONOUS: the database,
|
||||
opened before the container.
|
||||
|
||||
`deriveAll()` never reads a variable a token owns, and a token is dropped once
|
||||
nothing injects it. `tests/unit/app-config/config-ownership.test.ts` records
|
||||
@@ -129,7 +148,7 @@ variable is token-owned, module-top `readEnv()` consts elsewhere):
|
||||
PORT, HOST, TRUST_PROXY, HSTS_INCLUDE_SUBDOMAINS, HTTP_KEEP_ALIVE_TIMEOUT_MS, SESSION_DURATION(_REMEMBER), MCP_SESSION_TTL,
|
||||
MCP_MAX_SESSION_PER_USER, MCP_SSE_KEEPALIVE, TREK_PLUGIN_RPC_*/LOG_*/MAX_RSS_MB,
|
||||
TREK_PLUGIN_REGISTRY_URL, TREK_WIKI_DIR*, TREK_PLACE_PHOTO_DIR, BACKUP_*,
|
||||
TRANSIT_API_URL, LOG_LEVEL*, ALLOW_INTERNAL_NETWORK*, ALLOW_LINK_LOCAL_IPS*, DEFAULT_LANGUAGE,
|
||||
TRANSIT_API_URL, KITINERARY_EXTRACTOR_PATH, LOG_LEVEL*, ALLOW_INTERNAL_NETWORK*, ALLOW_LINK_LOCAL_IPS*, DEFAULT_LANGUAGE,
|
||||
TREK_DB_FILE, TREK_DB_JOURNAL_MODE, TREK_DB_SYNCHRONOUS, ENCRYPTION_KEY**.
|
||||
(* frozen today because the consuming module captures it at import; tests that
|
||||
override these set them at file top, before the SUT import.)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* mutate mid-lifetime, belongs in derive.ts instead.
|
||||
*/
|
||||
import type { RawEnv } from './derive';
|
||||
import { parseBool, positiveIntOr } from './parsers';
|
||||
import { parseBool, positiveIntOr, stripTrailingSlashes } from './parsers';
|
||||
|
||||
/**
|
||||
* Longer than the upstream idle timeout of the common reverse proxies and load
|
||||
@@ -42,10 +42,32 @@ export function deriveStorage(raw: RawEnv) {
|
||||
};
|
||||
}
|
||||
|
||||
/** The Transitous/MOTIS instance TransitService proxies to. */
|
||||
export function deriveTransit(raw: RawEnv) {
|
||||
return {
|
||||
apiBase: stripTrailingSlashes(raw.TRANSIT_API_URL || 'https://api.transitous.org'),
|
||||
};
|
||||
}
|
||||
|
||||
/** Where KitineraryExtractorService looks for its binary when the module starts. */
|
||||
export function deriveKitinerary(raw: RawEnv) {
|
||||
return {
|
||||
extractorPath: raw.KITINERARY_EXTRACTOR_PATH,
|
||||
// Windows spells it Path; every other platform PATH. Split here so the
|
||||
// probe gets a list and never re-implements the delimiter.
|
||||
searchPath: (raw.PATH || raw.Path || '')
|
||||
.split(process.platform === 'win32' ? ';' : ':')
|
||||
.map((p) => p.trim())
|
||||
.filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
/** One entry per token: the namespace it registers under and the function it derives with. */
|
||||
export const BOOT_DERIVERS = {
|
||||
http: deriveHttpBoot,
|
||||
storage: deriveStorage,
|
||||
transit: deriveTransit,
|
||||
kitinerary: deriveKitinerary,
|
||||
} as const;
|
||||
|
||||
export type BootNamespace = keyof typeof BOOT_DERIVERS;
|
||||
|
||||
@@ -279,7 +279,6 @@ export function deriveWebauthn(raw: RawEnv) {
|
||||
export function deriveIntegrations(raw: RawEnv) {
|
||||
return {
|
||||
unsplashAccessKey: raw.UNSPLASH_ACCESS_KEY?.trim(),
|
||||
transitApiBase: stripTrailingSlashes(raw.TRANSIT_API_URL || 'https://api.transitous.org'),
|
||||
// Trimmed before the default fires: the schema validates the trimmed value and
|
||||
// treats a blank one as unset, so a padded or whitespace-only value would
|
||||
// otherwise pass startup and then be the string that cannot be fetched.
|
||||
@@ -288,7 +287,6 @@ export function deriveIntegrations(raw: RawEnv) {
|
||||
// Longer than the `[timeout:20]` the query itself carries, or we abort an answer the
|
||||
// mirror was still allowed to be working on. See OVERPASS_QUERY_TIMEOUT_S.
|
||||
overpassTimeoutMs: positiveNumberOr(raw.OVERPASS_TIMEOUT_MS, 25000),
|
||||
kitineraryExtractorPath: raw.KITINERARY_EXTRACTOR_PATH,
|
||||
/**
|
||||
* One ceiling for a model call, replacing the three per-client constants
|
||||
* that used to disagree. The default is deliberately generous: heavier
|
||||
@@ -297,12 +295,9 @@ export function deriveIntegrations(raw: RawEnv) {
|
||||
* rejects a fractional value.
|
||||
*/
|
||||
llmTimeoutMs: Math.floor(positiveNumberOr(raw.LLM_TIMEOUT_MS, 900_000)),
|
||||
// Windows spells it Path; every other platform PATH. Split here so callers
|
||||
// get a list and never re-implement the delimiter.
|
||||
searchPath: (raw.PATH || raw.Path || '')
|
||||
.split(process.platform === 'win32' ? ';' : ':')
|
||||
.map(p => p.trim())
|
||||
.filter(Boolean),
|
||||
// TRANSIT_API_URL, KITINERARY_EXTRACTOR_PATH and PATH are boot-stable and
|
||||
// read only by Nest providers, so the transitConfig and kitineraryConfig
|
||||
// tokens own them (boot-derive.ts).
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user