support snapshots
This commit is contained in:
@@ -26,6 +26,7 @@ MRP Codex is a modular Manufacturing Resource Planning platform intended to be a
|
|||||||
- admin diagnostics with runtime footprint, record counts, and persisted audit-trail visibility
|
- admin diagnostics with runtime footprint, record counts, and persisted audit-trail visibility
|
||||||
- admin user management with account creation, activation, role assignment, and role-permission editing
|
- admin user management with account creation, activation, role assignment, and role-permission editing
|
||||||
- CRM/shipping audit coverage and startup validation surfaced through the admin diagnostics workflow
|
- CRM/shipping audit coverage and startup validation surfaced through the admin diagnostics workflow
|
||||||
|
- backup/restore guidance and exportable support snapshots in the admin diagnostics workflow
|
||||||
- Puppeteer PDF foundation
|
- Puppeteer PDF foundation
|
||||||
- single-container Docker deployment
|
- single-container Docker deployment
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ If implementation changes invalidate those docs, update them in the same change
|
|||||||
|
|
||||||
Near-term priorities are:
|
Near-term priorities are:
|
||||||
|
|
||||||
1. Backup/restore workflow documentation and support-oriented admin tooling
|
1. Backup verification checklist and restore drill guidance
|
||||||
2. Deeper startup diagnostics and support export helpers
|
2. Deeper startup diagnostics and support export helpers
|
||||||
|
|
||||||
When adding new modules, preserve the ability to extend the system without refactoring the existing app shell.
|
When adding new modules, preserve the ability to extend the system without refactoring the existing app shell.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ This file is the running release and change log for MRP Codex. Keep it updated w
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Backup/restore guidance surfaced in admin diagnostics with exportable support snapshot JSON for support handoff
|
||||||
- CRM customer/vendor changes and shipping mutations now feed the shared audit trail
|
- CRM customer/vendor changes and shipping mutations now feed the shared audit trail
|
||||||
- Startup validation now runs during server boot and surfaces readiness checks in admin diagnostics
|
- Startup validation now runs during server boot and surfaces readiness checks in admin diagnostics
|
||||||
- Admin user-management screen with account creation, activation control, role assignment, and role-permission editing
|
- Admin user-management screen with account creation, activation control, role assignment, and role-permission editing
|
||||||
@@ -46,7 +47,7 @@ This file is the running release and change log for MRP Codex. Keep it updated w
|
|||||||
- The client entry bundle now stays lighter by loading major modules on demand instead of importing all route pages eagerly in `main.tsx`
|
- The client entry bundle now stays lighter by loading major modules on demand instead of importing all route pages eagerly in `main.tsx`
|
||||||
- Company settings now acts as the staging area for admin surfaces while user administration lives on its own dedicated page instead of inside the company-profile form
|
- Company settings now acts as the staging area for admin surfaces while user administration lives on its own dedicated page instead of inside the company-profile form
|
||||||
- Admin diagnostics now includes startup-readiness status alongside runtime footprint and recent audit activity
|
- Admin diagnostics now includes startup-readiness status alongside runtime footprint and recent audit activity
|
||||||
- Roadmap and project docs now treat backup/restore workflow documentation and support-oriented admin tooling as the next active priority after the CRM/shipping audit and startup-validation slice
|
- Roadmap and project docs now treat backup verification checklist and restore drill guidance as the next active priority after the backup/support-tooling slice
|
||||||
|
|
||||||
## 2026-03-15
|
## 2026-03-15
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ This repository implements the platform foundation milestone:
|
|||||||
- admin diagnostics with runtime footprint, storage visibility, record counts, and recent audit activity
|
- admin diagnostics with runtime footprint, storage visibility, record counts, and recent audit activity
|
||||||
- admin user management with account creation, activation, role assignment, and role-permission editing
|
- admin user management with account creation, activation, role assignment, and role-permission editing
|
||||||
- CRM/shipping audit coverage and startup validation surfaced through diagnostics
|
- CRM/shipping audit coverage and startup validation surfaced through diagnostics
|
||||||
|
- backup/restore guidance and exportable support snapshots in diagnostics
|
||||||
- Dockerized single-container deployment
|
- Dockerized single-container deployment
|
||||||
- Puppeteer PDF pipeline foundation
|
- Puppeteer PDF pipeline foundation
|
||||||
|
|
||||||
@@ -65,5 +66,5 @@ This repository implements the platform foundation milestone:
|
|||||||
|
|
||||||
## Next roadmap candidates
|
## Next roadmap candidates
|
||||||
|
|
||||||
- backup/restore workflow depth and support-oriented admin tooling
|
- backup verification checklist and restore drill guidance
|
||||||
- deeper startup diagnostics and support export helpers
|
- deeper startup diagnostics and support export helpers
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ Current foundation scope includes:
|
|||||||
- admin diagnostics with runtime footprint, record counts, and recent audit-trail visibility
|
- admin diagnostics with runtime footprint, record counts, and recent audit-trail visibility
|
||||||
- admin user management with account creation, activation, role assignment, and role-permission editing
|
- admin user management with account creation, activation, role assignment, and role-permission editing
|
||||||
- CRM and shipping audit coverage plus startup validation surfaced through the admin diagnostics page
|
- CRM and shipping audit coverage plus startup validation surfaced through the admin diagnostics page
|
||||||
|
- backup/restore guidance and exportable support snapshots in the admin diagnostics workflow
|
||||||
- route-level code-splitting and vendor chunking for lighter initial client loads
|
- route-level code-splitting and vendor chunking for lighter initial client loads
|
||||||
- file storage and PDF rendering
|
- file storage and PDF rendering
|
||||||
|
|
||||||
@@ -50,13 +51,13 @@ Current completed foundation areas:
|
|||||||
|
|
||||||
Near-term priorities:
|
Near-term priorities:
|
||||||
|
|
||||||
1. Backup/restore workflow documentation and support-oriented admin tooling
|
1. Backup verification checklist and restore drill guidance
|
||||||
2. Deeper startup diagnostics and support export helpers
|
2. Deeper startup diagnostics and support export helpers
|
||||||
|
|
||||||
Revisit / deferred items:
|
Revisit / deferred items:
|
||||||
|
|
||||||
- local Windows Prisma migration reliability
|
- local Windows Prisma migration reliability
|
||||||
- backup/restore workflow depth and support tooling
|
- backup verification checklist and restore drill depth
|
||||||
- deeper startup diagnostics and support export helpers
|
- deeper startup diagnostics and support export helpers
|
||||||
|
|
||||||
Dashboard direction:
|
Dashboard direction:
|
||||||
@@ -351,11 +352,12 @@ The current admin operations slice supports:
|
|||||||
- a dedicated user-management page for account creation, activation, role assignment, password reset-style updates, and role-permission administration
|
- a dedicated user-management page for account creation, activation, role assignment, password reset-style updates, and role-permission administration
|
||||||
- CRM customer/vendor changes and shipping mutations now flow into the shared audit trail
|
- CRM customer/vendor changes and shipping mutations now flow into the shared audit trail
|
||||||
- startup validation now checks storage paths, database connectivity, client bundle readiness, Chromium availability, and risky production defaults during server boot
|
- startup validation now checks storage paths, database connectivity, client bundle readiness, Chromium availability, and risky production defaults during server boot
|
||||||
|
- backup and restore guidance now surfaces directly in diagnostics, along with exportable support snapshot JSON for support handoff
|
||||||
- operator-facing review of recent high-impact changes without direct database access
|
- operator-facing review of recent high-impact changes without direct database access
|
||||||
|
|
||||||
Current follow-up direction:
|
Current follow-up direction:
|
||||||
|
|
||||||
- backup/restore workflow guidance and support-oriented admin tooling
|
- backup verification checklist and restore drill guidance
|
||||||
- deeper startup diagnostics and support export helpers
|
- deeper startup diagnostics and support export helpers
|
||||||
|
|
||||||
## UI Notes
|
## UI Notes
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ MRP Codex is being built as a streamlined, modular manufacturing resource planni
|
|||||||
- Dedicated user-management screen for account creation, activation, role assignment, and role-permission editing
|
- Dedicated user-management screen for account creation, activation, role assignment, and role-permission editing
|
||||||
- CRM customer/vendor changes and shipping mutations covered by the shared audit trail
|
- CRM customer/vendor changes and shipping mutations covered by the shared audit trail
|
||||||
- Startup validation during server boot with checks for storage paths, database connectivity, client bundle readiness, Chromium availability, and risky production defaults
|
- Startup validation during server boot with checks for storage paths, database connectivity, client bundle readiness, Chromium availability, and risky production defaults
|
||||||
|
- Backup/restore guidance and exportable support snapshots surfaced through the admin diagnostics workflow
|
||||||
- Route-level frontend code-splitting and vendor chunking to keep the initial client payload lighter
|
- Route-level frontend code-splitting and vendor chunking to keep the initial client payload lighter
|
||||||
- SKU-searchable BOM component selection for inventory-scale datasets
|
- SKU-searchable BOM component selection for inventory-scale datasets
|
||||||
- Theme persistence fixes and denser responsive workspace layouts
|
- Theme persistence fixes and denser responsive workspace layouts
|
||||||
@@ -293,5 +294,5 @@ QOL subfeatures:
|
|||||||
|
|
||||||
## Near-term priority order
|
## Near-term priority order
|
||||||
|
|
||||||
1. Backup/restore workflow documentation and support-oriented admin tooling
|
1. Backup verification checklist and restore drill guidance
|
||||||
2. Deeper startup diagnostics and support export helpers
|
2. Deeper startup diagnostics and support export helpers
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import type {
|
import type {
|
||||||
AdminDiagnosticsDto,
|
AdminDiagnosticsDto,
|
||||||
|
BackupGuidanceDto,
|
||||||
AdminPermissionOptionDto,
|
AdminPermissionOptionDto,
|
||||||
AdminRoleDto,
|
AdminRoleDto,
|
||||||
AdminRoleInput,
|
AdminRoleInput,
|
||||||
|
SupportSnapshotDto,
|
||||||
AdminUserDto,
|
AdminUserDto,
|
||||||
AdminUserInput,
|
AdminUserInput,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
@@ -136,6 +138,12 @@ export const api = {
|
|||||||
getAdminDiagnostics(token: string) {
|
getAdminDiagnostics(token: string) {
|
||||||
return request<AdminDiagnosticsDto>("/api/v1/admin/diagnostics", undefined, token);
|
return request<AdminDiagnosticsDto>("/api/v1/admin/diagnostics", undefined, token);
|
||||||
},
|
},
|
||||||
|
getBackupGuidance(token: string) {
|
||||||
|
return request<BackupGuidanceDto>("/api/v1/admin/backup-guidance", undefined, token);
|
||||||
|
},
|
||||||
|
getSupportSnapshot(token: string) {
|
||||||
|
return request<SupportSnapshotDto>("/api/v1/admin/support-snapshot", undefined, token);
|
||||||
|
},
|
||||||
getAdminPermissions(token: string) {
|
getAdminPermissions(token: string) {
|
||||||
return request<AdminPermissionOptionDto[]>("/api/v1/admin/permissions", undefined, token);
|
return request<AdminPermissionOptionDto[]>("/api/v1/admin/permissions", undefined, token);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AdminDiagnosticsDto } from "@mrp/shared";
|
import type { AdminDiagnosticsDto, BackupGuidanceDto } from "@mrp/shared";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ function parseMetadata(metadataJson: string) {
|
|||||||
export function AdminDiagnosticsPage() {
|
export function AdminDiagnosticsPage() {
|
||||||
const { token } = useAuth();
|
const { token } = useAuth();
|
||||||
const [diagnostics, setDiagnostics] = useState<AdminDiagnosticsDto | null>(null);
|
const [diagnostics, setDiagnostics] = useState<AdminDiagnosticsDto | null>(null);
|
||||||
|
const [backupGuidance, setBackupGuidance] = useState<BackupGuidanceDto | null>(null);
|
||||||
const [status, setStatus] = useState("Loading diagnostics...");
|
const [status, setStatus] = useState("Loading diagnostics...");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -29,13 +30,13 @@ export function AdminDiagnosticsPage() {
|
|||||||
|
|
||||||
let active = true;
|
let active = true;
|
||||||
|
|
||||||
api
|
Promise.all([api.getAdminDiagnostics(token), api.getBackupGuidance(token)])
|
||||||
.getAdminDiagnostics(token)
|
.then(([nextDiagnostics, nextBackupGuidance]) => {
|
||||||
.then((nextDiagnostics) => {
|
|
||||||
if (!active) {
|
if (!active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDiagnostics(nextDiagnostics);
|
setDiagnostics(nextDiagnostics);
|
||||||
|
setBackupGuidance(nextBackupGuidance);
|
||||||
setStatus("Diagnostics loaded.");
|
setStatus("Diagnostics loaded.");
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
@@ -50,10 +51,26 @@ export function AdminDiagnosticsPage() {
|
|||||||
};
|
};
|
||||||
}, [token]);
|
}, [token]);
|
||||||
|
|
||||||
if (!diagnostics) {
|
if (!diagnostics || !backupGuidance) {
|
||||||
return <div className="rounded-[28px] border border-line/70 bg-surface/90 p-4 text-sm text-muted shadow-panel">{status}</div>;
|
return <div className="rounded-[28px] border border-line/70 bg-surface/90 p-4 text-sm text-muted shadow-panel">{status}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleExportSupportSnapshot() {
|
||||||
|
if (!token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshot = await api.getSupportSnapshot(token);
|
||||||
|
const blob = new Blob([JSON.stringify(snapshot, null, 2)], { type: "application/json" });
|
||||||
|
const objectUrl = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = objectUrl;
|
||||||
|
link.download = `mrp-codex-support-snapshot-${new Date().toISOString().replace(/[:.]/g, "-")}.json`;
|
||||||
|
link.click();
|
||||||
|
window.setTimeout(() => window.URL.revokeObjectURL(objectUrl), 60_000);
|
||||||
|
setStatus("Support snapshot exported.");
|
||||||
|
}
|
||||||
|
|
||||||
const summaryCards = [
|
const summaryCards = [
|
||||||
["Server time", formatDateTime(diagnostics.serverTime)],
|
["Server time", formatDateTime(diagnostics.serverTime)],
|
||||||
["Node runtime", diagnostics.nodeVersion],
|
["Node runtime", diagnostics.nodeVersion],
|
||||||
@@ -97,6 +114,13 @@ export function AdminDiagnosticsPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleExportSupportSnapshot}
|
||||||
|
className="rounded-2xl border border-line/70 px-3 py-2 text-sm font-semibold text-text"
|
||||||
|
>
|
||||||
|
Export support snapshot
|
||||||
|
</button>
|
||||||
<Link to="/settings/users" className="rounded-2xl border border-line/70 px-3 py-2 text-sm font-semibold text-text">
|
<Link to="/settings/users" className="rounded-2xl border border-line/70 px-3 py-2 text-sm font-semibold text-text">
|
||||||
User management
|
User management
|
||||||
</Link>
|
</Link>
|
||||||
@@ -115,6 +139,47 @@ export function AdminDiagnosticsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded-[28px] border border-line/70 bg-surface/90 p-4 shadow-panel backdrop-blur 2xl:p-5">
|
||||||
|
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Backup And Restore</p>
|
||||||
|
<h3 className="mt-2 text-lg font-bold text-text">Operational backup workflow</h3>
|
||||||
|
<p className="mt-2 max-w-3xl text-sm text-muted">
|
||||||
|
Use these paths and steps as the support baseline for manual backup and restore procedures.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-line/70 bg-page/70 px-3 py-3 text-sm text-muted">
|
||||||
|
<div>Data: {backupGuidance.dataPath}</div>
|
||||||
|
<div>DB: {backupGuidance.databasePath}</div>
|
||||||
|
<div>Uploads: {backupGuidance.uploadsPath}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 grid gap-4 xl:grid-cols-2">
|
||||||
|
<div className="rounded-2xl border border-line/70 bg-page/70 p-4">
|
||||||
|
<p className="text-sm font-semibold text-text">Backup checklist</p>
|
||||||
|
<div className="mt-3 space-y-3">
|
||||||
|
{backupGuidance.backupSteps.map((step) => (
|
||||||
|
<div key={step.id}>
|
||||||
|
<p className="text-sm font-semibold text-text">{step.label}</p>
|
||||||
|
<p className="mt-1 text-sm text-muted">{step.detail}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-line/70 bg-page/70 p-4">
|
||||||
|
<p className="text-sm font-semibold text-text">Restore checklist</p>
|
||||||
|
<div className="mt-3 space-y-3">
|
||||||
|
{backupGuidance.restoreSteps.map((step) => (
|
||||||
|
<div key={step.id}>
|
||||||
|
<p className="text-sm font-semibold text-text">{step.label}</p>
|
||||||
|
<p className="mt-1 text-sm text-muted">{step.detail}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section className="rounded-[28px] border border-line/70 bg-surface/90 p-4 shadow-panel backdrop-blur 2xl:p-5">
|
<section className="rounded-[28px] border border-line/70 bg-surface/90 p-4 shadow-panel backdrop-blur 2xl:p-5">
|
||||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
|
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import { requirePermissions } from "../../lib/rbac.js";
|
|||||||
import {
|
import {
|
||||||
createAdminRole,
|
createAdminRole,
|
||||||
createAdminUser,
|
createAdminUser,
|
||||||
|
getBackupGuidance,
|
||||||
getAdminDiagnostics,
|
getAdminDiagnostics,
|
||||||
|
getSupportSnapshot,
|
||||||
listAdminPermissions,
|
listAdminPermissions,
|
||||||
listAdminRoles,
|
listAdminRoles,
|
||||||
listAdminUsers,
|
listAdminUsers,
|
||||||
@@ -40,6 +42,14 @@ adminRouter.get("/diagnostics", requirePermissions([permissions.adminManage]), a
|
|||||||
return ok(response, await getAdminDiagnostics());
|
return ok(response, await getAdminDiagnostics());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
adminRouter.get("/backup-guidance", requirePermissions([permissions.adminManage]), async (_request, response) => {
|
||||||
|
return ok(response, getBackupGuidance());
|
||||||
|
});
|
||||||
|
|
||||||
|
adminRouter.get("/support-snapshot", requirePermissions([permissions.adminManage]), async (_request, response) => {
|
||||||
|
return ok(response, await getSupportSnapshot());
|
||||||
|
});
|
||||||
|
|
||||||
adminRouter.get("/permissions", requirePermissions([permissions.adminManage]), async (_request, response) => {
|
adminRouter.get("/permissions", requirePermissions([permissions.adminManage]), async (_request, response) => {
|
||||||
return ok(response, await listAdminPermissions());
|
return ok(response, await listAdminPermissions());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import type {
|
import type {
|
||||||
AdminDiagnosticsDto,
|
AdminDiagnosticsDto,
|
||||||
|
BackupGuidanceDto,
|
||||||
AdminPermissionOptionDto,
|
AdminPermissionOptionDto,
|
||||||
AdminRoleDto,
|
AdminRoleDto,
|
||||||
AdminRoleInput,
|
AdminRoleInput,
|
||||||
AdminUserDto,
|
AdminUserDto,
|
||||||
AdminUserInput,
|
AdminUserInput,
|
||||||
|
SupportSnapshotDto,
|
||||||
AuditEventDto,
|
AuditEventDto,
|
||||||
} from "@mrp/shared";
|
} from "@mrp/shared";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
@@ -547,3 +549,76 @@ export async function getAdminDiagnostics(): Promise<AdminDiagnosticsDto> {
|
|||||||
recentAuditEvents: recentAuditEvents.map(mapAuditEvent),
|
recentAuditEvents: recentAuditEvents.map(mapAuditEvent),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getBackupGuidance(): BackupGuidanceDto {
|
||||||
|
return {
|
||||||
|
dataPath: paths.dataDir,
|
||||||
|
databasePath: `${paths.prismaDir}/app.db`,
|
||||||
|
uploadsPath: paths.uploadsDir,
|
||||||
|
recommendedBackupTarget: "/mnt/user/backups/mrp-codex",
|
||||||
|
backupSteps: [
|
||||||
|
{
|
||||||
|
id: "stop-app",
|
||||||
|
label: "Stop writes before copying data",
|
||||||
|
detail: "Stop the container or application process before copying the data directory so SQLite and attachments stay consistent.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "copy-data",
|
||||||
|
label: "Back up the full data directory",
|
||||||
|
detail: `Copy the full data directory at ${paths.dataDir}, not just the SQLite file, so uploads and attachments are preserved with the database.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "retain-metadata",
|
||||||
|
label: "Keep timestamps and structure",
|
||||||
|
detail: "Preserve directory structure, filenames, and timestamps during backup so support recovery remains straightforward.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "record-build",
|
||||||
|
label: "Record image/version context",
|
||||||
|
detail: "Capture the deployed image tag or commit alongside the backup so schema and runtime expectations are clear during restore.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
restoreSteps: [
|
||||||
|
{
|
||||||
|
id: "stop-target",
|
||||||
|
label: "Stop the target app before restore",
|
||||||
|
detail: "Do not restore into a running instance. Stop the target container or process before replacing the data directory.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "replace-data",
|
||||||
|
label: "Restore the full data directory",
|
||||||
|
detail: `Replace the target data directory with the backed-up copy so ${paths.prismaDir}/app.db and uploads come back together.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "start-and-migrate",
|
||||||
|
label: "Start the app and let migrations run",
|
||||||
|
detail: "Restart the application after restore and allow the normal startup migration flow to complete before validation.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "validate-core",
|
||||||
|
label: "Validate login, files, and PDFs",
|
||||||
|
detail: "Confirm admin login, attachment access, and PDF generation after restore to verify the operational surface is healthy.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSupportSnapshot(): Promise<SupportSnapshotDto> {
|
||||||
|
const diagnostics = await getAdminDiagnostics();
|
||||||
|
const [users, roles] = await Promise.all([
|
||||||
|
prisma.user.findMany({
|
||||||
|
where: { isActive: true },
|
||||||
|
select: { email: true },
|
||||||
|
orderBy: [{ email: "asc" }],
|
||||||
|
}),
|
||||||
|
prisma.role.count(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
generatedAt: new Date().toISOString(),
|
||||||
|
diagnostics,
|
||||||
|
userCount: diagnostics.userCount,
|
||||||
|
roleCount: roles,
|
||||||
|
activeUserEmails: users.map((user) => user.email),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,6 +60,29 @@ export interface StartupValidationCheckDto {
|
|||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BackupChecklistItemDto {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
detail: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BackupGuidanceDto {
|
||||||
|
dataPath: string;
|
||||||
|
databasePath: string;
|
||||||
|
uploadsPath: string;
|
||||||
|
recommendedBackupTarget: string;
|
||||||
|
backupSteps: BackupChecklistItemDto[];
|
||||||
|
restoreSteps: BackupChecklistItemDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SupportSnapshotDto {
|
||||||
|
generatedAt: string;
|
||||||
|
diagnostics: AdminDiagnosticsDto;
|
||||||
|
userCount: number;
|
||||||
|
roleCount: number;
|
||||||
|
activeUserEmails: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface AdminDiagnosticsDto {
|
export interface AdminDiagnosticsDto {
|
||||||
serverTime: string;
|
serverTime: string;
|
||||||
nodeVersion: string;
|
nodeVersion: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user