Spaces:
Paused
Paused
| import { prisma } from '../../database/prisma.js'; | |
| export interface Note { | |
| id?: number; | |
| userId: string; | |
| orgId: string; | |
| source: string; | |
| title: string; | |
| body: string; | |
| tags: string; | |
| owner: string; | |
| compliance: 'clean' | 'review' | 'restricted'; | |
| retention: '30d' | '90d' | '1y' | 'archive'; | |
| riskScore: number; | |
| attachments: number; | |
| createdAt?: string; | |
| updatedAt?: string; | |
| } | |
| export class NotesRepository { | |
| async createNote(note: Note): Promise<number> { | |
| const created = await prisma.note.create({ | |
| data: { | |
| userId: note.userId, | |
| orgId: note.orgId, | |
| source: note.source, | |
| title: note.title, | |
| body: note.body, | |
| tags: note.tags || '', | |
| owner: note.owner, | |
| compliance: note.compliance || 'clean', | |
| retention: note.retention || '90d', | |
| riskScore: note.riskScore || 0, | |
| attachments: note.attachments || 0, | |
| }, | |
| }); | |
| return created.id; | |
| } | |
| async getNotes(userId: string, orgId: string, filters?: { | |
| source?: string; | |
| compliance?: string; | |
| retention?: string; | |
| query?: string; | |
| limit?: number; | |
| }): Promise<Note[]> { | |
| const where: any = { | |
| userId, | |
| orgId, | |
| }; | |
| if (filters?.source) { | |
| where.source = filters.source; | |
| } | |
| if (filters?.compliance) { | |
| where.compliance = filters.compliance; | |
| } | |
| if (filters?.retention) { | |
| where.retention = filters.retention; | |
| } | |
| if (filters?.query) { | |
| where.OR = [ | |
| { title: { contains: filters.query, mode: 'insensitive' } }, | |
| { body: { contains: filters.query, mode: 'insensitive' } }, | |
| { tags: { contains: filters.query, mode: 'insensitive' } }, | |
| ]; | |
| } | |
| const notes = await prisma.note.findMany({ | |
| where, | |
| orderBy: { updatedAt: 'desc' }, | |
| take: filters?.limit, | |
| }); | |
| return notes.map(this.mapToNote); | |
| } | |
| async getNoteById(id: number, userId: string, orgId: string): Promise<Note | null> { | |
| const note = await prisma.note.findFirst({ | |
| where: { id, userId, orgId }, | |
| }); | |
| return note ? this.mapToNote(note) : null; | |
| } | |
| async updateNote(id: number, userId: string, orgId: string, updates: Partial<Note>): Promise<boolean> { | |
| const data: any = {}; | |
| if (updates.title !== undefined) data.title = updates.title; | |
| if (updates.body !== undefined) data.body = updates.body; | |
| if (updates.tags !== undefined) data.tags = updates.tags; | |
| if (updates.source !== undefined) data.source = updates.source; | |
| if (updates.compliance !== undefined) data.compliance = updates.compliance; | |
| if (updates.retention !== undefined) data.retention = updates.retention; | |
| if (updates.riskScore !== undefined) data.riskScore = updates.riskScore; | |
| if (updates.attachments !== undefined) data.attachments = updates.attachments; | |
| if (Object.keys(data).length === 0) { | |
| return false; | |
| } | |
| await prisma.note.updateMany({ | |
| where: { id, userId, orgId }, | |
| data, | |
| }); | |
| return true; | |
| } | |
| async deleteNote(id: number, userId: string, orgId: string): Promise<boolean> { | |
| await prisma.note.deleteMany({ | |
| where: { id, userId, orgId }, | |
| }); | |
| return true; | |
| } | |
| async getStatistics(userId: string, orgId: string) { | |
| const notes = await this.getNotes(userId, orgId); | |
| const sourceDistribution: Record<string, number> = {}; | |
| let flaggedCount = 0; | |
| let totalRisk = 0; | |
| notes.forEach(note => { | |
| sourceDistribution[note.source] = (sourceDistribution[note.source] || 0) + 1; | |
| if (note.compliance !== 'clean') { | |
| flaggedCount++; | |
| } | |
| totalRisk += note.riskScore; | |
| }); | |
| return { | |
| total: notes.length, | |
| flagged: flaggedCount, | |
| avgRisk: notes.length > 0 ? Math.round(totalRisk / notes.length) : 0, | |
| sourceDistribution, | |
| }; | |
| } | |
| // Seed sample data for demo purposes | |
| async seedSampleData(userId: string, orgId: string) { | |
| const sampleNotes = [ | |
| { | |
| userId, | |
| orgId, | |
| source: 'Microsoft OneNote', | |
| title: 'Q4 Strategy Workshop', | |
| body: 'Collected workshop notes covering AI procurement roadmap, stakeholder interviews and deliverables for DG CONNECT.', | |
| tags: 'strategy,ai,eu', | |
| owner: 'A. Rossi', | |
| compliance: 'clean' as const, | |
| retention: '1y' as const, | |
| riskScore: 12, | |
| attachments: 3, | |
| }, | |
| { | |
| userId, | |
| orgId, | |
| source: 'Google Keep', | |
| title: 'Privacy Task Force recap', | |
| body: 'Summary of Schrems II mitigation controls, DPA template updates and DPIA workflow automation ideas.', | |
| tags: 'privacy,gdpr', | |
| owner: 'K. Jensen', | |
| compliance: 'review' as const, | |
| retention: '90d' as const, | |
| riskScore: 58, | |
| attachments: 1, | |
| }, | |
| { | |
| userId, | |
| orgId, | |
| source: 'Local Files', | |
| title: 'Field research scans', | |
| body: 'Offline PDFs captured from site inspections in Munich and Ghent. Contains photos, transcripts and checklists.', | |
| tags: 'inspection,pdf', | |
| owner: 'M. Novak', | |
| compliance: 'restricted' as const, | |
| retention: '30d' as const, | |
| riskScore: 83, | |
| attachments: 12, | |
| }, | |
| { | |
| userId, | |
| orgId, | |
| source: 'Email', | |
| title: 'Bid coaching thread', | |
| body: 'Email notes exchanged with supplier consortium clarifying KPIs, SOW split and legal guardrails.', | |
| tags: 'bid,procurement', | |
| owner: 'L. Ruíz', | |
| compliance: 'review' as const, | |
| retention: '1y' as const, | |
| riskScore: 46, | |
| attachments: 2, | |
| }, | |
| { | |
| userId, | |
| orgId, | |
| source: 'Evernote', | |
| title: 'Incident Post-Mortem', | |
| body: 'Voice memo transcription summarizing containment timeline, threat intel links and RCA decisions.', | |
| tags: 'security,incident', | |
| owner: 'J. Olofsson', | |
| compliance: 'clean' as const, | |
| retention: '1y' as const, | |
| riskScore: 28, | |
| attachments: 0, | |
| }, | |
| ]; | |
| for (const note of sampleNotes) { | |
| await this.createNote(note); | |
| } | |
| } | |
| private mapToNote(row: any): Note { | |
| return { | |
| id: row.id, | |
| userId: row.userId, | |
| orgId: row.orgId, | |
| source: row.source, | |
| title: row.title, | |
| body: row.body, | |
| tags: row.tags, | |
| owner: row.owner, | |
| compliance: row.compliance as 'clean' | 'review' | 'restricted', | |
| retention: row.retention as '30d' | '90d' | '1y' | 'archive', | |
| riskScore: row.riskScore, | |
| attachments: row.attachments, | |
| createdAt: row.createdAt?.toISOString(), | |
| updatedAt: row.updatedAt?.toISOString(), | |
| }; | |
| } | |
| } | |