mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
Replace direct OpenAI API calls with resolveProvider() in all insight generators (general, blood-pressure, weight, pulse, bmi, medication- compliance, mood). Each generator now uses domain-specific prompts from @/lib/ai/prompts/ and the AIProvider.generateCompletion() interface. hasKey -> hasProvider in return signatures and all UI consumers. Remove openaiKeyEncrypted references from data reset routes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
85 lines
2.7 KiB
TypeScript
85 lines
2.7 KiB
TypeScript
import { prisma } from "@/lib/db";
|
|
import { apiHandler, requireAdmin } from "@/lib/api-handler";
|
|
import { auditLog } from "@/lib/auth/audit";
|
|
import { apiSuccess, apiError, getClientIp } from "@/lib/api-response";
|
|
import { annotate } from "@/lib/logging/context";
|
|
import { checkRateLimit, rateLimitHeaders } from "@/lib/rate-limit";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
/**
|
|
* Admin-only global wipe of all health/integration data.
|
|
* Keeps users/passkeys so access to the app remains possible.
|
|
*/
|
|
export const DELETE = apiHandler(async (request: NextRequest) => {
|
|
const ip = getClientIp(request);
|
|
const rl = await checkRateLimit(`admin-data-delete:${ip}`, 5, 60 * 1000);
|
|
if (!rl.allowed) {
|
|
return NextResponse.json(
|
|
{ data: null, error: "Rate limit exceeded" },
|
|
{ status: 429, headers: rateLimitHeaders(rl) },
|
|
);
|
|
}
|
|
|
|
const { user } = await requireAdmin();
|
|
annotate({ action: { name: "admin.data.delete" } });
|
|
|
|
let confirm = "";
|
|
try {
|
|
const body = await request.json();
|
|
confirm = typeof body?.confirm === "string" ? body.confirm : "";
|
|
} catch {
|
|
return apiError("Invalid request", 422);
|
|
}
|
|
|
|
if (confirm !== "DELETE ALL") {
|
|
return apiError("Confirmation missing", 422);
|
|
}
|
|
|
|
const result = await prisma.$transaction(async (tx) => {
|
|
const measurements = await tx.measurement.deleteMany({});
|
|
const intakeEvents = await tx.medicationIntakeEvent.deleteMany({});
|
|
const medications = await tx.medication.deleteMany({});
|
|
const apiTokens = await tx.apiToken.deleteMany({});
|
|
const withingsConnections = await tx.withingsConnection.deleteMany({});
|
|
const auditLogs = await tx.auditLog.deleteMany({});
|
|
const authChallenges = await tx.authChallenge.deleteMany({});
|
|
|
|
await tx.user.updateMany({
|
|
data: {
|
|
heightCm: null,
|
|
dateOfBirth: null,
|
|
gender: null,
|
|
insightsPrivacyMode: "aggregated",
|
|
insightsCachedAt: null,
|
|
insightsCachedText: null,
|
|
telegramBotToken: null,
|
|
telegramChatId: null,
|
|
telegramEnabled: false,
|
|
withingsClientIdEncrypted: null,
|
|
withingsClientSecretEncrypted: null,
|
|
onboardingCompletedAt: null,
|
|
},
|
|
});
|
|
|
|
return {
|
|
measurements: measurements.count,
|
|
intakeEvents: intakeEvents.count,
|
|
medications: medications.count,
|
|
apiTokens: apiTokens.count,
|
|
withingsConnections: withingsConnections.count,
|
|
auditLogs: auditLogs.count,
|
|
authChallenges: authChallenges.count,
|
|
};
|
|
});
|
|
|
|
await auditLog("admin.data.clear", {
|
|
userId: user.id,
|
|
ipAddress: getClientIp(request),
|
|
details: result,
|
|
});
|
|
|
|
return apiSuccess({ cleared: true, ...result });
|
|
});
|