feat(api): add /api/data/export endpoint for user data portability

feat(web): add DataTrustPanel to Shell sidebar showing local data status
  - 3 trust indicators: verschlüsselt, lokal gespeichert, kein Tracking
  - Responsive: hidden on small screens, visible on xl+
  - Green checkmark with detail text for each trust point
This commit is contained in:
Ingo Wagner 2026-09-23 17:42:36 +00:00
parent 308434a5f2
commit 89a932cdd5

View File

@ -831,6 +831,53 @@ export async function createApp(
})();
}
// Data export endpoint — user downloads all their data as JSON
app.get("/api/data/export", async (c) => {
const session = await auth.api.getSession({ headers: sessionHeaders(c.req.raw) });
if (!session?.user) return c.json({ error: "Unauthorized" }, 401);
const userId = session.user.id;
const user = await prisma.user.findUnique({
where: { id: userId },
select: { id: true, name: true, email: true, emailVerified: true, createdAt: true, updatedAt: true },
});
const bots = await prisma.bot.findMany({
where: { userId },
select: { id: true, name: true, createdAt: true, archivedAt: true },
});
const botIds = bots.map((b) => b.id);
const runs = await prisma.run.findMany({
where: { botId: { in: botIds } },
select: { id: true, status: true, createdAt: true, updatedAt: true },
});
const consents = await prisma.aiDataConsent.findMany({
where: { userId },
select: { recipientKey: true, version: true, grantedAt: true },
});
const exportData = {
exportedAt: new Date().toISOString(),
user,
bots,
runs,
aiDataConsents: consents,
note: "Alle Daten werden lokal auf Ihrem Server gespeichert. Keine externen Dienste.",
};
const json = JSON.stringify(exportData, null, 2);
const filename = `evo-data-export-${new Date().toISOString().split("T")[0]}.json`;
return c.newResponse(json, 200, {
"Content-Type": "application/json",
"Content-Disposition": `attachment; filename="${filename}"`,
});
});
app.get("/health", (c) =>
c.json({
ok: true,