life-manager/codebase/features/diary/backend/diary.controller.ts
2026-03-17 17:51:03 -07:00

133 lines
4.4 KiB
TypeScript

import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
Patch,
Post,
Query,
} from '@nestjs/common';
import { ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
import { DiaryService, DiaryEntryResponse } from './diary.service';
import { DiaryExplorationService } from './diary-exploration.service';
import { FeelingCategoryService } from './feeling-category.service';
import { CreateDiaryEntryDto } from './dto/create-diary-entry.dto';
import { UpdateDiaryEntryDto } from './dto/update-diary-entry.dto';
import { QueryDiaryDto } from './dto/query-diary.dto';
import { PaginatedResponse } from '@common/pagination.dto';
import { FeelingCategory } from './entities/feeling-category.entity';
import type { FeelingTrendPoint } from '@life-platform/shared';
@ApiTags('Diary')
@Controller('diary')
export class DiaryController {
constructor(
private readonly diaryService: DiaryService,
private readonly explorationService: DiaryExplorationService,
private readonly feelingCategoryService: FeelingCategoryService,
) {}
// --- Static routes BEFORE :id param routes ---
@Get('trends')
@ApiOperation({ summary: 'Get feeling intensity trends over time' })
@ApiQuery({ name: 'days', required: false, description: 'Number of days to look back (default 30)' })
@ApiQuery({ name: 'category', required: false, description: 'Filter to specific feeling category' })
getTrends(
@Query('days') days?: string,
@Query('category') category?: string,
): Promise<FeelingTrendPoint[]> {
return this.diaryService.getFeelingTrends(
days ? parseInt(days, 10) : 30,
category,
);
}
@Get('correlations')
@ApiOperation({ summary: 'Get feeling correlations with other factors' })
@ApiQuery({ name: 'category', required: true, description: 'Feeling category to correlate' })
getCorrelations(@Query('category') category: string) {
return this.diaryService.getCorrelations(category);
}
@Get('feeling-categories')
@ApiOperation({ summary: 'List all feeling categories' })
getFeelingCategories(): Promise<FeelingCategory[]> {
return this.feelingCategoryService.findAll();
}
@Post('feeling-categories')
@ApiOperation({ summary: 'Create a custom feeling category' })
createFeelingCategory(
@Body() data: { slug: string; name: string; description: string; examples: string[] },
): Promise<FeelingCategory> {
return this.feelingCategoryService.create(data);
}
// --- CRUD routes ---
@Post()
@ApiOperation({ summary: 'Create diary entry (content encrypted at rest)' })
async createEntry(@Body() dto: CreateDiaryEntryDto): Promise<DiaryEntryResponse> {
const entry = await this.diaryService.create(dto);
if (dto.triggerExploration && entry.content) {
const needsExploration = await this.explorationService.detectNeedsExploration(entry.content);
if (needsExploration) {
const questions = await this.explorationService.generateQuestions(entry.content);
return {
...entry,
metadata: {
...(entry.metadata ?? {}),
pendingExploration: true,
explorationQuestions: questions,
},
};
}
}
return entry;
}
@Get()
@ApiOperation({ summary: 'List diary entries with filters' })
findAll(@Query() query: QueryDiaryDto): Promise<PaginatedResponse<DiaryEntryResponse>> {
return this.diaryService.findAll(query);
}
@Get(':id')
@ApiOperation({ summary: 'Get diary entry by ID (decrypted content)' })
findOne(@Param('id') id: string): Promise<DiaryEntryResponse> {
return this.diaryService.findOne(id);
}
@Post(':id/explore')
@ApiOperation({ summary: 'Trigger emotional exploration on an existing entry' })
async explore(@Param('id') id: string): Promise<{ questions: string[] }> {
const entry = await this.diaryService.findOne(id);
if (!entry.content) {
return { questions: [] };
}
const questions = await this.explorationService.generateQuestions(entry.content);
return { questions };
}
@Patch(':id')
@ApiOperation({ summary: 'Update diary entry' })
update(
@Param('id') id: string,
@Body() dto: UpdateDiaryEntryDto,
): Promise<DiaryEntryResponse> {
return this.diaryService.update(id, dto);
}
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({ summary: 'Soft delete diary entry' })
remove(@Param('id') id: string): Promise<void> {
return this.diaryService.softRemove(id);
}
}