feat: attachments on tickets and comments

Files are uploaded over REST, stored on disk (a volume in Docker) and
recorded on the ticket as events; the bytes are written first, so no
attachment ever points at a missing file. Only images, video, audio, PDF
and plain text are shown inline, everything with nosniff and a sandbox CSP.
The web shows thumbnails, uploads by drag and drop or by pasting a
screenshot, and attaches files to new comments.

Also adds `nx run api:contracts`, which writes schema.gql and
openapi.json without starting the API or any database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 08:35:33 +02:00
parent 80d9bee6e0
commit 0b65c1c912
72 changed files with 6766 additions and 4297 deletions

View File

@@ -3,6 +3,7 @@ import { CqrsModule } from '@nestjs/cqrs';
import { MongooseModule } from '@nestjs/mongoose';
import { TypeOrmModule } from '@nestjs/typeorm';
import {
AttachmentStorage,
ChangeFeed,
FindActivityRepository,
FindCommentsRepository,
@@ -51,6 +52,7 @@ import {
import { PostgresReleaseIssuesRepository } from './postgres/release-issues.repository';
import { PostgresTrackerStreamsRepository } from './postgres/tracker-streams.repository';
import { RedisChangeFeed } from './realtime/redis-change-feed';
import { FileSystemAttachmentStorage } from './storage/file-system-attachment.storage';
const PORTS = [
// Write side lookups → PostgreSQL (aggregates themselves live in the event store)
@@ -96,6 +98,8 @@ const PORTS = [
provide: UpsertMaterializedTimelineRepository,
useClass: MongoUpsertMaterializedTimelineRepository,
},
// Attached files → disk (ATTACHMENTS_DIR)
{ provide: AttachmentStorage, useClass: FileSystemAttachmentStorage },
// Realtime → Redis pub/sub
{ provide: ChangeFeed, useClass: RedisChangeFeed },
// Identity context (anti-corruption layer)