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:
parent
308434a5f2
commit
89a932cdd5
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user