Files
FlockPal/backend/src/repositories/auditRepository.ts
T
blaisadmin 6ade13a8be
Deploy / deploy-dev (push) Successful in 2m27s
Deploy / deploy-prod (push) Has been skipped
Add flock member notes and audit tabs
2026-05-30 22:30:35 -04:00

134 lines
3.9 KiB
TypeScript

import { db } from '../db/client.js';
import type { AuditLogEntryRow, AuthContext, FlockNoteRow } from '../types.js';
type AuditLogInput = {
workspaceId: number;
auth?: AuthContext;
action: string;
entityType: string;
entityId?: string | null;
entityName?: string | null;
details?: Record<string, unknown>;
};
export const createAuditLogEntry = async ({
workspaceId,
auth,
action,
entityType,
entityId = null,
entityName = null,
details = {},
}: AuditLogInput) => {
const result = await db.query<AuditLogEntryRow>(
`INSERT INTO audit_log_entries (workspace_id, user_id, actor_name, actor_email, action, entity_type, entity_id, entity_name, details)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, workspace_id, user_id, actor_name, actor_email, action, entity_type, entity_id, entity_name, details, created_at`,
[
workspaceId,
auth?.user.id ?? null,
auth?.user.name ?? null,
auth?.user.email ?? null,
action,
entityType,
entityId,
entityName,
JSON.stringify(details),
],
);
return result.rows[0] ?? null;
};
export const listAuditLogEntries = async (workspaceId: number, limit = 100) => {
const result = await db.query<AuditLogEntryRow>(
`SELECT id, workspace_id, user_id, actor_name, actor_email, action, entity_type, entity_id, entity_name, details, created_at
FROM audit_log_entries
WHERE workspace_id = $1
ORDER BY created_at DESC
LIMIT $2`,
[workspaceId, limit],
);
return result.rows;
};
export const listFlockNotes = async (workspaceId: number) => {
const result = await db.query<FlockNoteRow>(
`SELECT flock_notes.id,
flock_notes.workspace_id,
flock_notes.bird_id,
birds.name AS bird_name,
flock_notes.title,
flock_notes.body,
flock_notes.created_by_user_id,
users.name AS created_by_name,
flock_notes.created_at,
flock_notes.updated_at
FROM flock_notes
LEFT JOIN birds ON birds.id = flock_notes.bird_id
LEFT JOIN users ON users.id = flock_notes.created_by_user_id
WHERE flock_notes.workspace_id = $1
ORDER BY flock_notes.updated_at DESC`,
[workspaceId],
);
return result.rows;
};
export const createFlockNote = async ({
workspaceId,
birdId,
body,
createdByUserId,
}: {
workspaceId: number;
birdId: string | null;
body: string;
createdByUserId: string | null;
}) => {
const title = body.split(/\s+/).join(' ').slice(0, 160) || 'Note';
const result = await db.query<FlockNoteRow>(
`WITH inserted_note AS (
INSERT INTO flock_notes (workspace_id, bird_id, title, body, created_by_user_id)
SELECT $1, $2, $3, $4, $5
WHERE $2::uuid IS NULL
OR EXISTS (
SELECT 1
FROM birds
WHERE birds.id = $2
AND birds.workspace_id = $1
)
RETURNING id, workspace_id, bird_id, title, body, created_by_user_id, created_at, updated_at
)
SELECT inserted_note.id,
inserted_note.workspace_id,
inserted_note.bird_id,
birds.name AS bird_name,
inserted_note.title,
inserted_note.body,
inserted_note.created_by_user_id,
users.name AS created_by_name,
inserted_note.created_at,
inserted_note.updated_at
FROM inserted_note
LEFT JOIN birds ON birds.id = inserted_note.bird_id
LEFT JOIN users ON users.id = inserted_note.created_by_user_id`,
[workspaceId, birdId, title, body, createdByUserId],
);
return result.rows[0] ?? null;
};
export const deleteFlockNote = async (noteId: string, workspaceId: number) => {
const result = await db.query<{ id: string; title: string }>(
`DELETE FROM flock_notes
WHERE id = $1
AND workspace_id = $2
RETURNING id, title`,
[noteId, workspaceId],
);
return result.rows[0] ?? null;
};