life-projects/productivity/scheduling/backend/ical.controller.ts
autocommit 3aea13e429 feat(ical-specific): Add iCal event validation and synchronization support for scheduling backend
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 05:01:02 -07:00

80 lines
2.5 KiB
TypeScript

import {
Body,
Controller,
Get,
HttpCode,
HttpStatus,
Param,
Post,
Query,
Res,
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import type { Response } from 'express';
import { IcalExportService } from './ical-export.service';
import { IcalImportService } from './ical-import.service';
import type { IcalImportBatch } from './entities/ical-import-batch.entity';
@ApiTags('Scheduling - iCal')
@Controller('scheduling/ical')
export class IcalController {
constructor(
private readonly exportService: IcalExportService,
private readonly importService: IcalImportService,
) {}
@Get('feeds')
@ApiOperation({ summary: 'List available iCal feed URLs' })
async listFeeds(@Query('baseUrl') baseUrl?: string): Promise<Array<{ name: string; url: string; description: string }>> {
const base = baseUrl ?? `http://lm.apricot.lan:3700`;
return this.exportService.listFeeds(base);
}
@Get('feed.ics')
@ApiOperation({ summary: 'Get iCal feed (subscribe in calendar apps)' })
async getFeed(
@Res() res: Response,
@Query('domainId') domainId?: string,
@Query('projectId') projectId?: string,
): Promise<void> {
const ical = await this.exportService.generateFeed({ domainId, projectId });
res
.status(200)
.set('Content-Type', 'text/calendar; charset=utf-8')
.set('Content-Disposition', 'attachment; filename="feed.ics"')
.send(ical);
}
@Post('import')
@ApiOperation({ summary: 'Import .ics content into TimeBlocks' })
async importIcal(
@Body() body: { content: string; sourceName: string; sourceUrl?: string; domainId?: string },
): Promise<IcalImportBatch> {
return this.importService.importIcal({
icalContent: body.content,
sourceName: body.sourceName,
sourceUrl: body.sourceUrl,
domainId: body.domainId,
});
}
@Get('imports')
@ApiOperation({ summary: 'List import batches' })
async listImports(): Promise<IcalImportBatch[]> {
return this.importService.listBatches();
}
@Get('imports/:id')
@ApiOperation({ summary: 'Get import batch detail' })
async getImport(@Param('id') id: string): Promise<IcalImportBatch> {
return this.importService.getBatch(id);
}
@Post('imports/:id/restore')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Undo an import (delete imported blocks)' })
async restoreImport(@Param('id') id: string): Promise<IcalImportBatch> {
return this.importService.restoreBatch(id);
}
}