feat(CC-060): add ExportController (GET /export/data, POST /export/deletion-request)
This commit is contained in:
parent
8f69f9d95c
commit
eca3f4a48b
1 changed files with 130 additions and 0 deletions
130
src/export/export.controller.ts
Normal file
130
src/export/export.controller.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import { Body, Controller, Get, IsString, Post } from '@nestjs/common';
|
||||
import { eq, isNull, and } from 'drizzle-orm';
|
||||
import { CurrentSession } from '../auth/session.decorator';
|
||||
import type { AuthenticatedSession } from '../auth/tenant.guard';
|
||||
import { db } from '../db/client';
|
||||
import {
|
||||
organizations,
|
||||
tasks,
|
||||
goals,
|
||||
decisions,
|
||||
transactions,
|
||||
aiRequests,
|
||||
consentRecords,
|
||||
notifications,
|
||||
researchBriefs,
|
||||
savedSegments,
|
||||
opportunities,
|
||||
} from '../db/schema';
|
||||
|
||||
class DeletionRequestDto {
|
||||
@IsString() reason!: string;
|
||||
}
|
||||
|
||||
@Controller('export')
|
||||
export class ExportController {
|
||||
@Get('data')
|
||||
async exportData(@CurrentSession() session: AuthenticatedSession) {
|
||||
const tid = session.tenantId;
|
||||
const uid = session.userId;
|
||||
|
||||
const [
|
||||
orgs,
|
||||
tks,
|
||||
gls,
|
||||
decs,
|
||||
txs,
|
||||
aiReqs,
|
||||
consents,
|
||||
notifs,
|
||||
briefs,
|
||||
segs,
|
||||
opps,
|
||||
] = await Promise.all([
|
||||
db.query.organizations.findMany({
|
||||
where: and(eq(organizations.tenantId, tid), isNull(organizations.deletedAt)),
|
||||
}),
|
||||
db.query.tasks.findMany({ where: eq(tasks.tenantId, tid) }),
|
||||
db.query.goals.findMany({ where: eq(goals.tenantId, tid) }),
|
||||
db.query.decisions.findMany({ where: eq(decisions.tenantId, tid) }),
|
||||
db.query.transactions.findMany({ where: eq(transactions.tenantId, tid) }),
|
||||
db.query.aiRequests.findMany({
|
||||
where: eq(aiRequests.tenantId, tid),
|
||||
columns: { contextManifest: false },
|
||||
}),
|
||||
db.query.consentRecords.findMany({
|
||||
where: and(
|
||||
eq(consentRecords.tenantId, tid),
|
||||
eq(consentRecords.userId, uid),
|
||||
),
|
||||
}),
|
||||
db.query.notifications.findMany({ where: eq(notifications.tenantId, tid) }),
|
||||
db.query.researchBriefs.findMany({ where: eq(researchBriefs.tenantId, tid) }),
|
||||
db.query.savedSegments.findMany({ where: eq(savedSegments.tenantId, tid) }),
|
||||
db.query.opportunities.findMany({ where: eq(opportunities.tenantId, tid) }),
|
||||
]);
|
||||
|
||||
return {
|
||||
exportedAt: new Date().toISOString(),
|
||||
tenantId: tid,
|
||||
userId: uid,
|
||||
schema: '1.0',
|
||||
data: {
|
||||
organizations: orgs,
|
||||
tasks: tks,
|
||||
goals: gls,
|
||||
decisions: decs,
|
||||
transactions: txs,
|
||||
aiRequests: aiReqs,
|
||||
consentRecords: consents,
|
||||
notifications: notifs,
|
||||
researchBriefs: briefs,
|
||||
savedSegments: segs,
|
||||
opportunities: opps,
|
||||
},
|
||||
counts: {
|
||||
organizations: orgs.length,
|
||||
tasks: tks.length,
|
||||
goals: gls.length,
|
||||
decisions: decs.length,
|
||||
transactions: txs.length,
|
||||
aiRequests: aiReqs.length,
|
||||
consentRecords: consents.length,
|
||||
notifications: notifs.length,
|
||||
researchBriefs: briefs.length,
|
||||
savedSegments: segs.length,
|
||||
opportunities: opps.length,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Post('deletion-request')
|
||||
async deletionRequest(
|
||||
@CurrentSession() session: AuthenticatedSession,
|
||||
@Body() dto: DeletionRequestDto,
|
||||
) {
|
||||
// Record deletion request as a consent record with special purpose
|
||||
const existing = await db.query.consentRecords.findFirst({
|
||||
where: and(
|
||||
eq(consentRecords.tenantId, session.tenantId),
|
||||
eq(consentRecords.userId, session.userId),
|
||||
eq(consentRecords.purpose, 'account_deletion_requested'),
|
||||
isNull(consentRecords.revokedAt),
|
||||
),
|
||||
});
|
||||
if (existing) {
|
||||
return { submitted: true, alreadyPending: true, submittedAt: existing.grantedAt };
|
||||
}
|
||||
const [record] = await db
|
||||
.insert(consentRecords)
|
||||
.values({
|
||||
tenantId: session.tenantId,
|
||||
userId: session.userId,
|
||||
purpose: 'account_deletion_requested',
|
||||
grantedAt: new Date(),
|
||||
metadata: { reason: dto.reason },
|
||||
})
|
||||
.returning();
|
||||
return { submitted: true, alreadyPending: false, submittedAt: record.grantedAt };
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue