5da3ec8818
- 백엔드 modules/ai: POST /ai/chat — Anthropic Messages API 프록시(키 서버 env 한정, 30s 타임아웃, throttle 20/분, 미설정 503/외부오류 502) - ChatDto 검증(role enum, content 8000자, 메시지 50개 상한), 모델 CLAUDE_MODEL(기본 claude-sonnet-4-6) - 프론트 AgentChatPanel: 우측 슬라이드 채팅(말풍선/타이핑/에러/새 대화), plain text 렌더로 XSS 안전 - AppShell 헤더에 AI 버튼 상시 노출 — 모든 화면에서 열림 - 대화 이력은 stores/ai.store(싱글톤)에 보관 → 화면 이동에도 유지, '새 대화'로만 초기화 - TaskDetailPage 의 기존 목업 에이전트 패널 전부 제거(보안 L1 해소) - env.example 에 CLAUDE_MODEL 추가 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
111 lines
4.4 KiB
TypeScript
111 lines
4.4 KiB
TypeScript
import { Logger, Module } from '@nestjs/common';
|
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
import { ConfigModule } from '@nestjs/config';
|
|
import { ScheduleModule } from '@nestjs/schedule';
|
|
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
|
|
import { CacheModule } from '@nestjs/cache-manager';
|
|
import { createKeyv } from '@keyv/redis';
|
|
import type Keyv from 'keyv';
|
|
import { APP_GUARD } from '@nestjs/core';
|
|
|
|
import { AppController } from './app.controller';
|
|
import { AppService } from './app.service';
|
|
import { HealthModule } from './modules/health/health.module';
|
|
import { UserModule } from './modules/user/user.module';
|
|
import { AuthModule } from './modules/auth/auth.module';
|
|
import { RepoModule } from './modules/repo/repo.module';
|
|
import { TaskModule } from './modules/task/task.module';
|
|
import { ActivityModule } from './modules/activity/activity.module';
|
|
import { NotificationModule } from './modules/notification/notification.module';
|
|
import { AiModule } from './modules/ai/ai.module';
|
|
|
|
@Module({
|
|
imports: [
|
|
ScheduleModule.forRoot(),
|
|
ConfigModule.forRoot({
|
|
isGlobal: true,
|
|
envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
|
|
}),
|
|
// 전역 캐시 매니저 — REDIS_* 설정 시 Redis(keyv) 스토어, 없으면 인메모리 폴백.
|
|
// docker-compose 가 REDIS_HOST/PORT/PASSWORD 를 주입한다(단독 실행/테스트는 미설정 → 인메모리).
|
|
CacheModule.registerAsync({
|
|
isGlobal: true,
|
|
useFactory: (): { ttl: number; stores?: Keyv[] } => {
|
|
const logger = new Logger('CacheModule');
|
|
const options: { ttl: number; stores?: Keyv[] } = { ttl: 60_000 };
|
|
const host = process.env.REDIS_HOST;
|
|
if (!host) {
|
|
// Redis 미설정 — 인메모리 폴백(LRU). 로컬/테스트에서 Redis 없이도 동작.
|
|
// 주의: 다중 인스턴스를 인메모리 모드로 띄우면 인스턴스별 캐시·무효화가 분리됨(단일 인스턴스 전용)
|
|
logger.log('캐시: 인메모리 모드(단일 인스턴스) — REDIS_HOST 미설정');
|
|
return options;
|
|
}
|
|
const port = process.env.REDIS_PORT || '6379';
|
|
const password = process.env.REDIS_PASSWORD;
|
|
const auth = password ? `:${encodeURIComponent(password)}@` : '';
|
|
const keyv = createKeyv(`redis://${auth}${host}:${port}`);
|
|
// Redis 연결 오류가 unhandled 로 프로세스를 죽이지 않도록 흡수(캐시 장애는 DB 조회로 폴백)
|
|
keyv.on('error', (err: unknown) => {
|
|
logger.warn(
|
|
`Redis 캐시 오류(무시, DB 폴백): ${err instanceof Error ? err.message : String(err)}`,
|
|
);
|
|
});
|
|
options.stores = [keyv];
|
|
logger.log('캐시: Redis 모드(다중 인스턴스 무효화 정합)');
|
|
return options;
|
|
},
|
|
}),
|
|
// 전역 Rate Limiter — main.ts 의 express-rate-limit 와 별개로 라우트 단위 제어 가능
|
|
ThrottlerModule.forRoot([
|
|
{
|
|
ttl: 60_000,
|
|
limit: 100,
|
|
},
|
|
]),
|
|
TypeOrmModule.forRootAsync({
|
|
useFactory: () => {
|
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
return {
|
|
type: 'postgres',
|
|
host: process.env.DB_HOST || 'db',
|
|
port: parseInt(process.env.DB_PORT || '5432', 10),
|
|
username:
|
|
process.env.DB_USER || process.env.DB_USERNAME || 'postgres',
|
|
password: process.env.DB_PASSWORD || 'password',
|
|
database:
|
|
process.env.DB_NAME || process.env.DB_DATABASE || 'project_db',
|
|
autoLoadEntities: true,
|
|
// 운영 환경에서는 절대 synchronize 사용 금지 (스키마 자동 변경 → 데이터 손실 위험)
|
|
synchronize: !isProduction,
|
|
migrationsRun: isProduction,
|
|
logging: !isProduction,
|
|
extra: {
|
|
max: 100,
|
|
connectionTimeoutMillis: 5000,
|
|
idleTimeoutMillis: 30000,
|
|
},
|
|
entities: [__dirname + '/**/*.entity{.ts,.js}'],
|
|
migrations: [__dirname + '/migrations/*{.ts,.js}'],
|
|
};
|
|
},
|
|
}),
|
|
HealthModule,
|
|
UserModule,
|
|
AuthModule,
|
|
RepoModule,
|
|
TaskModule,
|
|
ActivityModule,
|
|
NotificationModule,
|
|
AiModule,
|
|
],
|
|
controllers: [AppController],
|
|
providers: [
|
|
AppService,
|
|
{
|
|
provide: APP_GUARD,
|
|
useClass: ThrottlerGuard,
|
|
},
|
|
],
|
|
})
|
|
export class AppModule {}
|