INSIGHT-MVP/packages/core-service/src/main.ts
Thomas Reitz b326081c54 feat: implement expert profile with skills, experience, languages, projects, certifications and attachments
Full-stack implementation of the Expert Profile tab with 6 sections:
- Skills (tag/chip UI with add/remove)
- Experience (area, years, optional level)
- Languages (language + proficiency level)
- Project History (modal form with dates, role, tasks, company details)
- Certifications (modal form with title, issuer, website, year)
- Attachments (file upload/download as Base64, max 10MB)

Backend: 15 API endpoints, 8 DTOs, full CRUD service with ownership verification.
Frontend: Reusable Modal component (React Portal), ExpertProfileTab orchestrator, 8 section components.
Database: 6 new tables (expert_profiles, expert_experiences, expert_languages, expert_projects, expert_certifications, expert_attachments).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:23:47 +01:00

86 lines
2.3 KiB
TypeScript

import { NestFactory } from '@nestjs/core';
import { ValidationPipe, Logger } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import cookieParser from 'cookie-parser';
import helmet from 'helmet';
import { json } from 'express';
import { AppModule } from './app.module';
async function bootstrap(): Promise<void> {
const logger = new Logger('Bootstrap');
const app = await NestFactory.create(AppModule, {
logger: ['error', 'warn', 'log', 'debug', 'verbose'],
});
// Security
app.use(helmet());
app.use(cookieParser());
// Body size limit für Base64-Uploads (Avatare, Profilanlagen bis 10MB)
app.use(json({ limit: '12mb' }));
// CORS
const corsOrigins = process.env.CORS_ORIGINS?.split(',') ?? [
'http://172.20.10.59',
];
app.enableCors({
origin: corsOrigins,
credentials: true,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Tenant-ID',
'X-Request-ID',
],
});
// Global Validation Pipe (Sicherheitsregel: whitelist + forbidNonWhitelisted)
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
transformOptions: { enableImplicitConversion: true },
}),
);
// Global Prefix
app.setGlobalPrefix('api/v1', {
exclude: ['health'],
});
// Swagger (nur Development)
if (process.env.NODE_ENV !== 'production') {
const config = new DocumentBuilder()
.setTitle('INSIGHT Platform API')
.setDescription('Multi-Tenant Business Platform API')
.setVersion('0.1.0')
.addBearerAuth(
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
description: 'Access Token (RS256)',
},
'access-token',
)
.addCookieAuth('refresh_token', {
type: 'apiKey',
in: 'cookie',
description: 'HttpOnly Refresh Token',
})
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
logger.log('Swagger UI: /api/docs');
}
const port = process.env.APP_PORT ?? 3000;
await app.listen(port);
logger.log(`Core-Service laeuft auf Port ${port}`);
logger.log(`Umgebung: ${process.env.NODE_ENV ?? 'development'}`);
}
bootstrap();