mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 06:56:35 +00:00
cc79f561e5
JwtAuthGuard, OptionalJwtAuthGuard, CookieAuthGuard, AdminGuard, the global and MFA guards, @CurrentUser, @Public, JWT verification and the ephemeral token service lived in the auth domain, so every controller that checked a session depended on the whole account module and its imports. They import no domain themselves; under src/nest/auth-core they are shared kernel like common/, every importer points there directly, and AtlasModule and NotificationsModule stop importing AuthModule, which they only did for the demo gate. lint:boundaries counts auth-core as kernel: 57 fewer cyclic domain edges, the largest cycle shrinks from 46 to 40 domains.
101 lines
3.5 KiB
TypeScript
101 lines
3.5 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
Headers,
|
|
HttpException,
|
|
Param,
|
|
Post,
|
|
Put,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import type { User } from '../../types';
|
|
import { DayNotesService } from './day-notes.service';
|
|
import { DayNoteCreateDto, DayNoteUpdateDto } from './day-notes.dto';
|
|
import { JwtAuthGuard } from '../auth-core/jwt-auth.guard';
|
|
import { CurrentUser } from '../auth-core/current-user.decorator';
|
|
import { RequirePermission, TripAccessGuard } from '../permissions/trip-access.guard';
|
|
|
|
/**
|
|
* /api/trips/:tripId/days/:dayId/notes — free-text annotations on a day.
|
|
*
|
|
* Bodies validate against the @trek/shared day-note contracts via the global
|
|
* ZodValidationPipe (which, like the legacy string-length guard, 400s before
|
|
* the trip-access check), then trip access (404), then the 'day_edit'
|
|
* permission (403); create 201 / rest 200; the bespoke "Day not found" /
|
|
* "Note not found" / "Text required" bodies; WebSocket broadcasts with the
|
|
* forwarded X-Socket-Id.
|
|
*/
|
|
@Controller('api/trips/:tripId/days/:dayId/notes')
|
|
// TripAccessGuard resolves :tripId and 404s a trip the user cannot reach; mutations
|
|
// add @RequirePermission('day_edit'), the same action string the service's canEdit
|
|
// passes, so the HTTP and MCP paths cannot demand different rights.
|
|
@UseGuards(JwtAuthGuard, TripAccessGuard)
|
|
export class DayNotesController {
|
|
constructor(private readonly notes: DayNotesService) {}
|
|
|
|
|
|
|
|
@Get()
|
|
async list(@CurrentUser() user: User, @Param('tripId') tripId: string, @Param('dayId') dayId: string) {
|
|
return { notes: await this.notes.list(dayId, tripId) };
|
|
}
|
|
|
|
@RequirePermission('day_edit')
|
|
@Post()
|
|
async create(
|
|
@CurrentUser() user: User,
|
|
@Param('tripId') tripId: string,
|
|
@Param('dayId') dayId: string,
|
|
@Body() body: DayNoteCreateDto,
|
|
@Headers('x-socket-id') socketId?: string,
|
|
) {
|
|
if (!(await this.notes.dayExists(dayId, tripId))) {
|
|
throw new HttpException({ error: 'Day not found' }, 404);
|
|
}
|
|
if (!body.text?.trim()) {
|
|
throw new HttpException({ error: 'Text required' }, 400);
|
|
}
|
|
const note = await this.notes.create(dayId, tripId, body.text, body.time, body.icon, body.sort_order, body.color);
|
|
this.notes.broadcast(tripId, 'dayNote:created', { dayId: Number(dayId), note }, socketId);
|
|
return { note };
|
|
}
|
|
|
|
@RequirePermission('day_edit')
|
|
@Put(':id')
|
|
async update(
|
|
@CurrentUser() user: User,
|
|
@Param('tripId') tripId: string,
|
|
@Param('dayId') dayId: string,
|
|
@Param('id') id: string,
|
|
@Body() body: DayNoteUpdateDto,
|
|
@Headers('x-socket-id') socketId?: string,
|
|
) {
|
|
const current = await this.notes.getNote(id, dayId, tripId);
|
|
if (!current) {
|
|
throw new HttpException({ error: 'Note not found' }, 404);
|
|
}
|
|
const note = await this.notes.update(id, current as never, { text: body.text, time: body.time, icon: body.icon, sort_order: body.sort_order, color: body.color });
|
|
this.notes.broadcast(tripId, 'dayNote:updated', { dayId: Number(dayId), note }, socketId);
|
|
return { note };
|
|
}
|
|
|
|
@RequirePermission('day_edit')
|
|
@Delete(':id')
|
|
async remove(
|
|
@CurrentUser() user: User,
|
|
@Param('tripId') tripId: string,
|
|
@Param('dayId') dayId: string,
|
|
@Param('id') id: string,
|
|
@Headers('x-socket-id') socketId?: string,
|
|
) {
|
|
if (!(await this.notes.getNote(id, dayId, tripId))) {
|
|
throw new HttpException({ error: 'Note not found' }, 404);
|
|
}
|
|
await this.notes.remove(id);
|
|
this.notes.broadcast(tripId, 'dayNote:deleted', { noteId: Number(id), dayId: Number(dayId) }, socketId);
|
|
return { success: true };
|
|
}
|
|
}
|