mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 07:36:39 +02:00
Docker Infrastructure:
- docker-compose.yml with Traefik 3, PostgreSQL 16, PgBouncer, Redis 7, step-ca
- docker-compose.observability.yml with Prometheus, Grafana, Loki, Tempo, Promtail
- Traefik dynamic config (TLS, security headers, CORS, compression)
- PostgreSQL init script (uuid-ossp, pgcrypto, pg_trgm extensions)
- Grafana auto-provisioned datasources (Prometheus, Loki, Tempo)
NestJS Core-Service:
- Auth module: Login (email/password), TOTP 2FA, JWT RS256, token refresh/revocation
- Users module: CRUD, bcrypt cost 12, pagination, role-based access
- Tenants module: CRUD, member management, slug validation
- Prisma schemas: core (Users, AuthProviders, Tenants, Modules, AuditLog)
tenant (Contacts, Activities - CRM reference for Sprint 2)
- TenantPrismaService: Dynamic per-tenant DB connections with caching
- RedisService: Token blocklist, refresh token families, generic cache
- Global JwtAuthGuard with @Public() decorator, RolesGuard, GlobalExceptionFilter
- Health endpoint with DB + Redis status checks
- Swagger API documentation (dev only)
- Multi-stage Dockerfile (dev + production)
React Frontend:
- Vite 6 + React 18 + TypeScript strict
- AuthContext with silent refresh (access token in memory, NOT localStorage)
- Login page with TOTP 2FA support
- App shell with sidebar navigation
- Admin pages: Users + Tenants management tables
- API client with automatic token refresh interceptor
- Multi-stage Dockerfile (dev + nginx production)
CI/CD Pipelines:
- ci.yml: Lint, type-check, test, build on all branches
- deploy.yml: Docker build, push to Forgejo registry, SSH deploy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
72 lines
1.7 KiB
TypeScript
72 lines
1.7 KiB
TypeScript
import { Controller, Get } from '@nestjs/common';
|
|
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
|
import { Public } from '../common/decorators/public.decorator';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { RedisService } from '../redis/redis.service';
|
|
|
|
interface HealthResponse {
|
|
status: 'ok' | 'error';
|
|
timestamp: string;
|
|
version: string;
|
|
services: {
|
|
database: 'up' | 'down';
|
|
redis: 'up' | 'down';
|
|
};
|
|
}
|
|
|
|
@ApiTags('Health')
|
|
@Controller('health')
|
|
export class HealthController {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly redis: RedisService,
|
|
) {}
|
|
|
|
@Get()
|
|
@Public()
|
|
@ApiOperation({ summary: 'Health-Check fuer alle Services' })
|
|
async check(): Promise<HealthResponse> {
|
|
const [dbStatus, redisStatus] = await Promise.allSettled([
|
|
this.checkDatabase(),
|
|
this.checkRedis(),
|
|
]);
|
|
|
|
const allUp =
|
|
dbStatus.status === 'fulfilled' &&
|
|
dbStatus.value &&
|
|
redisStatus.status === 'fulfilled' &&
|
|
redisStatus.value;
|
|
|
|
return {
|
|
status: allUp ? 'ok' : 'error',
|
|
timestamp: new Date().toISOString(),
|
|
version: '0.1.0',
|
|
services: {
|
|
database:
|
|
dbStatus.status === 'fulfilled' && dbStatus.value ? 'up' : 'down',
|
|
redis:
|
|
redisStatus.status === 'fulfilled' && redisStatus.value
|
|
? 'up'
|
|
: 'down',
|
|
},
|
|
};
|
|
}
|
|
|
|
private async checkDatabase(): Promise<boolean> {
|
|
try {
|
|
await this.prisma.$queryRaw`SELECT 1`;
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private async checkRedis(): Promise<boolean> {
|
|
try {
|
|
const pong = await this.redis.ping();
|
|
return pong === 'PONG';
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|