Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/server-nestjs/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ KEYCLOAK_PROTOCOL=http
KEYCLOAK_CLIENT_ID=dso-console-backend
# Secret du client Keycloak backend (confidentiel)
KEYCLOAK_CLIENT_SECRET=client-secret-backend
# Identifiant de l'administrateur Keycloak
KEYCLOAK_ADMIN=admin
# Mot de passe de l'administrateur Keycloak
KEYCLOAK_ADMIN_PASSWORD=admin
# Identifiant administrateur Keycloak (utilisé pour l'API admin)
KEYCLOAK_ADMIN=admin
# Mot de passe administrateur Keycloak (confidentiel)
KEYCLOAK_ADMIN_PASSWORD=admin
# URL de redirection après authentification Keycloak
KEYCLOAK_REDIRECT_URI=http://localhost:8080
# Port d'écoute du serveur backend
Expand Down
4 changes: 4 additions & 0 deletions apps/server-nestjs/.env.docker-example
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ KEYCLOAK_PROTOCOL=http
KEYCLOAK_CLIENT_ID=dso-console-backend
# Secret du client Keycloak backend (confidentiel)
KEYCLOAK_CLIENT_SECRET=client-secret-backend
# Identifiant de l'administrateur Keycloak
KEYCLOAK_ADMIN=admin
# Mot de passe de l'administrateur Keycloak
KEYCLOAK_ADMIN_PASSWORD=admin
# URL de redirection après authentification Keycloak
KEYCLOAK_REDIRECT_URI=http://localhost:8080
# Port d'écoute du serveur dans le réseau Docker
Expand Down
51 changes: 31 additions & 20 deletions apps/server-nestjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,22 @@
"db:migrate": "prisma migrate dev --name dso",
"db:reset": "prisma migrate reset",
"format": "eslint ./ --fix",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"prestart": "prisma generate",
"start": "nest start",
"start:debug": "nest start --debug --watch",
"start:dev": "nest start --watch",
"start:prod": "node dist/main"
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"pretest": "prisma generate",
"test": "vitest run",
"test:watch": "vitest",
"pretest:cov": "prisma generate",
"test:cov": "vitest run --coverage",
"test:debug": "vitest --inspect"
},
"dependencies": {
"@casl/ability": "^6.7.1",
"@casl/prisma": "^1.5.0",
"@cpn-console/argocd-plugin": "workspace:^",
"@cpn-console/gitlab-plugin": "workspace:^",
"@cpn-console/harbor-plugin": "workspace:^",
Expand All @@ -37,12 +46,25 @@
"@fastify/swagger": "^8.15.0",
"@fastify/swagger-ui": "^4.2.0",
"@gitbeaker/core": "^40.6.0",
"@gitbeaker/requester-utils": "^40.6.0",
"@gitbeaker/rest": "^40.6.0",
"@keycloak/keycloak-admin-client": "^24.0.0",
"@kubernetes-models/argo-cd": "^2.7.2",
"@nestjs/common": "^11.1.16",
"@nestjs/config": "^4.0.3",
"@nestjs/core": "^11.1.16",
"@nestjs/event-emitter": "^3.0.1",
"@nestjs/platform-express": "^11.1.16",
"@nestjs/schedule": "^5.0.1",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/auto-instrumentations-node": "^0.70.1",
"@opentelemetry/exporter-metrics-otlp-proto": "^0.213.0",
"@opentelemetry/exporter-trace-otlp-proto": "^0.213.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.58.0",
"@opentelemetry/instrumentation-pino": "^0.59.0",
"@opentelemetry/sdk-metrics": "^2.5.1",
"@opentelemetry/sdk-node": "^0.212.0",
"@opentelemetry/sdk-trace-node": "^2.5.1",
"@prisma/client": "^6.19.2",
"@ts-rest/core": "^3.52.1",
"@ts-rest/fastify": "^3.52.1",
Expand All @@ -52,15 +74,19 @@
"dotenv": "^16.6.1",
"fastify": "^4.29.1",
"fastify-keycloak-adapter": "2.3.2",
"js-yaml": "^4.1.1",
"json-2-csv": "^5.5.10",
"keycloak-connect": "^25.0.0",
"mustache": "^4.2.0",
"nest-keycloak-connect": "^1.10.1",
"nestjs-pino": "^4.6.0",
"pino-http": "^11.0.0",
"prisma": "^6.19.2",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.2",
"undici": "^7.22.0",
"vitest-mock-extended": "^2.0.2"
"vitest-mock-extended": "^2.0.2",
"zod": "^3.25.76"
},
"devDependencies": {
"@cpn-console/eslint-config": "workspace:^",
Expand All @@ -79,6 +105,8 @@
"eslint": "^9.39.4",
"fastify-plugin": "^5.1.0",
"globals": "^16.5.0",
"jest": "^30.3.0",
"msw": "^2.12.10",
"nodemon": "^3.1.14",
"pino-pretty": "^13.1.3",
"rimraf": "^6.1.3",
Expand All @@ -95,23 +123,6 @@
"vite-node": "^2.1.9",
"vitest": "^2.1.9"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
},
"publishConfig": {
"tag": "latest"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export class ConfigurationService {
keycloakRealm = process.env.KEYCLOAK_REALM
keycloakClientId = process.env.KEYCLOAK_CLIENT_ID
keycloakClientSecret = process.env.KEYCLOAK_CLIENT_SECRET
keycloakAdmin = process.env.KEYCLOAK_ADMIN
keycloakAdminPassword = process.env.KEYCLOAK_ADMIN_PASSWORD
keycloakRedirectUri = process.env.KEYCLOAK_REDIRECT_URI

adminsUserId = process.env.ADMIN_KC_USER_ID
? process.env.ADMIN_KC_USER_ID.split(',')
: []
Expand All @@ -33,10 +36,58 @@ export class ConfigurationService {
= process.env.CONTACT_EMAIL
?? 'cloudpinative-relations@interieur.gouv.fr'

// argocd
argoNamespace = process.env.ARGO_NAMESPACE ?? 'argocd'
argocdUrl = process.env.ARGOCD_URL
argocdExtraRepositories = process.env.ARGOCD_EXTRA_REPOSITORIES

// dso
dsoEnvChartVersion = process.env.DSO_ENV_CHART_VERSION ?? 'dso-env-1.6.0'
dsoNsChartVersion = process.env.DSO_NS_CHART_VERSION ?? 'dso-ns-1.1.5'

// plugins
mockPlugins = process.env.MOCK_PLUGINS === 'true'
projectRootDir = process.env.PROJECTS_ROOT_DIR
projectRootPath = process.env.PROJECTS_ROOT_DIR
pluginsDir = process.env.PLUGINS_DIR ?? '/plugins'

// gitlab
gitlabToken = process.env.GITLAB_TOKEN
gitlabUrl = process.env.GITLAB_URL
gitlabInternalUrl = process.env.GITLAB_INTERNAL_URL
? process.env.GITLAB_INTERNAL_URL
: process.env.GITLAB_URL

gitlabMirrorTokenExpirationDays = Number(process.env.GITLAB_MIRROR_TOKEN_EXPIRATION_DAYS) ?? 180
gitlabMirrorTokenRotationThresholdDays = Number(process.env.GITLAB_MIRROR_TOKEN_ROTATION_THRESHOLD_DAYS) ?? 90

// vault
vaultToken = process.env.VAULT_TOKEN
vaultUrl = process.env.VAULT_URL
vaultInternalUrl = process.env.VAULT_INTERNAL_URL
? process.env.VAULT_INTERNAL_URL
: process.env.VAULT_URL

vaultKvName = process.env.VAULT_KV_NAME ?? 'forge-dso'

// registry (harbor)
harborUrl = process.env.HARBOR_URL
harborInternalUrl = process.env.HARBOR_INTERNAL_URL ?? process.env.HARBOR_URL
harborAdmin = process.env.HARBOR_ADMIN
harborAdminPassword = process.env.HARBOR_ADMIN_PASSWORD
harborRuleTemplate = process.env.HARBOR_RULE_TEMPLATE
harborRuleCount = process.env.HARBOR_RULE_COUNT
harborRetentionCron = process.env.HARBOR_RETENTION_CRON

// nexus
nexusUrl = process.env.NEXUS_URL
nexusInternalUrl = process.env.NEXUS_INTERNAL_URL ?? process.env.NEXUS_URL
nexusAdmin = process.env.NEXUS_ADMIN
nexusAdminPassword = process.env.NEXUS_ADMIN_PASSWORD
nexusSecretExposedUrl
= process.env.NEXUS__SECRET_EXPOSE_INTERNAL_URL === 'true'
? (process.env.NEXUS_INTERNAL_URL ?? process.env.NEXUS_URL)
: process.env.NEXUS_URL

NODE_ENV
= process.env.NODE_ENV === 'test'
? 'test'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { OnModuleInit, OnModuleDestroy } from '@nestjs/common'
import { Injectable } from '@nestjs/common'
import { PrismaClient } from '@prisma/client'

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect()
}

async onModuleDestroy() {
await this.$disconnect()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Module } from '@nestjs/common'

import { ConfigurationModule } from './configuration/configuration.module'
import { DatabaseService } from './database/database.service'
import { PrismaService } from './database/prisma.service'
import { HttpClientService } from './http-client/http-client.service'
import { LoggerModule } from './logger/logger.module'
import { ServerService } from './server/server.service'

@Module({
providers: [DatabaseService, HttpClientService, ServerService],
providers: [DatabaseService, PrismaService, HttpClientService, ServerService],
imports: [LoggerModule, ConfigurationModule],
exports: [DatabaseService, HttpClientService, ServerService],
exports: [DatabaseService, PrismaService, HttpClientService, ServerService],
})
export class InfrastructureModule {}
30 changes: 30 additions & 0 deletions apps/server-nestjs/src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { NodeSDK } from '@opentelemetry/sdk-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto'

function createSdk() {
return new NodeSDK({
traceExporter: new OTLPTraceExporter({}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter(),
}),
instrumentations: [
getNodeAutoInstrumentations(),
new NestInstrumentation(),
new PinoInstrumentation(),
],
serviceName: 'cloud-pi-native-console',
})
}

export function instrument() {
const sdk = createSdk()
sdk.start()
process.on('SIGTERM', () => {
sdk.shutdown()
})
}
10 changes: 9 additions & 1 deletion apps/server-nestjs/src/main.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { Module } from '@nestjs/common'
import { EventEmitterModule } from '@nestjs/event-emitter'
import { ScheduleModule } from '@nestjs/schedule'

import { CpinModule } from './cpin-module/cpin.module'
import { KeycloakModule } from './modules/keycloak/keycloak.module'

// This module only exists to import other module.
// « One module to rule them all, and in NestJs bind them »
@Module({
imports: [CpinModule],
imports: [
CpinModule,
KeycloakModule,
EventEmitterModule.forRoot(),
ScheduleModule.forRoot(),
],
controllers: [],
providers: [],
})
Expand Down
4 changes: 3 additions & 1 deletion apps/server-nestjs/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NestFactory } from '@nestjs/core'
import { Logger } from 'nestjs-pino'

import { instrument } from './instrumentation'
import { ConfigurationService } from './cpin-module/infrastructure/configuration/configuration.service'
import { MainModule } from './main.module'

Expand All @@ -11,4 +11,6 @@ async function bootstrap() {
const config = app.get(ConfigurationService)
await app.listen(config.port ?? 0)
}

instrument()
bootstrap()
Loading