Schema: - 8 neue Profile-Felder fuer DiGA-Demographics (birthYear/gender/maritalStatus/ profession/bundesland/city + 2 consent-stamps demographicsConsentAt/ demographicsWithdrawnAt) - 4 Pro-Trial-Felder (proTrialStartedAt/ExpiresAt/Source/UsedAt) — Free-User bekommen 1 Woche Pro als Reward fuer DiGA-Daten-Pflege (siehe project_demographic_pro_trial_reward.md) - lyra_voice_id (Legend-only Voice-Picker) - diga_banner_dismissed_at (server-side persistence ueber Re-Install) - last_install_at (Streak-Logic survives Re-Install) - Migration 20260507_profile_demographics_and_trial: alle Felder optional, keine Backfill-Logik notwendig Endpoints (alle auth-protected, scope=me): - GET /api/profile/me/sos-insights - GET /api/profile/me/cooldown-history - GET /api/profile/me/approved-domains - POST /api/profile/me/install-event (track app re-installs) - POST /api/profile/me/diga-banner-dismiss - PATCH /api/profile/me/demographics (consent-stamp + re-grant-after-withdrawal in tx) - DELETE /api/profile/me/demographics (DSGVO right-to-be-forgotten) Plugin: - pro-trial-expiry-cron: 6h-Interval, conservative-fallback (revoke nur wenn kein stripeSubId), 60s initial-delay damit Server-boot nicht blockiert wird Tests: - vitest config + erste Test-Files (test-infrastructure setup) Memory: - feedback_demographics_user_initiated.md (Lyra darf NIE extrahieren) - project_demographic_pro_trial_reward.md (Pro-Trial-Reward-Mechanik) - project_profile_page_design.md (UI-Showpiece, eigene/fremde-Ansicht streng getrennt) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
/**
|
|
* GET /api/profile/me/approved-domains
|
|
*
|
|
* Liste der vom User submitten und vom Admin genehmigten Domains
|
|
* (= Community-Beitrag-Benchmark, siehe project_profile_page_design.md §2).
|
|
*
|
|
* Source: domain_submissions WHERE userId = me AND status = 'approved'
|
|
* (NICHT user_custom_domains — domain_submissions ist source of truth für
|
|
* "von dir submitted und approved").
|
|
*
|
|
* Response shape:
|
|
* { count: number, list: Array<{ domain, approvedAt }> }
|
|
*
|
|
* Sortiert: approvedAt DESC. Cap 100 (UI rendert max 100, mehr braucht
|
|
* Pagination — kann später additiv kommen).
|
|
*/
|
|
import { requireUser } from "../../../utils/auth";
|
|
import { usePrisma } from "../../../utils/prisma";
|
|
|
|
const MAX_LIST_ITEMS = 100;
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const user = await requireUser(event);
|
|
const db = usePrisma();
|
|
|
|
const [count, rows] = await Promise.all([
|
|
db.domainSubmission.count({
|
|
where: { userId: user.id, status: "approved" },
|
|
}),
|
|
db.domainSubmission.findMany({
|
|
where: { userId: user.id, status: "approved" },
|
|
orderBy: { reviewedAt: "desc" },
|
|
take: MAX_LIST_ITEMS,
|
|
select: { domain: true, reviewedAt: true },
|
|
}),
|
|
]);
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
count,
|
|
list: rows.map((r) => ({
|
|
domain: r.domain,
|
|
approvedAt: r.reviewedAt?.toISOString() ?? null,
|
|
})),
|
|
},
|
|
};
|
|
});
|