Kraft102's picture
Update backend source
34367da verified
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(),
};
}
}