Files
mono/components/orchestrator/src/main.ts
T
coopops 3f1d816ac0 [E10-4] feat(gateway-orchestrator): install-pipeline через DockerRunner/HealthProbe/OciToken порты
Story 10.4 — pipeline установки subgraph-расширения:
1. (опц.) OCI token у CA-auth → docker pull образа;
2. (опц.) docker compose up сервиса;
3. healthcheck poll по url subgraph'а до 200 OK;
4. upsert в subgraph_registry (Apollo Gateway за ≤10с подхватит);
5. на failure — composeDown rollback + discriminated outcome {applied|failed}.

InstallController стал тонкой обёрткой над InstallOrchestratorService.
Внешние зависимости (docker shell / fetch / CA-auth) изолированы за
портами; тесты (7/7 green) гоняют сценарий через ин-мемори моки без
реального docker daemon'а и сети.

Что ОСТАЁТСЯ для отдельной ветки под этим зонтиком:
- wiring approve→install REST из mono controller (mutation approvePackage);
- desktop notification после успешного install'а;
- E2E на реальном compose + chatcoop subgraph (Story 10.8).
2026-06-04 05:08:39 +00:00

55 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { DataSource } from 'typeorm';
import { AppModule } from './app.module';
import { loadAppConfig } from './config/app-config';
import { SubgraphRegistryEntity } from './gateway/subgraph-registry.entity';
/**
* Перед стартом Apollo Gateway seed'им запись core-subgraph'а в registry,
* иначе IntrospectAndCompose упадёт с "пустой registry".
*
* Bootstrap делается в отдельной DataSource (минуя Nest DI), чтобы быть
* уверенным что seed произошёл ДО запуска GraphQLModule.forRootAsync.
*/
async function seedCoreSubgraph(cfg: ReturnType<typeof loadAppConfig>) {
const ds = new DataSource({
type: 'postgres',
url: cfg.postgresUrl,
entities: [SubgraphRegistryEntity],
synchronize: true,
});
await ds.initialize();
const repo = ds.getRepository(SubgraphRegistryEntity);
await repo.upsert(
{
packageId: 'core@coopback',
version: 'monolith',
url: cfg.coreSubgraphUrl,
active: true,
healthStatus: 'unknown',
},
['packageId'],
);
await ds.destroy();
}
async function bootstrap() {
const cfg = loadAppConfig();
await seedCoreSubgraph(cfg);
const app = await NestFactory.create(AppModule);
await app.listen(cfg.port);
// eslint-disable-next-line no-console
console.log(
`[orchestrator] up at :${cfg.port} ` +
`(coopname=${cfg.coopname}, core=${cfg.coreSubgraphUrl})`,
);
}
bootstrap().catch((err) => {
// eslint-disable-next-line no-console
console.error('[orchestrator] failed to bootstrap:', err);
process.exit(1);
});