feat: expose safe build metadata and verify foundation
This commit is contained in:
@@ -51,3 +51,11 @@ scripts/teamcity/rollback.sh # redeploy the previous IMAGE_TAG
|
|||||||
## Production topology
|
## Production topology
|
||||||
|
|
||||||
Production Docker Compose (`compose.yml`) publishes **exactly one** host port, on the `edge` (Nginx) service, which serves the built Angular app and reverse-proxies `/api/*` and `/health/*` to the internal `api` service. `api`, `worker`, `postgres`, and `redis` are reachable only over the internal Docker network. See `docs/architecture/deployment.md` for the full contract and `scripts/teamcity/` for the TeamCity-invoked build/deploy/rollback scripts.
|
Production Docker Compose (`compose.yml`) publishes **exactly one** host port, on the `edge` (Nginx) service, which serves the built Angular app and reverse-proxies `/api/*` and `/health/*` to the internal `api` service. `api`, `worker`, `postgres`, and `redis` are reachable only over the internal Docker network. See `docs/architecture/deployment.md` for the full contract and `scripts/teamcity/` for the TeamCity-invoked build/deploy/rollback scripts.
|
||||||
|
|
||||||
|
## Phase 01 status: foundation complete
|
||||||
|
|
||||||
|
- `GET /health/live` — process liveness only, no dependency checks.
|
||||||
|
- `GET /health/ready` — validates PostgreSQL and Redis connectivity.
|
||||||
|
- `GET /api/v1/version` — safe build metadata only (`appVersion`, `teamCityBuildNumber`, `sourceRevision`); never database/Redis URLs.
|
||||||
|
- The compiled no-op migration entry point (`backend/dist/apps/api/src/migration.js`) gives `deploy.sh` a stable container command contract; Phase 02 replaces its body with the real versioned migration runner.
|
||||||
|
- Verified end-to-end: `docker compose -f compose.yml up` with a real TLS certificate serves `/health/live`, `/health/ready`, `/`, and `/api/v1/version` through the single published edge port, with `postgres`/`redis`/`api`/`worker` unreachable from the host.
|
||||||
|
|||||||
33
backend/apps/api/src/version/version.controller.spec.ts
Normal file
33
backend/apps/api/src/version/version.controller.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Test } from '@nestjs/testing';
|
||||||
|
import {
|
||||||
|
APP_ENVIRONMENT,
|
||||||
|
AppEnvironment,
|
||||||
|
} from '../../../../libs/configuration/src';
|
||||||
|
import { VersionController } from './version.controller';
|
||||||
|
|
||||||
|
describe('VersionController', () => {
|
||||||
|
it('returns only safe build metadata', async () => {
|
||||||
|
const environment: AppEnvironment = {
|
||||||
|
databaseUrl: 'postgresql://u:p@postgres:5432/db',
|
||||||
|
redisUrl: 'redis://redis:6379',
|
||||||
|
appVersion: '1.0.0',
|
||||||
|
teamCityBuildNumber: '123',
|
||||||
|
sourceRevision: 'abc123',
|
||||||
|
};
|
||||||
|
|
||||||
|
const moduleRef = await Test.createTestingModule({
|
||||||
|
controllers: [VersionController],
|
||||||
|
providers: [{ provide: APP_ENVIRONMENT, useValue: environment }],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
const response = moduleRef.get(VersionController).version();
|
||||||
|
|
||||||
|
expect(response).toEqual({
|
||||||
|
appVersion: '1.0.0',
|
||||||
|
teamCityBuildNumber: '123',
|
||||||
|
sourceRevision: 'abc123',
|
||||||
|
});
|
||||||
|
expect(response).not.toHaveProperty('databaseUrl');
|
||||||
|
expect(response).not.toHaveProperty('redisUrl');
|
||||||
|
});
|
||||||
|
});
|
||||||
25
backend/apps/api/src/version/version.controller.ts
Normal file
25
backend/apps/api/src/version/version.controller.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Controller, Get, Inject } from '@nestjs/common';
|
||||||
|
import { APP_ENVIRONMENT } from '../../../../libs/configuration/src';
|
||||||
|
import type { AppEnvironment } from '../../../../libs/configuration/src';
|
||||||
|
|
||||||
|
interface SafeVersionInfo {
|
||||||
|
appVersion: string;
|
||||||
|
teamCityBuildNumber: string;
|
||||||
|
sourceRevision: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller('version')
|
||||||
|
export class VersionController {
|
||||||
|
constructor(
|
||||||
|
@Inject(APP_ENVIRONMENT) private readonly environment: AppEnvironment,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
version(): SafeVersionInfo {
|
||||||
|
return {
|
||||||
|
appVersion: this.environment.appVersion,
|
||||||
|
teamCityBuildNumber: this.environment.teamCityBuildNumber,
|
||||||
|
sourceRevision: this.environment.sourceRevision,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
7
backend/apps/api/src/version/version.module.ts
Normal file
7
backend/apps/api/src/version/version.module.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { VersionController } from './version.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [VersionController],
|
||||||
|
})
|
||||||
|
export class VersionModule {}
|
||||||
Reference in New Issue
Block a user