feat: Phases 4-7 — Full Feature Expansion (26 features)
Phase 4 — New Widget Types: - Clock/Weather, System Stats, RSS/Feed, Calendar, Markdown, Metric/Counter, Link Group, Camera/Stream widgets - Backend services with caching for each data source - Full creation form with dynamic config fields per type Phase 5 — Visual & Styling Enhancements: - Glassmorphism card style (solid/glass/outline) - Board-level themes with per-board hue/saturation - Animated SVG status rings replacing static dots - Card size options (compact/medium/large) - Custom CSS injection (admin + per-board, sanitized) - Wallpaper backgrounds with blur/overlay/parallax Phase 6 — Functional Features: - Favorites bar with drag-and-drop reordering - Recent apps tracking with privacy toggle - Uptime dashboard page (/status, guest-accessible) - Notifications system (Discord/Slack/Telegram/HTTP webhooks) - App tags with filtering in board view - Multi-URL app cards with expandable sub-links - Personal API tokens with scoped permissions - Audit log with retention and admin viewer Phase 7 — Quality of Life: - Onboarding wizard (5-step first-launch setup) - App URL health preview with favicon/title detection - Board templates (4 built-in + custom import/export) - Keyboard shortcut overlay (j/k nav, 1-9 boards, ? help) 212 files changed, 15641 insertions, 980 deletions. Build, lint, type check, and 222 tests all pass.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { prisma } from '../prisma.js';
|
||||
|
||||
/**
|
||||
* Record an audit log entry. Non-blocking: catches and swallows errors
|
||||
* to avoid slowing down the operation being audited.
|
||||
*/
|
||||
export function logAction(
|
||||
userId: string | null,
|
||||
action: string,
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
details?: Record<string, unknown>
|
||||
): void {
|
||||
prisma.auditLog
|
||||
.create({
|
||||
data: {
|
||||
userId,
|
||||
action,
|
||||
entityType,
|
||||
entityId,
|
||||
details: details ? JSON.stringify(details) : '{}'
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// Non-blocking: swallow errors so the parent operation is unaffected
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query audit logs with filters and pagination.
|
||||
*/
|
||||
export async function getAuditLogs(options?: {
|
||||
action?: string;
|
||||
entityType?: string;
|
||||
userId?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}) {
|
||||
const where: Record<string, unknown> = {};
|
||||
|
||||
if (options?.action) {
|
||||
where.action = options.action;
|
||||
}
|
||||
if (options?.entityType) {
|
||||
where.entityType = options.entityType;
|
||||
}
|
||||
if (options?.userId) {
|
||||
where.userId = options.userId;
|
||||
}
|
||||
|
||||
const dateFilter: Record<string, Date> = {};
|
||||
if (options?.startDate) {
|
||||
dateFilter.gte = new Date(options.startDate);
|
||||
}
|
||||
if (options?.endDate) {
|
||||
dateFilter.lte = new Date(options.endDate);
|
||||
}
|
||||
if (Object.keys(dateFilter).length > 0) {
|
||||
where.createdAt = dateFilter;
|
||||
}
|
||||
|
||||
const limit = options?.limit ?? 50;
|
||||
const offset = options?.offset ?? 0;
|
||||
|
||||
const [logs, total] = await Promise.all([
|
||||
prisma.auditLog.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: limit,
|
||||
skip: offset,
|
||||
include: {
|
||||
user: {
|
||||
select: { id: true, displayName: true, email: true }
|
||||
}
|
||||
}
|
||||
}),
|
||||
prisma.auditLog.count({ where })
|
||||
]);
|
||||
|
||||
return { logs, total };
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete audit logs older than the given retention period.
|
||||
*/
|
||||
export async function pruneOldLogs(retentionDays: number = 90) {
|
||||
const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000);
|
||||
|
||||
const result = await prisma.auditLog.deleteMany({
|
||||
where: {
|
||||
createdAt: { lt: cutoff }
|
||||
}
|
||||
});
|
||||
|
||||
return result.count;
|
||||
}
|
||||
Reference in New Issue
Block a user