@tantainnovative/create-ndpr 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Next.js App Router — DPIA (Data Protection Impact Assessment) Route
3
+ * Generated by create-ndpr for: {{ORG_NAME}}
4
+ *
5
+ * NDPA §28 — a DPIA must be conducted where processing is likely
6
+ * to result in a high risk to the rights and freedoms of data subjects.
7
+ *
8
+ * Endpoints:
9
+ * GET /api/dpia — List DPIA records (optional ?status= filter)
10
+ * GET /api/dpia?id=xxx — Get a single DPIA record
11
+ * POST /api/dpia — Create a new DPIA record
12
+ * PUT /api/dpia — Update an existing DPIA record
13
+ * DELETE /api/dpia?id=xxx — Delete a DPIA record
14
+ */
15
+
16
+ import { NextRequest, NextResponse } from 'next/server';
17
+ // {{#if ORM=prisma}}
18
+ import { PrismaClient } from '@prisma/client';
19
+
20
+ const prisma = new PrismaClient();
21
+ // {{/if}}
22
+ // {{#if ORM=drizzle}}
23
+ import { db } from '@/drizzle';
24
+ import { dpiaRecords, complianceAuditLog } from '@/drizzle/ndpr-schema';
25
+ import { eq, desc } from 'drizzle-orm';
26
+ // {{/if}}
27
+ // {{#if ORM=none}}
28
+ // TODO ({{ORG_NAME}}): wire this to your persistent store of choice. The
29
+ // in-memory map below is enough to develop against locally but DOES NOT
30
+ // satisfy NDPA Section 44 (record-keeping) or the auditability NDPC asks
31
+ // about. Replace `dpiaStore`/`auditLog` with your DB / KV / API.
32
+ interface DPIARecord {
33
+ id: string;
34
+ projectName: string;
35
+ description: string;
36
+ dpiaData: unknown;
37
+ overallRisk: string;
38
+ score: number;
39
+ status: string;
40
+ conductedBy: string;
41
+ approvedBy: string | null;
42
+ createdAt: Date;
43
+ updatedAt: Date;
44
+ }
45
+ const dpiaStore = new Map<string, DPIARecord>();
46
+ const auditLog: Array<{ id: string; module: string; action: string; entityId: string; at: Date }> = [];
47
+ function newId() { return crypto.randomUUID(); }
48
+ // {{/if}}
49
+
50
+ // GET /api/dpia?status=draft OR /api/dpia?id=xxx
51
+ export async function GET(req: NextRequest) {
52
+ const id = req.nextUrl.searchParams.get('id');
53
+
54
+ if (id) {
55
+ // {{#if ORM=prisma}}
56
+ const record = await prisma.dPIARecord.findUnique({ where: { id } });
57
+ // {{/if}}
58
+ // {{#if ORM=drizzle}}
59
+ const [record] = await db.select().from(dpiaRecords).where(eq(dpiaRecords.id, id)).limit(1);
60
+ // {{/if}}
61
+ // {{#if ORM=none}}
62
+ const record = dpiaStore.get(id) ?? null;
63
+ // {{/if}}
64
+
65
+ if (!record) {
66
+ return NextResponse.json({ error: 'DPIA record not found' }, { status: 404 });
67
+ }
68
+ return NextResponse.json(record);
69
+ }
70
+
71
+ const status = req.nextUrl.searchParams.get('status');
72
+
73
+ // {{#if ORM=prisma}}
74
+ const records = await prisma.dPIARecord.findMany({
75
+ where: status ? { status } : undefined,
76
+ orderBy: { createdAt: 'desc' },
77
+ });
78
+ // {{/if}}
79
+ // {{#if ORM=drizzle}}
80
+ const records = status
81
+ ? await db.select().from(dpiaRecords).where(eq(dpiaRecords.status, status)).orderBy(desc(dpiaRecords.createdAt))
82
+ : await db.select().from(dpiaRecords).orderBy(desc(dpiaRecords.createdAt));
83
+ // {{/if}}
84
+ // {{#if ORM=none}}
85
+ const records = [...dpiaStore.values()]
86
+ .filter((r) => (status ? r.status === status : true))
87
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
88
+ // {{/if}}
89
+
90
+ return NextResponse.json(records);
91
+ }
92
+
93
+ // POST /api/dpia
94
+ export async function POST(req: NextRequest) {
95
+ const body = await req.json();
96
+ const {
97
+ projectName,
98
+ description,
99
+ dpiaData,
100
+ overallRisk,
101
+ score,
102
+ conductedBy,
103
+ approvedBy,
104
+ } = body;
105
+
106
+ if (!projectName || !description || !dpiaData || !overallRisk || score == null || !conductedBy) {
107
+ return NextResponse.json(
108
+ { error: 'projectName, description, dpiaData, overallRisk, score, and conductedBy are required' },
109
+ { status: 400 },
110
+ );
111
+ }
112
+
113
+ // {{#if ORM=prisma}}
114
+ const record = await prisma.dPIARecord.create({
115
+ data: {
116
+ projectName,
117
+ description,
118
+ dpiaData,
119
+ overallRisk,
120
+ score,
121
+ status: 'draft',
122
+ conductedBy,
123
+ approvedBy: approvedBy ?? null,
124
+ },
125
+ });
126
+
127
+ await prisma.complianceAuditLog.create({
128
+ data: {
129
+ module: 'dpia',
130
+ action: 'created',
131
+ entityId: record.id,
132
+ entityType: 'DPIARecord',
133
+ changes: { projectName, overallRisk, score, status: 'draft' },
134
+ },
135
+ });
136
+ // {{/if}}
137
+ // {{#if ORM=drizzle}}
138
+ const [record] = await db.insert(dpiaRecords).values({
139
+ projectName,
140
+ description,
141
+ dpiaData,
142
+ overallRisk,
143
+ score,
144
+ status: 'draft',
145
+ conductedBy,
146
+ approvedBy: approvedBy ?? null,
147
+ }).returning();
148
+
149
+ await db.insert(complianceAuditLog).values({
150
+ module: 'dpia',
151
+ action: 'created',
152
+ entityId: record.id,
153
+ entityType: 'DPIARecord',
154
+ changes: { projectName, overallRisk, score, status: 'draft' },
155
+ });
156
+ // {{/if}}
157
+ // {{#if ORM=none}}
158
+ const now = new Date();
159
+ const record: DPIARecord = {
160
+ id: newId(),
161
+ projectName,
162
+ description,
163
+ dpiaData,
164
+ overallRisk,
165
+ score,
166
+ status: 'draft',
167
+ conductedBy,
168
+ approvedBy: approvedBy ?? null,
169
+ createdAt: now,
170
+ updatedAt: now,
171
+ };
172
+ dpiaStore.set(record.id, record);
173
+ auditLog.push({ id: newId(), module: 'dpia', action: 'created', entityId: record.id, at: new Date() });
174
+ // {{/if}}
175
+
176
+ return NextResponse.json(record, { status: 201 });
177
+ }
178
+
179
+ // PUT /api/dpia
180
+ export async function PUT(req: NextRequest) {
181
+ const body = await req.json();
182
+ const { id, ...data } = body;
183
+
184
+ if (!id) {
185
+ return NextResponse.json({ error: 'id is required' }, { status: 400 });
186
+ }
187
+
188
+ // {{#if ORM=prisma}}
189
+ const existing = await prisma.dPIARecord.findUnique({ where: { id } });
190
+ if (!existing) {
191
+ return NextResponse.json({ error: 'DPIA record not found' }, { status: 404 });
192
+ }
193
+
194
+ const record = await prisma.dPIARecord.update({
195
+ where: { id },
196
+ data,
197
+ });
198
+
199
+ await prisma.complianceAuditLog.create({
200
+ data: {
201
+ module: 'dpia',
202
+ action: 'updated',
203
+ entityId: record.id,
204
+ entityType: 'DPIARecord',
205
+ changes: data,
206
+ },
207
+ });
208
+ // {{/if}}
209
+ // {{#if ORM=drizzle}}
210
+ const [existing] = await db.select().from(dpiaRecords).where(eq(dpiaRecords.id, id)).limit(1);
211
+ if (!existing) {
212
+ return NextResponse.json({ error: 'DPIA record not found' }, { status: 404 });
213
+ }
214
+
215
+ const [record] = await db.update(dpiaRecords).set(data).where(eq(dpiaRecords.id, id)).returning();
216
+
217
+ await db.insert(complianceAuditLog).values({
218
+ module: 'dpia',
219
+ action: 'updated',
220
+ entityId: record.id,
221
+ entityType: 'DPIARecord',
222
+ changes: data,
223
+ });
224
+ // {{/if}}
225
+ // {{#if ORM=none}}
226
+ const existing = dpiaStore.get(id);
227
+ if (!existing) {
228
+ return NextResponse.json({ error: 'DPIA record not found' }, { status: 404 });
229
+ }
230
+ const record: DPIARecord = { ...existing, ...data, id, updatedAt: new Date() };
231
+ dpiaStore.set(id, record);
232
+ auditLog.push({ id: newId(), module: 'dpia', action: 'updated', entityId: record.id, at: new Date() });
233
+ // {{/if}}
234
+
235
+ return NextResponse.json(record);
236
+ }
237
+
238
+ // DELETE /api/dpia?id=xxx
239
+ export async function DELETE(req: NextRequest) {
240
+ const id = req.nextUrl.searchParams.get('id');
241
+
242
+ if (!id) {
243
+ return NextResponse.json({ error: 'id is required' }, { status: 400 });
244
+ }
245
+
246
+ // {{#if ORM=prisma}}
247
+ const existing = await prisma.dPIARecord.findUnique({ where: { id } });
248
+ if (!existing) {
249
+ return NextResponse.json({ error: 'DPIA record not found' }, { status: 404 });
250
+ }
251
+
252
+ await prisma.dPIARecord.delete({ where: { id } });
253
+
254
+ await prisma.complianceAuditLog.create({
255
+ data: {
256
+ module: 'dpia',
257
+ action: 'deleted',
258
+ entityId: id,
259
+ entityType: 'DPIARecord',
260
+ changes: { projectName: existing.projectName },
261
+ },
262
+ });
263
+ // {{/if}}
264
+ // {{#if ORM=drizzle}}
265
+ const [existing] = await db.select().from(dpiaRecords).where(eq(dpiaRecords.id, id)).limit(1);
266
+ if (!existing) {
267
+ return NextResponse.json({ error: 'DPIA record not found' }, { status: 404 });
268
+ }
269
+
270
+ await db.delete(dpiaRecords).where(eq(dpiaRecords.id, id));
271
+
272
+ await db.insert(complianceAuditLog).values({
273
+ module: 'dpia',
274
+ action: 'deleted',
275
+ entityId: id,
276
+ entityType: 'DPIARecord',
277
+ changes: { projectName: existing.projectName },
278
+ });
279
+ // {{/if}}
280
+ // {{#if ORM=none}}
281
+ const existing = dpiaStore.get(id);
282
+ if (!existing) {
283
+ return NextResponse.json({ error: 'DPIA record not found' }, { status: 404 });
284
+ }
285
+ dpiaStore.delete(id);
286
+ auditLog.push({ id: newId(), module: 'dpia', action: 'deleted', entityId: id, at: new Date() });
287
+ // {{/if}}
288
+
289
+ return NextResponse.json({ success: true });
290
+ }
@@ -2,9 +2,9 @@
2
2
  * Next.js App Router — DSR (Data Subject Rights) Route
3
3
  * Generated by create-ndpr for: {{ORG_NAME}}
4
4
  *
5
- * NDPA Part IV §34–38 — rights to access, rectification, erasure,
6
- * portability, and objection. All requests must be acknowledged
7
- * within 72 hours and fulfilled within 30 days.
5
+ * NDPA §34 — rights to access, rectification, erasure, portability, and
6
+ * objection. All requests must be acknowledged promptly and fulfilled
7
+ * within the statutory window (NDPC guidance: 30 days).
8
8
  *
9
9
  * Endpoints:
10
10
  * GET /api/dsr — List DSR requests (optional ?status= filter)
@@ -12,18 +12,59 @@
12
12
  */
13
13
 
14
14
  import { NextRequest, NextResponse } from 'next/server';
15
+ // {{#if ORM=prisma}}
15
16
  import { PrismaClient } from '@prisma/client';
16
17
 
17
18
  const prisma = new PrismaClient();
19
+ // {{/if}}
20
+ // {{#if ORM=drizzle}}
21
+ import { db } from '@/drizzle';
22
+ import { dsrRequests, complianceAuditLog } from '@/drizzle/ndpr-schema';
23
+ import { eq, desc } from 'drizzle-orm';
24
+ // {{/if}}
25
+ // {{#if ORM=none}}
26
+ // TODO ({{ORG_NAME}}): wire this to your persistent store of choice. The
27
+ // in-memory map below is enough to develop against locally but DOES NOT
28
+ // satisfy NDPA Section 44 (record-keeping) or the auditability NDPC asks
29
+ // about. Replace `dsrStore`/`auditLog` with your DB / KV / API.
30
+ interface DSRRequest {
31
+ id: string;
32
+ type: string;
33
+ subjectName: string;
34
+ subjectEmail: string;
35
+ subjectPhone: string | null;
36
+ identifierType: string;
37
+ identifierValue: string;
38
+ description: string | null;
39
+ status: string;
40
+ submittedAt: Date;
41
+ dueAt: Date;
42
+ }
43
+ const dsrStore = new Map<string, DSRRequest>();
44
+ const auditLog: Array<{ id: string; module: string; action: string; entityId: string; at: Date }> = [];
45
+ function newId() { return crypto.randomUUID(); }
46
+ // {{/if}}
18
47
 
19
48
  // GET /api/dsr?status=pending
20
49
  export async function GET(req: NextRequest) {
21
50
  const status = req.nextUrl.searchParams.get('status');
22
51
 
52
+ // {{#if ORM=prisma}}
23
53
  const requests = await prisma.dSRRequest.findMany({
24
54
  where: status ? { status } : undefined,
25
55
  orderBy: { submittedAt: 'desc' },
26
56
  });
57
+ // {{/if}}
58
+ // {{#if ORM=drizzle}}
59
+ const requests = status
60
+ ? await db.select().from(dsrRequests).where(eq(dsrRequests.status, status)).orderBy(desc(dsrRequests.submittedAt))
61
+ : await db.select().from(dsrRequests).orderBy(desc(dsrRequests.submittedAt));
62
+ // {{/if}}
63
+ // {{#if ORM=none}}
64
+ const requests = [...dsrStore.values()]
65
+ .filter((r) => (status ? r.status === status : true))
66
+ .sort((a, b) => b.submittedAt.getTime() - a.submittedAt.getTime());
67
+ // {{/if}}
27
68
 
28
69
  return NextResponse.json(requests);
29
70
  }
@@ -52,6 +93,7 @@ export async function POST(req: NextRequest) {
52
93
  const dueAt = new Date();
53
94
  dueAt.setDate(dueAt.getDate() + 30);
54
95
 
96
+ // {{#if ORM=prisma}}
55
97
  const request = await prisma.dSRRequest.create({
56
98
  data: {
57
99
  type,
@@ -75,6 +117,45 @@ export async function POST(req: NextRequest) {
75
117
  changes: { type, subjectEmail, status: 'pending' },
76
118
  },
77
119
  });
120
+ // {{/if}}
121
+ // {{#if ORM=drizzle}}
122
+ const [request] = await db.insert(dsrRequests).values({
123
+ type,
124
+ subjectName,
125
+ subjectEmail,
126
+ subjectPhone: subjectPhone ?? null,
127
+ identifierType,
128
+ identifierValue,
129
+ description: description ?? null,
130
+ status: 'pending',
131
+ dueAt,
132
+ }).returning();
133
+
134
+ await db.insert(complianceAuditLog).values({
135
+ module: 'dsr',
136
+ action: 'submitted',
137
+ entityId: request.id,
138
+ entityType: 'DSRRequest',
139
+ changes: { type, subjectEmail, status: 'pending' },
140
+ });
141
+ // {{/if}}
142
+ // {{#if ORM=none}}
143
+ const request: DSRRequest = {
144
+ id: newId(),
145
+ type,
146
+ subjectName,
147
+ subjectEmail,
148
+ subjectPhone: subjectPhone ?? null,
149
+ identifierType,
150
+ identifierValue,
151
+ description: description ?? null,
152
+ status: 'pending',
153
+ submittedAt: new Date(),
154
+ dueAt,
155
+ };
156
+ dsrStore.set(request.id, request);
157
+ auditLog.push({ id: newId(), module: 'dsr', action: 'submitted', entityId: request.id, at: new Date() });
158
+ // {{/if}}
78
159
 
79
160
  return NextResponse.json(request, { status: 201 });
80
161
  }
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Next.js App Router — Lawful Basis Route
3
+ * Generated by create-ndpr for: {{ORG_NAME}}
4
+ *
5
+ * NDPA §25 — data processing must have a lawful basis. This route
6
+ * manages a register of processing activities and their lawful bases.
7
+ *
8
+ * Endpoints:
9
+ * GET /api/lawful-basis — List lawful basis records (optional ?lawfulBasis= filter)
10
+ * GET /api/lawful-basis?id=xxx — Get a single record
11
+ * POST /api/lawful-basis — Create a new lawful basis record
12
+ * PUT /api/lawful-basis — Update an existing record
13
+ * DELETE /api/lawful-basis?id=xxx — Delete a record
14
+ */
15
+
16
+ import { NextRequest, NextResponse } from 'next/server';
17
+ // {{#if ORM=prisma}}
18
+ import { PrismaClient } from '@prisma/client';
19
+
20
+ const prisma = new PrismaClient();
21
+ // {{/if}}
22
+ // {{#if ORM=drizzle}}
23
+ import { db } from '@/drizzle';
24
+ import { lawfulBasisRecords, complianceAuditLog } from '@/drizzle/ndpr-schema';
25
+ import { eq, desc } from 'drizzle-orm';
26
+ // {{/if}}
27
+ // {{#if ORM=none}}
28
+ // TODO ({{ORG_NAME}}): wire this to your persistent store of choice. The
29
+ // in-memory map below is enough to develop against locally but DOES NOT
30
+ // satisfy NDPA Section 44 (record-keeping). Replace `lawfulBasisStore`/
31
+ // `auditLog` with your DB / KV / API.
32
+ interface LawfulBasisRecord {
33
+ id: string;
34
+ activityName: string;
35
+ lawfulBasis: string;
36
+ justification: string;
37
+ dataCategories: string[];
38
+ purposes: string[];
39
+ assessedBy: string;
40
+ reviewDate: Date | null;
41
+ createdAt: Date;
42
+ updatedAt: Date;
43
+ }
44
+ const lawfulBasisStore = new Map<string, LawfulBasisRecord>();
45
+ const auditLog: Array<{ id: string; module: string; action: string; entityId: string; at: Date }> = [];
46
+ function newId() { return crypto.randomUUID(); }
47
+ // {{/if}}
48
+
49
+ // GET /api/lawful-basis?lawfulBasis=consent OR /api/lawful-basis?id=xxx
50
+ export async function GET(req: NextRequest) {
51
+ const id = req.nextUrl.searchParams.get('id');
52
+
53
+ if (id) {
54
+ // {{#if ORM=prisma}}
55
+ const record = await prisma.lawfulBasisRecord.findUnique({ where: { id } });
56
+ // {{/if}}
57
+ // {{#if ORM=drizzle}}
58
+ const [record] = await db.select().from(lawfulBasisRecords).where(eq(lawfulBasisRecords.id, id)).limit(1);
59
+ // {{/if}}
60
+ // {{#if ORM=none}}
61
+ const record = lawfulBasisStore.get(id) ?? null;
62
+ // {{/if}}
63
+
64
+ if (!record) {
65
+ return NextResponse.json({ error: 'Lawful basis record not found' }, { status: 404 });
66
+ }
67
+ return NextResponse.json(record);
68
+ }
69
+
70
+ const lawfulBasis = req.nextUrl.searchParams.get('lawfulBasis');
71
+
72
+ // {{#if ORM=prisma}}
73
+ const records = await prisma.lawfulBasisRecord.findMany({
74
+ where: lawfulBasis ? { lawfulBasis } : undefined,
75
+ orderBy: { createdAt: 'desc' },
76
+ });
77
+ // {{/if}}
78
+ // {{#if ORM=drizzle}}
79
+ const records = lawfulBasis
80
+ ? await db.select().from(lawfulBasisRecords).where(eq(lawfulBasisRecords.lawfulBasis, lawfulBasis)).orderBy(desc(lawfulBasisRecords.createdAt))
81
+ : await db.select().from(lawfulBasisRecords).orderBy(desc(lawfulBasisRecords.createdAt));
82
+ // {{/if}}
83
+ // {{#if ORM=none}}
84
+ const records = [...lawfulBasisStore.values()]
85
+ .filter((r) => (lawfulBasis ? r.lawfulBasis === lawfulBasis : true))
86
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
87
+ // {{/if}}
88
+
89
+ return NextResponse.json(records);
90
+ }
91
+
92
+ // POST /api/lawful-basis
93
+ export async function POST(req: NextRequest) {
94
+ const body = await req.json();
95
+ const {
96
+ activityName,
97
+ lawfulBasis,
98
+ justification,
99
+ dataCategories,
100
+ purposes,
101
+ assessedBy,
102
+ reviewDate,
103
+ } = body;
104
+
105
+ if (!activityName || !lawfulBasis || !justification || !dataCategories || !purposes || !assessedBy) {
106
+ return NextResponse.json(
107
+ { error: 'activityName, lawfulBasis, justification, dataCategories, purposes, and assessedBy are required' },
108
+ { status: 400 },
109
+ );
110
+ }
111
+
112
+ if (!Array.isArray(dataCategories) || !Array.isArray(purposes)) {
113
+ return NextResponse.json(
114
+ { error: 'dataCategories and purposes must be arrays' },
115
+ { status: 400 },
116
+ );
117
+ }
118
+
119
+ // {{#if ORM=prisma}}
120
+ const record = await prisma.lawfulBasisRecord.create({
121
+ data: {
122
+ activityName,
123
+ lawfulBasis,
124
+ justification,
125
+ dataCategories,
126
+ purposes,
127
+ assessedBy,
128
+ reviewDate: reviewDate ? new Date(reviewDate) : null,
129
+ },
130
+ });
131
+
132
+ await prisma.complianceAuditLog.create({
133
+ data: {
134
+ module: 'lawful-basis',
135
+ action: 'created',
136
+ entityId: record.id,
137
+ entityType: 'LawfulBasisRecord',
138
+ changes: { activityName, lawfulBasis, assessedBy },
139
+ },
140
+ });
141
+ // {{/if}}
142
+ // {{#if ORM=drizzle}}
143
+ const [record] = await db.insert(lawfulBasisRecords).values({
144
+ activityName,
145
+ lawfulBasis,
146
+ justification,
147
+ dataCategories,
148
+ purposes,
149
+ assessedBy,
150
+ reviewDate: reviewDate ? new Date(reviewDate) : null,
151
+ }).returning();
152
+
153
+ await db.insert(complianceAuditLog).values({
154
+ module: 'lawful-basis',
155
+ action: 'created',
156
+ entityId: record.id,
157
+ entityType: 'LawfulBasisRecord',
158
+ changes: { activityName, lawfulBasis, assessedBy },
159
+ });
160
+ // {{/if}}
161
+ // {{#if ORM=none}}
162
+ const now = new Date();
163
+ const record: LawfulBasisRecord = {
164
+ id: newId(),
165
+ activityName,
166
+ lawfulBasis,
167
+ justification,
168
+ dataCategories,
169
+ purposes,
170
+ assessedBy,
171
+ reviewDate: reviewDate ? new Date(reviewDate) : null,
172
+ createdAt: now,
173
+ updatedAt: now,
174
+ };
175
+ lawfulBasisStore.set(record.id, record);
176
+ auditLog.push({ id: newId(), module: 'lawful-basis', action: 'created', entityId: record.id, at: new Date() });
177
+ // {{/if}}
178
+
179
+ return NextResponse.json(record, { status: 201 });
180
+ }
181
+
182
+ // PUT /api/lawful-basis
183
+ export async function PUT(req: NextRequest) {
184
+ const body = await req.json();
185
+ const { id, ...data } = body;
186
+
187
+ if (!id) {
188
+ return NextResponse.json({ error: 'id is required' }, { status: 400 });
189
+ }
190
+
191
+ if (data.reviewDate) {
192
+ data.reviewDate = new Date(data.reviewDate);
193
+ }
194
+
195
+ // {{#if ORM=prisma}}
196
+ const existing = await prisma.lawfulBasisRecord.findUnique({ where: { id } });
197
+ if (!existing) {
198
+ return NextResponse.json({ error: 'Lawful basis record not found' }, { status: 404 });
199
+ }
200
+
201
+ const record = await prisma.lawfulBasisRecord.update({
202
+ where: { id },
203
+ data,
204
+ });
205
+
206
+ await prisma.complianceAuditLog.create({
207
+ data: {
208
+ module: 'lawful-basis',
209
+ action: 'updated',
210
+ entityId: record.id,
211
+ entityType: 'LawfulBasisRecord',
212
+ changes: data,
213
+ },
214
+ });
215
+ // {{/if}}
216
+ // {{#if ORM=drizzle}}
217
+ const [existing] = await db.select().from(lawfulBasisRecords).where(eq(lawfulBasisRecords.id, id)).limit(1);
218
+ if (!existing) {
219
+ return NextResponse.json({ error: 'Lawful basis record not found' }, { status: 404 });
220
+ }
221
+
222
+ const [record] = await db.update(lawfulBasisRecords).set(data).where(eq(lawfulBasisRecords.id, id)).returning();
223
+
224
+ await db.insert(complianceAuditLog).values({
225
+ module: 'lawful-basis',
226
+ action: 'updated',
227
+ entityId: record.id,
228
+ entityType: 'LawfulBasisRecord',
229
+ changes: data,
230
+ });
231
+ // {{/if}}
232
+ // {{#if ORM=none}}
233
+ const existing = lawfulBasisStore.get(id);
234
+ if (!existing) {
235
+ return NextResponse.json({ error: 'Lawful basis record not found' }, { status: 404 });
236
+ }
237
+ const record: LawfulBasisRecord = { ...existing, ...data, id, updatedAt: new Date() };
238
+ lawfulBasisStore.set(id, record);
239
+ auditLog.push({ id: newId(), module: 'lawful-basis', action: 'updated', entityId: record.id, at: new Date() });
240
+ // {{/if}}
241
+
242
+ return NextResponse.json(record);
243
+ }
244
+
245
+ // DELETE /api/lawful-basis?id=xxx
246
+ export async function DELETE(req: NextRequest) {
247
+ const id = req.nextUrl.searchParams.get('id');
248
+
249
+ if (!id) {
250
+ return NextResponse.json({ error: 'id is required' }, { status: 400 });
251
+ }
252
+
253
+ // {{#if ORM=prisma}}
254
+ const existing = await prisma.lawfulBasisRecord.findUnique({ where: { id } });
255
+ if (!existing) {
256
+ return NextResponse.json({ error: 'Lawful basis record not found' }, { status: 404 });
257
+ }
258
+
259
+ await prisma.lawfulBasisRecord.delete({ where: { id } });
260
+
261
+ await prisma.complianceAuditLog.create({
262
+ data: {
263
+ module: 'lawful-basis',
264
+ action: 'deleted',
265
+ entityId: id,
266
+ entityType: 'LawfulBasisRecord',
267
+ changes: { activityName: existing.activityName },
268
+ },
269
+ });
270
+ // {{/if}}
271
+ // {{#if ORM=drizzle}}
272
+ const [existing] = await db.select().from(lawfulBasisRecords).where(eq(lawfulBasisRecords.id, id)).limit(1);
273
+ if (!existing) {
274
+ return NextResponse.json({ error: 'Lawful basis record not found' }, { status: 404 });
275
+ }
276
+
277
+ await db.delete(lawfulBasisRecords).where(eq(lawfulBasisRecords.id, id));
278
+
279
+ await db.insert(complianceAuditLog).values({
280
+ module: 'lawful-basis',
281
+ action: 'deleted',
282
+ entityId: id,
283
+ entityType: 'LawfulBasisRecord',
284
+ changes: { activityName: existing.activityName },
285
+ });
286
+ // {{/if}}
287
+ // {{#if ORM=none}}
288
+ const existing = lawfulBasisStore.get(id);
289
+ if (!existing) {
290
+ return NextResponse.json({ error: 'Lawful basis record not found' }, { status: 404 });
291
+ }
292
+ lawfulBasisStore.delete(id);
293
+ auditLog.push({ id: newId(), module: 'lawful-basis', action: 'deleted', entityId: id, at: new Date() });
294
+ // {{/if}}
295
+
296
+ return NextResponse.json({ success: true });
297
+ }