@nextsparkjs/core 0.1.0-beta.169 → 0.1.0-beta.170

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.
@@ -1 +1 @@
1
- {"version":3,"file":"user.actions.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/user.actions.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAMzE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CA2DlD;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAsElD;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAsDrE"}
1
+ {"version":3,"file":"user.actions.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/user.actions.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAMzE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CA2DlD;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAsElD;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAgCrE"}
@@ -114,27 +114,13 @@ async function deleteAccount() {
114
114
  return { success: false, error: "Authentication required" };
115
115
  }
116
116
  const userId = session.user.id;
117
- const { TeamService } = await import("../services/team.service.js");
118
- const ownedTeams = await TeamService.getByOwnerId(userId);
119
- if (ownedTeams.length > 0) {
120
- return {
121
- success: false,
122
- error: "Cannot delete account while owning teams. Transfer ownership or delete teams first."
123
- };
124
- }
125
- await UserService.deleteAllUserMetas(userId, userId);
126
- const { mutateWithRLS } = await import("../db.js");
127
- const result = await mutateWithRLS(
128
- 'DELETE FROM "users" WHERE id = $1',
129
- [userId],
130
- userId
131
- );
132
- if (result.rowCount === 0) {
133
- return { success: false, error: "User not found" };
134
- }
117
+ await UserService.anonymizeAccount(userId);
135
118
  revalidatePath("/");
136
119
  return { success: true };
137
120
  } catch (error) {
121
+ if ((error == null ? void 0 : error.code) === "OWNS_TEAMS") {
122
+ return { success: false, error: error.message };
123
+ }
138
124
  console.error("[deleteAccount] Error:", error);
139
125
  return {
140
126
  success: false,
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/helpers.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@ import { auth } from "../auth.js";
7
7
  import { MetaService } from "../services/meta.service.js";
8
8
  import { ScopeService } from "../services/scope.service.js";
9
9
  import { getChildEntities } from "../entities/queries.js";
10
- import { getCorsOrigins, normalizeOrigin } from "../utils/cors.js";
10
+ import { getCorsOrigins, normalizeOrigin, isOriginAllowed } from "../utils/cors.js";
11
11
  async function validateAndAuthenticateRequest(request) {
12
12
  var _a;
13
13
  const cookieHeader = request.headers.get("cookie");
@@ -271,8 +271,9 @@ async function addCorsHeaders(response, request) {
271
271
  allowedOrigin = normalizedOrigin;
272
272
  } else {
273
273
  const allowedOrigins = getCorsOrigins(config, env);
274
- if (allowedOrigins.includes(normalizedOrigin)) {
275
- allowedOrigin = normalizedOrigin;
274
+ const matched = isOriginAllowed(normalizedOrigin, allowedOrigins);
275
+ if (matched) {
276
+ allowedOrigin = matched;
276
277
  }
277
278
  }
278
279
  }
@@ -281,6 +282,7 @@ async function addCorsHeaders(response, request) {
281
282
  allowedOrigin = allowedOrigins[0] || "http://localhost:3000";
282
283
  }
283
284
  response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
285
+ response.headers.append("Vary", "Origin");
284
286
  response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
285
287
  response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key, x-team-id, x-builder-source");
286
288
  response.headers.set("Access-Control-Allow-Credentials", "true");
@@ -309,12 +311,14 @@ async function wrapAuthHandlerWithCors(handler, request) {
309
311
  allowedOrigin = normalizedOrigin;
310
312
  } else {
311
313
  const allowedOrigins = getCorsOrigins(config, env);
312
- if (allowedOrigins.includes(normalizedOrigin)) {
313
- allowedOrigin = normalizedOrigin;
314
+ const matched = isOriginAllowed(normalizedOrigin, allowedOrigins);
315
+ if (matched) {
316
+ allowedOrigin = matched;
314
317
  }
315
318
  }
316
319
  newResponse.headers.set("Access-Control-Allow-Origin", allowedOrigin);
317
320
  newResponse.headers.set("Access-Control-Allow-Credentials", "true");
321
+ newResponse.headers.append("Vary", "Origin");
318
322
  }
319
323
  return newResponse;
320
324
  }
@@ -189,6 +189,22 @@ export declare class UserService {
189
189
  * await UserService.deleteAllUserMetas('user-id-123', 'current-user-id')
190
190
  */
191
191
  static deleteAllUserMetas(userId: string, currentUserId: string): Promise<void>;
192
+ /**
193
+ * Anonymize (soft-delete) a user account.
194
+ *
195
+ * A hard DELETE of the users row fails under foreign-key constraints (other
196
+ * tables reference the user) and would orphan that history. Anonymizing
197
+ * instead frees the UNIQUE email for re-registration, strips PII, revokes
198
+ * every session and purges stored credentials, while preserving referential
199
+ * integrity. `userId` must come from the caller's session — a user may only
200
+ * anonymize their own account.
201
+ *
202
+ * Throws an Error with `.code === 'OWNS_TEAMS'` when the user still owns
203
+ * teams; the caller must surface that so ownership is transferred first.
204
+ *
205
+ * @param userId - ID of the account to anonymize (own account only)
206
+ */
207
+ static anonymizeAccount(userId: string): Promise<void>;
192
208
  /**
193
209
  * Get metadata for multiple users in bulk (solves N+1 query problem)
194
210
  *
@@ -1 +1 @@
1
- {"version":3,"file":"user.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/user.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"user.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/user.service.ts"],"names":[],"mappings":"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"}
@@ -530,6 +530,52 @@ class UserService {
530
530
  );
531
531
  }
532
532
  }
533
+ /**
534
+ * Anonymize (soft-delete) a user account.
535
+ *
536
+ * A hard DELETE of the users row fails under foreign-key constraints (other
537
+ * tables reference the user) and would orphan that history. Anonymizing
538
+ * instead frees the UNIQUE email for re-registration, strips PII, revokes
539
+ * every session and purges stored credentials, while preserving referential
540
+ * integrity. `userId` must come from the caller's session — a user may only
541
+ * anonymize their own account.
542
+ *
543
+ * Throws an Error with `.code === 'OWNS_TEAMS'` when the user still owns
544
+ * teams; the caller must surface that so ownership is transferred first.
545
+ *
546
+ * @param userId - ID of the account to anonymize (own account only)
547
+ */
548
+ static async anonymizeAccount(userId) {
549
+ if (!userId || userId.trim() === "") {
550
+ throw new Error("User ID is required");
551
+ }
552
+ const { TeamService } = await import("./team.service.js");
553
+ const ownedTeams = await TeamService.getByOwnerId(userId);
554
+ if (ownedTeams.length > 0) {
555
+ const error = new Error(
556
+ "Cannot delete account while owning teams. Transfer ownership or delete teams first."
557
+ );
558
+ error.code = "OWNS_TEAMS";
559
+ throw error;
560
+ }
561
+ await UserService.deleteAllUserMetas(userId, userId);
562
+ const result = await mutateWithRLS(
563
+ `UPDATE "users"
564
+ SET email = 'deleted+' || id || '@deleted.invalid',
565
+ "emailVerified" = false,
566
+ "firstName" = 'Deleted',
567
+ "lastName" = 'account',
568
+ image = NULL
569
+ WHERE id = $1`,
570
+ [userId],
571
+ userId
572
+ );
573
+ if (result.rowCount === 0) {
574
+ throw new Error("User not found");
575
+ }
576
+ await mutateWithRLS('DELETE FROM "session" WHERE "userId" = $1', [userId], userId, { service: true });
577
+ await mutateWithRLS('DELETE FROM "account" WHERE "userId" = $1', [userId], userId, { service: true });
578
+ }
533
579
  /**
534
580
  * Get metadata for multiple users in bulk (solves N+1 query problem)
535
581
  *
@@ -68,4 +68,24 @@ export declare function normalizeCorsEnvironment(env: string): 'development' | '
68
68
  * ```
69
69
  */
70
70
  export declare function getCorsOrigins(config: ApplicationConfig, env?: string): string[];
71
+ /**
72
+ * Check whether a request origin is allowed, supporting wildcard-pattern entries
73
+ * in the allow-list. A `*` in an entry matches exactly ONE host label (no dots),
74
+ * so `https://*.example.app` matches `https://tenant.example.app` but NOT
75
+ * `https://a.b.example.app`, `https://example.app`, or look-alikes like
76
+ * `https://evil-example.app`. Entries without `*` are matched exactly.
77
+ *
78
+ * Returns the CONCRETE request origin to echo back (never the pattern or `*`) so
79
+ * credentialed responses — which cannot use `*` for Access-Control-Allow-Origin —
80
+ * send a valid concrete origin; returns null when nothing matches.
81
+ *
82
+ * Why wildcards: multi-tenant apps serve dynamic per-tenant subdomains that can't
83
+ * be enumerated in config. A single `https://*.<apex>` entry lets a session on
84
+ * the apex be recognized cross-subdomain without listing every tenant origin.
85
+ *
86
+ * @param requestOrigin - The (already normalized) incoming Origin to validate.
87
+ * @param allowedOrigins - The allow-list from getCorsOrigins() (may contain patterns).
88
+ * @returns The origin to echo (the requestOrigin on match), or null on no match.
89
+ */
90
+ export declare function isOriginAllowed(requestOrigin: string, allowedOrigins: string[]): string | null;
71
91
  //# sourceMappingURL=cors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/cors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,GAAG,IAAI,CAAA;AAc7E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAetD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,YAAY,CAkBlF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,iBAAiB,EACzB,GAAG,GAAE,MAA8C,GAClD,MAAM,EAAE,CA+CV"}
1
+ {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/cors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,GAAG,IAAI,CAAA;AAc7E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAetD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,YAAY,CAkBlF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,iBAAiB,EACzB,GAAG,GAAE,MAA8C,GAClD,MAAM,EAAE,CA+CV;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,GAAG,IAAI,CA6Bf"}
@@ -57,8 +57,29 @@ function getCorsOrigins(config, env = process.env.NODE_ENV || "development") {
57
57
  }
58
58
  return origins;
59
59
  }
60
+ function escapeRegExp(value) {
61
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62
+ }
63
+ function isOriginAllowed(requestOrigin, allowedOrigins) {
64
+ if (!requestOrigin) return null;
65
+ for (const entry of allowedOrigins) {
66
+ if (!entry) continue;
67
+ if (!entry.includes("*")) {
68
+ if (entry === requestOrigin) return requestOrigin;
69
+ continue;
70
+ }
71
+ if (!entry.includes("://")) continue;
72
+ const pattern = "^" + escapeRegExp(entry).replace(/(?:\\\*)+/g, "[^.]+") + "$";
73
+ try {
74
+ if (new RegExp(pattern).test(requestOrigin)) return requestOrigin;
75
+ } catch {
76
+ }
77
+ }
78
+ return null;
79
+ }
60
80
  export {
61
81
  getCorsOrigins,
82
+ isOriginAllowed,
62
83
  normalizeCorsEnvironment,
63
84
  normalizeOrigin
64
85
  };
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated": "2026-06-21T18:23:36.275Z",
2
+ "generated": "2026-06-21T21:50:46.106Z",
3
3
  "totalClasses": 1081,
4
4
  "classes": [
5
5
  "!text-2xl",
@@ -1,7 +1,7 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { auth } from "@nextsparkjs/core/lib/auth";
3
- import { mutateWithRLS } from "@nextsparkjs/core/lib/db";
4
3
  import { withRateLimitTier } from "@nextsparkjs/core/lib/api/rate-limit";
4
+ import { UserService } from "@nextsparkjs/core/lib/services";
5
5
 
6
6
  export const DELETE = withRateLimitTier(async (req: NextRequest) => {
7
7
  try {
@@ -17,35 +17,37 @@ export const DELETE = withRateLimitTier(async (req: NextRequest) => {
17
17
  const userId = session.user.id;
18
18
 
19
19
  try {
20
- // Sign out the user first (while the user still exists)
20
+ // Anonymize the account: scrubs user metadata, frees the UNIQUE email,
21
+ // strips PII, and revokes every session + stored credential. A hard
22
+ // DELETE fails under foreign-key constraints; anonymizing preserves
23
+ // referential integrity. Throws code 'OWNS_TEAMS' if the user still
24
+ // owns teams (ownership must be transferred or those teams deleted first).
25
+ await UserService.anonymizeAccount(userId);
26
+
27
+ // Sessions are already revoked server-side; clear the current session
28
+ // cookie on this device too.
21
29
  try {
22
- await auth.api.signOut({
23
- headers: req.headers,
24
- });
30
+ await auth.api.signOut({ headers: req.headers });
25
31
  } catch (signOutError) {
26
- console.warn("Sign out failed (user may already be signed out):", signOutError);
32
+ console.warn("Sign out after account deletion failed (already revoked):", signOutError);
27
33
  }
28
34
 
29
- // Delete user account and all associated data
30
- // RLS policies will ensure only the user's own data is deleted
31
- await mutateWithRLS(
32
- 'DELETE FROM "users" WHERE id = $1',
33
- [userId],
34
- userId
35
- );
36
-
37
- return NextResponse.json({
38
- message: "Account deleted successfully"
35
+ return NextResponse.json({
36
+ message: "Account deleted successfully",
39
37
  });
40
-
41
38
  } catch (dbError) {
39
+ if ((dbError as { code?: string })?.code === "OWNS_TEAMS") {
40
+ return NextResponse.json(
41
+ { error: (dbError as Error).message },
42
+ { status: 409 }
43
+ );
44
+ }
42
45
  console.error("Failed to delete user account:", dbError);
43
46
  return NextResponse.json(
44
47
  { error: "Failed to delete account" },
45
48
  { status: 500 }
46
49
  );
47
50
  }
48
-
49
51
  } catch (error) {
50
52
  console.error("Delete account error:", error);
51
53
  return NextResponse.json(
@@ -116,21 +116,15 @@ export const GET = withRateLimitTier(withApiLogging(async (
116
116
  // Get user metadata value using UserService
117
117
  const metaValue = await UserService.getUserMeta(id, key, authResult.user.id)
118
118
 
119
- // Return 404 if metadata key doesn't exist
120
- if (metaValue === null || metaValue === undefined) {
121
- const response = createApiError(
122
- `Metadata key '${key}' not found for user`,
123
- 404,
124
- null,
125
- 'META_NOT_FOUND'
126
- )
127
- return addCorsHeaders(response)
128
- }
129
-
130
- // Return metadata value
119
+ // Return 200 with value:null for an unset-but-valid key. A missing optional
120
+ // meta value is a normal state, not an error — a 404 here forces every
121
+ // client that reads an optional key to swallow a network 4xx (which the
122
+ // browser still logs as a failed request). The key itself was already
123
+ // validated above (INVALID_META_KEY), so reaching here means the key is
124
+ // valid and simply has no stored value yet.
131
125
  const response = createApiResponse({
132
126
  key,
133
- value: metaValue,
127
+ value: metaValue ?? null,
134
128
  })
135
129
  return addCorsHeaders(response)
136
130
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextsparkjs/core",
3
- "version": "0.1.0-beta.169",
3
+ "version": "0.1.0-beta.170",
4
4
  "description": "NextSpark - The complete SaaS framework for Next.js",
5
5
  "license": "MIT",
6
6
  "author": "NextSpark <hello@nextspark.dev>",
@@ -469,7 +469,7 @@
469
469
  "tailwind-merge": "^3.3.1",
470
470
  "uuid": "^13.0.0",
471
471
  "zod": "^4.1.5",
472
- "@nextsparkjs/testing": "0.1.0-beta.169"
472
+ "@nextsparkjs/testing": "0.1.0-beta.170"
473
473
  },
474
474
  "scripts": {
475
475
  "postinstall": "node scripts/postinstall.mjs || true",
@@ -1,7 +1,7 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { auth } from "@nextsparkjs/core/lib/auth";
3
- import { mutateWithRLS } from "@nextsparkjs/core/lib/db";
4
3
  import { withRateLimitTier } from "@nextsparkjs/core/lib/api/rate-limit";
4
+ import { UserService } from "@nextsparkjs/core/lib/services";
5
5
 
6
6
  export const DELETE = withRateLimitTier(async (req: NextRequest) => {
7
7
  try {
@@ -17,35 +17,37 @@ export const DELETE = withRateLimitTier(async (req: NextRequest) => {
17
17
  const userId = session.user.id;
18
18
 
19
19
  try {
20
- // Sign out the user first (while the user still exists)
20
+ // Anonymize the account: scrubs user metadata, frees the UNIQUE email,
21
+ // strips PII, and revokes every session + stored credential. A hard
22
+ // DELETE fails under foreign-key constraints; anonymizing preserves
23
+ // referential integrity. Throws code 'OWNS_TEAMS' if the user still
24
+ // owns teams (ownership must be transferred or those teams deleted first).
25
+ await UserService.anonymizeAccount(userId);
26
+
27
+ // Sessions are already revoked server-side; clear the current session
28
+ // cookie on this device too.
21
29
  try {
22
- await auth.api.signOut({
23
- headers: req.headers,
24
- });
30
+ await auth.api.signOut({ headers: req.headers });
25
31
  } catch (signOutError) {
26
- console.warn("Sign out failed (user may already be signed out):", signOutError);
32
+ console.warn("Sign out after account deletion failed (already revoked):", signOutError);
27
33
  }
28
34
 
29
- // Delete user account and all associated data
30
- // RLS policies will ensure only the user's own data is deleted
31
- await mutateWithRLS(
32
- 'DELETE FROM "users" WHERE id = $1',
33
- [userId],
34
- userId
35
- );
36
-
37
- return NextResponse.json({
38
- message: "Account deleted successfully"
35
+ return NextResponse.json({
36
+ message: "Account deleted successfully",
39
37
  });
40
-
41
38
  } catch (dbError) {
39
+ if ((dbError as { code?: string })?.code === "OWNS_TEAMS") {
40
+ return NextResponse.json(
41
+ { error: (dbError as Error).message },
42
+ { status: 409 }
43
+ );
44
+ }
42
45
  console.error("Failed to delete user account:", dbError);
43
46
  return NextResponse.json(
44
47
  { error: "Failed to delete account" },
45
48
  { status: 500 }
46
49
  );
47
50
  }
48
-
49
51
  } catch (error) {
50
52
  console.error("Delete account error:", error);
51
53
  return NextResponse.json(
@@ -116,21 +116,15 @@ export const GET = withRateLimitTier(withApiLogging(async (
116
116
  // Get user metadata value using UserService
117
117
  const metaValue = await UserService.getUserMeta(id, key, authResult.user.id)
118
118
 
119
- // Return 404 if metadata key doesn't exist
120
- if (metaValue === null || metaValue === undefined) {
121
- const response = createApiError(
122
- `Metadata key '${key}' not found for user`,
123
- 404,
124
- null,
125
- 'META_NOT_FOUND'
126
- )
127
- return addCorsHeaders(response)
128
- }
129
-
130
- // Return metadata value
119
+ // Return 200 with value:null for an unset-but-valid key. A missing optional
120
+ // meta value is a normal state, not an error — a 404 here forces every
121
+ // client that reads an optional key to swallow a network 4xx (which the
122
+ // browser still logs as a failed request). The key itself was already
123
+ // validated above (INVALID_META_KEY), so reaching here means the key is
124
+ // valid and simply has no stored value yet.
131
125
  const response = createApiResponse({
132
126
  key,
133
- value: metaValue,
127
+ value: metaValue ?? null,
134
128
  })
135
129
  return addCorsHeaders(response)
136
130
  } catch (error) {