@nextsparkjs/core 0.1.0-beta.193 → 0.1.0-beta.194
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.
- package/README.md +2 -2
- package/dist/lib/api/auth/permissions.d.ts +10 -0
- package/dist/lib/api/auth/permissions.d.ts.map +1 -1
- package/dist/lib/api/auth/permissions.js +7 -0
- package/dist/lib/api/rate-limit.d.ts.map +1 -1
- package/dist/lib/api/rate-limit.js +3 -0
- package/dist/lib/api/request-origin.d.ts +26 -0
- package/dist/lib/api/request-origin.d.ts.map +1 -0
- package/dist/lib/api/request-origin.js +46 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +8 -12
- package/dist/lib/entities/external-api-generator.d.ts.map +1 -1
- package/dist/lib/entities/external-api-generator.js +3 -1
- package/dist/lib/services/subscription.service.d.ts.map +1 -1
- package/dist/lib/services/subscription.service.js +3 -0
- package/dist/lib/utils/cors.d.ts +7 -0
- package/dist/lib/utils/cors.d.ts.map +1 -1
- package/dist/lib/utils/cors.js +8 -0
- package/dist/routes/api/v1/users/[id]/meta/[key]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/users/[id]/meta/[key]/route.js +13 -0
- package/dist/routes/api/v1/users/[id]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/users/[id]/route.js +17 -0
- package/dist/templates/projects/blog/package.json +3 -3
- package/dist/templates/projects/crm/package.json +3 -3
- package/dist/templates/projects/productivity/package.json +3 -3
- package/package.json +8 -5
- package/scripts/build/auth-readiness.mjs +4 -0
- package/scripts/build/registry/__tests__/build-output.test.mjs +1 -1
- package/scripts/build/registry/__tests__/project-mode-root-first.test.mjs +6 -6
- package/scripts/build/registry/__tests__/write-places.test.mjs +1 -1
- package/scripts/build/registry/contracts/__tests__/contracts-wire.test.mjs +1 -1
- package/scripts/build/registry/contracts/__tests__/contracts.test.mjs +1 -1
- package/scripts/build/registry/contracts/render.mjs +3 -3
- package/scripts/build/registry/host/README.md +2 -2
- package/scripts/build/registry/host/__tests__/facade-emitter.test.mjs +9 -9
- package/scripts/build/registry/host/__tests__/host-mode.test.mjs +1 -1
- package/scripts/build/registry/host/__tests__/host-project-composition.test.mjs +1 -1
- package/scripts/build/registry/host/__tests__/host-project.test.mjs +2 -2
- package/scripts/build/registry/host/__tests__/host-robustness.test.mjs +65 -2
- package/scripts/build/registry/host/facade-emitter.mjs +2 -2
- package/scripts/build/registry/host/generation.mjs +54 -0
- package/scripts/build/registry/host/next-route-exports.json +2 -2
- package/scripts/build/registry/host/prepare.mjs +3 -0
- package/scripts/db/migration-time-limit.mjs +216 -34
- package/scripts/db/run-migrations.mjs +11 -179
- package/scripts/db/verify-theme-migrations.mjs +1 -1
- package/templates/projects/blog/package.json +3 -3
- package/templates/projects/crm/package.json +3 -3
- package/templates/projects/productivity/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,8 +5,6 @@ The complete SaaS framework for Next.js. Build production-ready SaaS application
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @nextsparkjs/core
|
|
9
|
-
# or
|
|
10
8
|
pnpm add @nextsparkjs/core
|
|
11
9
|
```
|
|
12
10
|
|
|
@@ -16,6 +14,8 @@ pnpm add @nextsparkjs/core
|
|
|
16
14
|
# Create a new NextSpark project
|
|
17
15
|
npx create-nextspark-app my-app
|
|
18
16
|
cd my-app
|
|
17
|
+
# set DATABASE_URL in .env (BETTER_AUTH_SECRET is already generated)
|
|
18
|
+
pnpm db:migrate
|
|
19
19
|
pnpm dev
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -27,4 +27,14 @@ export declare function isSuperAdmin(authResult: DualAuthResult): boolean;
|
|
|
27
27
|
* @returns true si tiene permisos de admin
|
|
28
28
|
*/
|
|
29
29
|
export declare function hasAdminPermission(authResult: DualAuthResult, requiredScope?: string | string[]): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the caller may read or edit the user `target` (an id or an email, as
|
|
32
|
+
* the /users/:id routes accept both): the user themselves, or a superadmin.
|
|
33
|
+
* An API key is its owner, so it gets the same answer, but a superadmin key
|
|
34
|
+
* must also carry `requiredScope`.
|
|
35
|
+
*
|
|
36
|
+
* Application-layer check: row-level security does not cover a deployment that
|
|
37
|
+
* has not cut over to the non-owner runtime role.
|
|
38
|
+
*/
|
|
39
|
+
export declare function canAccessUser(authResult: DualAuthResult, target: string, requiredScope?: string | string[]): boolean;
|
|
30
40
|
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/permissions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAOhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,cAAc,EAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAChC,OAAO,CAYT"}
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/permissions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAOhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,cAAc,EAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAChC,OAAO,CAYT;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,cAAc,EAC1B,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAChC,OAAO,CAOT"}
|
|
@@ -14,7 +14,14 @@ function hasAdminPermission(authResult, requiredScope) {
|
|
|
14
14
|
}
|
|
15
15
|
return true;
|
|
16
16
|
}
|
|
17
|
+
function canAccessUser(authResult, target, requiredScope) {
|
|
18
|
+
if (!authResult.success || !authResult.user) return false;
|
|
19
|
+
if (hasAdminPermission(authResult, requiredScope)) return true;
|
|
20
|
+
const { id, email } = authResult.user;
|
|
21
|
+
return target === id || !!email && target === email;
|
|
22
|
+
}
|
|
17
23
|
export {
|
|
24
|
+
canAccessUser,
|
|
18
25
|
hasAdminPermission,
|
|
19
26
|
isSuperAdmin
|
|
20
27
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/lib/api/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/lib/api/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,MAAa,EACpB,QAAQ,GAAE,MAAc,GACvB,eAAe,CAyCjB;AAED;;;GAGG;AACH,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,aAAqB,GAC1B,OAAO,CAAC,eAAe,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA6CpD;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,eAAe,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAwB5G;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,IAAI,OAAO,CAEzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,GACf,YAAY,GAAG,IAAI,CAkCrB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,GACf,YAAY,CASd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,OAAO,EAAE,EAC/C,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,IAEtD,SAAS,WAAW,EAAE,GAAG,MAAM,CAAC,KAAG,OAAO,CAAC,YAAY,CAAC,CAwCvE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG,IAAI,CAaP;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAGlD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,KAAK,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CASD;AAED;;GAEG;AACH,wBAAgB,sBAAsB;;;;;EAErC;AAsGD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,OAAO,EAAE,EACnD,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,EACpE,IAAI,GAAE,aAAqB,IAEb,SAAS,WAAW,EAAE,GAAG,MAAM,CAAC,KAAG,OAAO,CAAC,YAAY,CAAC,CAsCvE"}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
checkRateLimit as checkRedisRateLimit,
|
|
7
7
|
maybeRedisConfigured
|
|
8
8
|
} from "../rate-limit-redis.js";
|
|
9
|
+
import { checkRequestOrigin } from "./request-origin.js";
|
|
9
10
|
function checkRateLimit(identifier, limit = 1e3, windowMs = 6e4) {
|
|
10
11
|
const now = Date.now();
|
|
11
12
|
const cacheKey = getCacheKey("rate_limit", identifier);
|
|
@@ -239,6 +240,8 @@ function isRateLimitingDisabled() {
|
|
|
239
240
|
}
|
|
240
241
|
function withRateLimitTier(handler, tier = "api") {
|
|
241
242
|
return async (request, ...args) => {
|
|
243
|
+
const originRefused = checkRequestOrigin(request);
|
|
244
|
+
if (originRefused) return originRefused;
|
|
242
245
|
if (isRateLimitingDisabled()) {
|
|
243
246
|
return handler(request, ...args);
|
|
244
247
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request origin check for cookie-authenticated writes.
|
|
3
|
+
*
|
|
4
|
+
* A POST/PUT/PATCH/DELETE that authenticates with the session cookie must come
|
|
5
|
+
* from an origin the app trusts: its own (NEXT_PUBLIC_APP_URL / BETTER_AUTH_URL)
|
|
6
|
+
* or one in the list Better Auth already trusts for sign-in (see
|
|
7
|
+
* getTrustedOrigins). withRateLimitTier runs it before every handler it wraps.
|
|
8
|
+
*
|
|
9
|
+
* What passes without a trusted origin, and why:
|
|
10
|
+
* - Reads (GET/HEAD/OPTIONS).
|
|
11
|
+
* - Requests without the session cookie: there is no session for them to use.
|
|
12
|
+
* - Requests with an `Authorization: Bearer` token or an x-api-key header (API
|
|
13
|
+
* keys, the mobile client's bearer token). A browser adds those only when the page's script
|
|
14
|
+
* sets them, and a script on another origin needs CORS approval first.
|
|
15
|
+
* - Requests with neither Origin nor Referer, unless their body is one an HTML
|
|
16
|
+
* form can send. Browsers send Origin on every write, so these come from
|
|
17
|
+
* native or server-side clients; requiring a non-form content type keeps
|
|
18
|
+
* a body without origin information from ever being read as a form post.
|
|
19
|
+
*/
|
|
20
|
+
import { NextResponse } from 'next/server';
|
|
21
|
+
/**
|
|
22
|
+
* Returns a 403 response when a cookie-authenticated write does not come from a
|
|
23
|
+
* trusted origin, or null when the request may proceed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkRequestOrigin(request: Request): NextResponse | null;
|
|
26
|
+
//# sourceMappingURL=request-origin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-origin.d.ts","sourceRoot":"","sources":["../../../src/lib/api/request-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AA6B1C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAsBxE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { APP_CONFIG_MERGED } from "../config/index.js";
|
|
3
|
+
import { hasSessionCookie } from "../auth/session-hint.js";
|
|
4
|
+
import { getTrustedOrigins, isOriginAllowed, normalizeOrigin } from "../utils/cors.js";
|
|
5
|
+
const WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
6
|
+
const FORM_CONTENT_TYPES = /* @__PURE__ */ new Set(["text/plain", "application/x-www-form-urlencoded", "multipart/form-data"]);
|
|
7
|
+
function requestOrigin(headers) {
|
|
8
|
+
const origin = headers.get("origin");
|
|
9
|
+
if (origin) return origin;
|
|
10
|
+
const referer = headers.get("referer");
|
|
11
|
+
if (!referer) return null;
|
|
12
|
+
try {
|
|
13
|
+
return new URL(referer).origin;
|
|
14
|
+
} catch {
|
|
15
|
+
return "null";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function hasHeaderCredential(headers) {
|
|
19
|
+
var _a;
|
|
20
|
+
if ((_a = headers.get("x-api-key")) == null ? void 0 : _a.trim()) return true;
|
|
21
|
+
return /^bearer\s+\S/i.test(headers.get("authorization") ?? "");
|
|
22
|
+
}
|
|
23
|
+
function checkRequestOrigin(request) {
|
|
24
|
+
if (!WRITE_METHODS.has(request.method.toUpperCase())) return null;
|
|
25
|
+
const headers = request.headers;
|
|
26
|
+
if (!hasSessionCookie(headers.get("cookie"))) return null;
|
|
27
|
+
if (hasHeaderCredential(headers)) return null;
|
|
28
|
+
const origin = requestOrigin(headers);
|
|
29
|
+
if (origin === null) {
|
|
30
|
+
const contentType = (headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
|
|
31
|
+
if (!FORM_CONTENT_TYPES.has(contentType)) return null;
|
|
32
|
+
return NextResponse.json(
|
|
33
|
+
{ success: false, error: "This request needs an Origin header", code: "ORIGIN_REQUIRED" },
|
|
34
|
+
{ status: 403 }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const normalized = normalizeOrigin(origin);
|
|
38
|
+
if (isOriginAllowed(normalized, getTrustedOrigins(APP_CONFIG_MERGED, normalized))) return null;
|
|
39
|
+
return NextResponse.json(
|
|
40
|
+
{ success: false, error: "Request origin not allowed", code: "ORIGIN_NOT_ALLOWED" },
|
|
41
|
+
{ status: 403 }
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
checkRequestOrigin
|
|
46
|
+
};
|
package/dist/lib/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAUA,OAAO,EAAgF,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAkBvH;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQlF;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsHD,eAAO,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAUA,OAAO,EAAgF,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAkBvH;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQlF;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsHD,eAAO,MAAM,IAAI,qEA8Wf,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,kBAAkB,EAAE,QAAQ,EAAE,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5B,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAO9B"}
|
package/dist/lib/auth.js
CHANGED
|
@@ -19,7 +19,7 @@ import { resolveSessionConfig } from "./auth/session-config.js";
|
|
|
19
19
|
import { resolveOtpConfig } from "./auth/otp-config.js";
|
|
20
20
|
import { isPasswordLoginEnabled } from "./auth/auth-methods.js";
|
|
21
21
|
import { isRuntimeEmailAvailable, isRuntimeGoogleAvailable } from "./auth/runtime-readiness.js";
|
|
22
|
-
import {
|
|
22
|
+
import { getTrustedOrigins } from "./utils/cors.js";
|
|
23
23
|
import { withBasePath } from "./base-path.js";
|
|
24
24
|
async function hasPendingInvitationForEmail(email) {
|
|
25
25
|
const row = await queryOne(
|
|
@@ -206,17 +206,7 @@ const auth = betterAuth({
|
|
|
206
206
|
// adding a wildcard entry (e.g. "https://*.vercel.app") to
|
|
207
207
|
// api.cors.additionalOrigins — better-auth's own trustedOrigins matching
|
|
208
208
|
// already supports the same wildcard syntax as isOriginAllowed() above.
|
|
209
|
-
trustedOrigins: async (request) =>
|
|
210
|
-
const origins = getCorsOrigins(APP_CONFIG_MERGED);
|
|
211
|
-
if (normalizeCorsEnvironment(process.env.NODE_ENV || "development") === "production") {
|
|
212
|
-
return origins;
|
|
213
|
-
}
|
|
214
|
-
const requestOrigin = request == null ? void 0 : request.headers.get("origin");
|
|
215
|
-
if (requestOrigin && isPrivateLanOrigin(requestOrigin)) {
|
|
216
|
-
return [...origins, requestOrigin];
|
|
217
|
-
}
|
|
218
|
-
return origins;
|
|
219
|
-
},
|
|
209
|
+
trustedOrigins: async (request) => getTrustedOrigins(APP_CONFIG_MERGED, request == null ? void 0 : request.headers.get("origin")),
|
|
220
210
|
// Redirect auth errors to our custom error page instead of Better Auth's default.
|
|
221
211
|
// Better Auth redirects to this path as-is, outside the Next.js router.
|
|
222
212
|
onAPIError: {
|
|
@@ -249,6 +239,12 @@ const auth = betterAuth({
|
|
|
249
239
|
nextCookies()
|
|
250
240
|
// MUST be the last plugin for Next.js cookie handling
|
|
251
241
|
],
|
|
242
|
+
// The proxy reads the session on every request; that lookup only reads the
|
|
243
|
+
// caller's own signed cookie, so it is kept out of the shared auth rate limit.
|
|
244
|
+
// The default limits (sign-in, sign-up, OTP, ...) stay.
|
|
245
|
+
rateLimit: {
|
|
246
|
+
customRules: { "/get-session": false }
|
|
247
|
+
},
|
|
252
248
|
session: {
|
|
253
249
|
// Configurable per theme via `auth.session` in app.config.ts (defaults:
|
|
254
250
|
// 7 days / renewed daily / 5-minute cookie cache).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-api-generator.d.ts","sourceRoot":"","sources":["../../../src/lib/entities/external-api-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAqB,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAK3C,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,eAAe,CAAC,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,EAAE,MAAM,CAAA;KACpB,CAAA;IACD,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,OAAO,EAAE,MAAM,EAAE,CAAA;KAClB,CAAA;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,YAAY,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB;mBAqFT,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;oBAAnD,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;qBAAnD,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;sBAAnD,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;uBAkGnD,WAAW;EAnHnC;
|
|
1
|
+
{"version":3,"file":"external-api-generator.d.ts","sourceRoot":"","sources":["../../../src/lib/entities/external-api-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAqB,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAK3C,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,eAAe,CAAC,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,EAAE,MAAM,CAAA;KACpB,CAAA;IACD,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,OAAO,EAAE,MAAM,EAAE,CAAA;KAClB,CAAA;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,YAAY,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB;mBAqFT,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;oBAAnD,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;qBAAnD,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;sBAAnD,WAAW,YAAY;QAAE,MAAM,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;uBAkGnD,WAAW;EAnHnC;AA4KD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoMrD"}
|
|
@@ -153,7 +153,9 @@ function addCORSHeaders(response, corsOptions, request) {
|
|
|
153
153
|
response.headers.set("Access-Control-Allow-Methods", corsOptions.methods.join(", "));
|
|
154
154
|
response.headers.set("Access-Control-Allow-Headers", corsOptions.headers.join(", "));
|
|
155
155
|
response.headers.set("Access-Control-Max-Age", "86400");
|
|
156
|
-
|
|
156
|
+
if (origin && corsOptions.origins.includes(origin)) {
|
|
157
|
+
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
158
|
+
}
|
|
157
159
|
}
|
|
158
160
|
function createErrorResponse(status, message, details) {
|
|
159
161
|
return NextResponse.json({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/subscription.service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"subscription.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/subscription.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -810,6 +810,9 @@ class SubscriptionService {
|
|
|
810
810
|
console.warn("[SubscriptionService] Team permissions module not available");
|
|
811
811
|
}
|
|
812
812
|
}
|
|
813
|
+
if (BILLING_REGISTRY.plans.length === 0) {
|
|
814
|
+
return { allowed: true };
|
|
815
|
+
}
|
|
813
816
|
const requiredFeature = BILLING_REGISTRY.actionMappings.features[action];
|
|
814
817
|
if (requiredFeature) {
|
|
815
818
|
const hasFeatureAccess = await this.hasFeature(teamId, requiredFeature, userId);
|
package/dist/lib/utils/cors.d.ts
CHANGED
|
@@ -81,6 +81,13 @@ export declare function getCorsOrigins(config: ApplicationConfig, env?: string):
|
|
|
81
81
|
* production trust — pair with an environment check.
|
|
82
82
|
*/
|
|
83
83
|
export declare function isPrivateLanOrigin(origin: string): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* The origins trusted for cookie-authenticated requests: Better Auth's
|
|
86
|
+
* trustedOrigins and the write-origin check (lib/api/request-origin) share it.
|
|
87
|
+
* getCorsOrigins() plus, outside production, the request's own origin when it is
|
|
88
|
+
* a private-network address (#170).
|
|
89
|
+
*/
|
|
90
|
+
export declare function getTrustedOrigins(config: ApplicationConfig, requestOrigin?: string | null, env?: string): string[];
|
|
84
91
|
/**
|
|
85
92
|
* Check whether a request origin is allowed, supporting wildcard-pattern entries
|
|
86
93
|
* in the allow-list. A `*` in an entry matches exactly ONE host label (no dots),
|
|
@@ -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;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAqB1D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,GAAG,IAAI,CA6Bf"}
|
|
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;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAqB1D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,iBAAiB,EACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,EAC7B,GAAG,GAAE,MAA8C,GAClD,MAAM,EAAE,CAMV;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,GAAG,IAAI,CA6Bf"}
|
package/dist/lib/utils/cors.js
CHANGED
|
@@ -79,6 +79,13 @@ function isPrivateLanOrigin(origin) {
|
|
|
79
79
|
if (a === 169 && b === 254) return true;
|
|
80
80
|
return false;
|
|
81
81
|
}
|
|
82
|
+
function getTrustedOrigins(config, requestOrigin, env = process.env.NODE_ENV || "development") {
|
|
83
|
+
const origins = getCorsOrigins(config, env);
|
|
84
|
+
if (normalizeCorsEnvironment(env) !== "production" && requestOrigin && isPrivateLanOrigin(requestOrigin)) {
|
|
85
|
+
return [...origins, requestOrigin];
|
|
86
|
+
}
|
|
87
|
+
return origins;
|
|
88
|
+
}
|
|
82
89
|
function isOriginAllowed(requestOrigin, allowedOrigins) {
|
|
83
90
|
if (!requestOrigin) return null;
|
|
84
91
|
for (const entry of allowedOrigins) {
|
|
@@ -98,6 +105,7 @@ function isOriginAllowed(requestOrigin, allowedOrigins) {
|
|
|
98
105
|
}
|
|
99
106
|
export {
|
|
100
107
|
getCorsOrigins,
|
|
108
|
+
getTrustedOrigins,
|
|
101
109
|
isOriginAllowed,
|
|
102
110
|
isPrivateLanOrigin,
|
|
103
111
|
normalizeCorsEnvironment,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/routes/api/v1/users/[id]/meta/[key]/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/routes/api/v1/users/[id]/meta/[key]/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAuBvD,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,kCAEjD;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,GAAG;YAEQ,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;2BAmEjD,CAAC;AAEZ;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,GAAG;YAEQ,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;2BAoGhD,CAAC;AAEb;;;;GAIG;AACH,eAAO,MAAM,KAAK;YA3GM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;2BA2GpC,CAAA;AAExB;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;YAEK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;2BA8DhD,CAAC"}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
addCorsHeaders
|
|
8
8
|
} from "@nextsparkjs/core/lib/api/helpers";
|
|
9
9
|
import { authenticateRequest, createAuthFailureResponse } from "@nextsparkjs/core/lib/api/auth/dual-auth";
|
|
10
|
+
import { canAccessUser } from "@nextsparkjs/core/lib/api/auth/permissions";
|
|
10
11
|
import * as z from "zod";
|
|
11
12
|
import { withRateLimitTier } from "@nextsparkjs/core/lib/api/rate-limit";
|
|
12
13
|
const metadataValueSchema = z.object({
|
|
@@ -33,6 +34,10 @@ export const GET = withRateLimitTier(withApiLogging(async (req, { params }) => {
|
|
|
33
34
|
const response2 = createApiError("User ID is required", 400, null, "MISSING_USER_ID");
|
|
34
35
|
return addCorsHeaders(response2, req);
|
|
35
36
|
}
|
|
37
|
+
if (!canAccessUser(authResult, id, "users:read")) {
|
|
38
|
+
const response2 = createApiError("Insufficient permissions", 403, null, "FORBIDDEN");
|
|
39
|
+
return addCorsHeaders(response2, req);
|
|
40
|
+
}
|
|
36
41
|
if (!key || key.trim() === "") {
|
|
37
42
|
const response2 = createApiError("Metadata key is required", 400, null, "MISSING_META_KEY");
|
|
38
43
|
return addCorsHeaders(response2, req);
|
|
@@ -75,6 +80,10 @@ export const PUT = withRateLimitTier(withApiLogging(async (req, { params }) => {
|
|
|
75
80
|
const response2 = createApiError("User ID is required", 400, null, "MISSING_USER_ID");
|
|
76
81
|
return addCorsHeaders(response2, req);
|
|
77
82
|
}
|
|
83
|
+
if (!canAccessUser(authResult, id, "users:write")) {
|
|
84
|
+
const response2 = createApiError("Insufficient permissions", 403, null, "FORBIDDEN");
|
|
85
|
+
return addCorsHeaders(response2, req);
|
|
86
|
+
}
|
|
78
87
|
if (!key || key.trim() === "") {
|
|
79
88
|
const response2 = createApiError("Metadata key is required", 400, null, "MISSING_META_KEY");
|
|
80
89
|
return addCorsHeaders(response2, req);
|
|
@@ -150,6 +159,10 @@ export const DELETE = withRateLimitTier(withApiLogging(async (req, { params }) =
|
|
|
150
159
|
const response2 = createApiError("User ID is required", 400, null, "MISSING_USER_ID");
|
|
151
160
|
return addCorsHeaders(response2, req);
|
|
152
161
|
}
|
|
162
|
+
if (!canAccessUser(authResult, id, "users:write")) {
|
|
163
|
+
const response2 = createApiError("Insufficient permissions", 403, null, "FORBIDDEN");
|
|
164
|
+
return addCorsHeaders(response2, req);
|
|
165
|
+
}
|
|
153
166
|
if (!key || key.trim() === "") {
|
|
154
167
|
const response2 = createApiError("Metadata key is required", 400, null, "MISSING_META_KEY");
|
|
155
168
|
return addCorsHeaders(response2, req);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/routes/api/v1/users/[id]/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/routes/api/v1/users/[id]/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA2BxD,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,kCAEjD;AAGD,eAAO,MAAM,GAAG;YAEQ,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;2BAoDpC,CAAC;AAGZ,eAAO,MAAM,KAAK;YAEM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;2BAqInC,CAAC;AAGb,eAAO,MAAM,MAAM;YAEK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;2BAiEnC,CAAC"}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
processEntityMetadata
|
|
12
12
|
} from "@nextsparkjs/core/lib/api/helpers";
|
|
13
13
|
import { authenticateRequest, createAuthFailureResponse } from "@nextsparkjs/core/lib/api/auth/dual-auth";
|
|
14
|
+
import { canAccessUser, hasAdminPermission } from "@nextsparkjs/core/lib/api/auth/permissions";
|
|
14
15
|
import * as z from "zod";
|
|
15
16
|
import { withRateLimitTier } from "@nextsparkjs/core/lib/api/rate-limit";
|
|
16
17
|
const updateUserSchema = z.object({
|
|
@@ -37,6 +38,10 @@ export const GET = withRateLimitTier(withApiLogging(async (req, { params }) => {
|
|
|
37
38
|
const response2 = createApiError("User ID or email is required", 400, null, "MISSING_IDENTIFIER");
|
|
38
39
|
return addCorsHeaders(response2, req);
|
|
39
40
|
}
|
|
41
|
+
if (!canAccessUser(authResult, id, "users:read")) {
|
|
42
|
+
const response2 = createApiError("Insufficient permissions", 403, null, "FORBIDDEN");
|
|
43
|
+
return addCorsHeaders(response2, req);
|
|
44
|
+
}
|
|
40
45
|
const user = await queryOneWithRLS(
|
|
41
46
|
'SELECT id, email, name, "firstName", "lastName", image, country, timezone, language, role, "emailVerified", "createdAt", "updatedAt" FROM "users" WHERE id = $1 OR email = $1',
|
|
42
47
|
[id],
|
|
@@ -70,9 +75,17 @@ export const PATCH = withRateLimitTier(withApiLogging(async (req, { params }) =>
|
|
|
70
75
|
const response2 = createApiError("User ID or email is required", 400, null, "MISSING_IDENTIFIER");
|
|
71
76
|
return addCorsHeaders(response2, req);
|
|
72
77
|
}
|
|
78
|
+
if (!canAccessUser(authResult, id, "users:write")) {
|
|
79
|
+
const response2 = createApiError("Insufficient permissions", 403, null, "FORBIDDEN");
|
|
80
|
+
return addCorsHeaders(response2, req);
|
|
81
|
+
}
|
|
73
82
|
const body = await req.json();
|
|
74
83
|
const { metas, ...userData } = body;
|
|
75
84
|
const validatedData = updateUserSchema.parse(userData);
|
|
85
|
+
if (validatedData.role !== void 0 && !hasAdminPermission(authResult, "users:write")) {
|
|
86
|
+
const response2 = createApiError("Only a superadmin can change a role", 403, null, "FORBIDDEN");
|
|
87
|
+
return addCorsHeaders(response2, req);
|
|
88
|
+
}
|
|
76
89
|
const updates = [];
|
|
77
90
|
const values = [];
|
|
78
91
|
let paramCount = 1;
|
|
@@ -157,6 +170,10 @@ export const DELETE = withRateLimitTier(withApiLogging(async (req, { params }) =
|
|
|
157
170
|
const response2 = createApiError("User ID or email is required", 400, null, "MISSING_IDENTIFIER");
|
|
158
171
|
return addCorsHeaders(response2, req);
|
|
159
172
|
}
|
|
173
|
+
if (!hasAdminPermission(authResult, "users:delete")) {
|
|
174
|
+
const response2 = createApiError("Insufficient permissions. Superadmin access required.", 403, null, "FORBIDDEN");
|
|
175
|
+
return addCorsHeaders(response2, req);
|
|
176
|
+
}
|
|
160
177
|
const targetUser = await queryOneWithRLS(
|
|
161
178
|
'SELECT id, email FROM "users" WHERE id = $1 OR email = $1',
|
|
162
179
|
[id],
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"@nextsparkjs/core": ">=0.1.0-0",
|
|
15
15
|
"@nextsparkjs/testing": ">=0.1.0-0",
|
|
16
16
|
"lucide-react": "^0.539.0",
|
|
17
|
-
"next": "
|
|
17
|
+
"next": "~16.3.6",
|
|
18
18
|
"next-intl": "^4.0.0",
|
|
19
|
-
"react": "^19.
|
|
20
|
-
"react-dom": "^19.
|
|
19
|
+
"react": "^19.2.0",
|
|
20
|
+
"react-dom": "^19.2.0",
|
|
21
21
|
"zod": "^4.0.0"
|
|
22
22
|
},
|
|
23
23
|
"nextspark": {
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"@nextsparkjs/core": ">=0.1.0-0",
|
|
14
14
|
"@nextsparkjs/testing": ">=0.1.0-0",
|
|
15
15
|
"lucide-react": "^0.539.0",
|
|
16
|
-
"next": "
|
|
16
|
+
"next": "~16.3.6",
|
|
17
17
|
"next-intl": "^4.0.0",
|
|
18
|
-
"react": "^19.
|
|
19
|
-
"react-dom": "^19.
|
|
18
|
+
"react": "^19.2.0",
|
|
19
|
+
"react-dom": "^19.2.0",
|
|
20
20
|
"zod": "^4.0.0"
|
|
21
21
|
},
|
|
22
22
|
"nextspark": {
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"@nextsparkjs/core": ">=0.1.0-0",
|
|
18
18
|
"@nextsparkjs/testing": ">=0.1.0-0",
|
|
19
19
|
"lucide-react": "^0.539.0",
|
|
20
|
-
"next": "
|
|
21
|
-
"react": "^19.
|
|
22
|
-
"react-dom": "^19.
|
|
20
|
+
"next": "~16.3.6",
|
|
21
|
+
"react": "^19.2.0",
|
|
22
|
+
"react-dom": "^19.2.0",
|
|
23
23
|
"zod": "^4.0.0"
|
|
24
24
|
},
|
|
25
25
|
"nextspark": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextsparkjs/core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.194",
|
|
4
4
|
"description": "NextSpark - The complete SaaS framework for Next.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "NextSpark <hello@nextspark.dev>",
|
|
@@ -390,9 +390,9 @@
|
|
|
390
390
|
"scripts"
|
|
391
391
|
],
|
|
392
392
|
"peerDependencies": {
|
|
393
|
-
"next": "~16.3.
|
|
394
|
-
"react": "
|
|
395
|
-
"react-dom": "
|
|
393
|
+
"next": "~16.3.6",
|
|
394
|
+
"react": "^19.2.0",
|
|
395
|
+
"react-dom": "^19.2.0"
|
|
396
396
|
},
|
|
397
397
|
"devDependencies": {
|
|
398
398
|
"@better-fetch/fetch": "1.3.1",
|
|
@@ -496,7 +496,10 @@
|
|
|
496
496
|
"use-intl": "^4.9.1",
|
|
497
497
|
"uuid": "^13.0.0",
|
|
498
498
|
"zod": "^4.3.6",
|
|
499
|
-
"@nextsparkjs/ui": "0.1.0-beta.
|
|
499
|
+
"@nextsparkjs/ui": "0.1.0-beta.194"
|
|
500
|
+
},
|
|
501
|
+
"engines": {
|
|
502
|
+
"node": ">=22.14.0"
|
|
500
503
|
},
|
|
501
504
|
"scripts": {
|
|
502
505
|
"postinstall": "node scripts/postinstall.mjs || true",
|
|
@@ -192,6 +192,10 @@ async function main() {
|
|
|
192
192
|
console.log(' RESEND_API_KEY, RESEND_FROM_EMAIL; Google: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET), or change auth.methods.')
|
|
193
193
|
console.log(' Credentials injected only at runtime: declare them with NEXTSPARK_AUTH_RUNTIME_ONLY=email,google;')
|
|
194
194
|
console.log(' they are validated again when the server starts and on every login request.')
|
|
195
|
+
if (result.diagnostics.some(({ code }) => code.endsWith('_PLACEHOLDER'))) {
|
|
196
|
+
console.log(' A placeholder value (re_..., your-google-client-id) is a value, not a missing one: NEXTSPARK_AUTH_RUNTIME_ONLY does not defer it.')
|
|
197
|
+
console.log(' Remove the placeholder lines from .env (and the build environment), or replace them with the real values.')
|
|
198
|
+
}
|
|
195
199
|
process.exit(1)
|
|
196
200
|
}
|
|
197
201
|
|
|
@@ -29,7 +29,7 @@ const RAW_CONTROL = /[\u0000-\u0009\u000b-\u001f\u007f-\u009f\u061c\u200e\u200f\
|
|
|
29
29
|
async function createProject(parent = tmpdir(), name = 'nextspark-build-output-test-') {
|
|
30
30
|
const root = await mkdtemp(join(parent, name))
|
|
31
31
|
await writeFile(join(root, 'nextspark.config.ts'), 'export default {}\n')
|
|
32
|
-
await writeFile(join(root, 'package.json'), '{"dependencies":{"next":"16.3.
|
|
32
|
+
await writeFile(join(root, 'package.json'), '{"dependencies":{"next":"16.3.6"}}')
|
|
33
33
|
await mkdir(join(root, 'src', 'app'), { recursive: true })
|
|
34
34
|
return root
|
|
35
35
|
}
|
|
@@ -17,7 +17,7 @@ async function project(files = {}) {
|
|
|
17
17
|
|
|
18
18
|
test('the nearest nextspark.config.ts owns every project source path', async () => {
|
|
19
19
|
const root = await project({
|
|
20
|
-
'package.json': JSON.stringify({ dependencies: { next: '16.3.
|
|
20
|
+
'package.json': JSON.stringify({ dependencies: { next: '16.3.6' } }),
|
|
21
21
|
'nextspark.config.ts': 'export default {}\n',
|
|
22
22
|
})
|
|
23
23
|
const nested = join(root, 'entities', 'tasks')
|
|
@@ -40,7 +40,7 @@ test('the nearest nextspark.config.ts owns every project source path', async ()
|
|
|
40
40
|
|
|
41
41
|
test('a next.config file cannot select a project without nextspark.config.ts', async () => {
|
|
42
42
|
const root = await project({
|
|
43
|
-
'package.json': JSON.stringify({ dependencies: { next: '16.3.
|
|
43
|
+
'package.json': JSON.stringify({ dependencies: { next: '16.3.6' } }),
|
|
44
44
|
'next.config.mjs': 'export default {}\n',
|
|
45
45
|
})
|
|
46
46
|
try {
|
|
@@ -66,7 +66,7 @@ test('an enabled packaged plugin resolves through the project dependency tree',
|
|
|
66
66
|
const root = await project({
|
|
67
67
|
'package.json': JSON.stringify({
|
|
68
68
|
dependencies: {
|
|
69
|
-
next: '16.3.
|
|
69
|
+
next: '16.3.6',
|
|
70
70
|
'@example/plugin-search': '1.0.0',
|
|
71
71
|
},
|
|
72
72
|
}),
|
|
@@ -98,7 +98,7 @@ test('an enabled packaged plugin that is not installed fails with an actionable
|
|
|
98
98
|
const root = await project({
|
|
99
99
|
'package.json': JSON.stringify({
|
|
100
100
|
dependencies: {
|
|
101
|
-
next: '16.3.
|
|
101
|
+
next: '16.3.6',
|
|
102
102
|
'@example/plugin-search': '1.0.0',
|
|
103
103
|
},
|
|
104
104
|
}),
|
|
@@ -119,7 +119,7 @@ test('a packaged plugin cannot use a path segment as its logical name', async ()
|
|
|
119
119
|
const root = await project({
|
|
120
120
|
'package.json': JSON.stringify({
|
|
121
121
|
dependencies: {
|
|
122
|
-
next: '16.3.
|
|
122
|
+
next: '16.3.6',
|
|
123
123
|
'@example/plugin-search': '1.0.0',
|
|
124
124
|
},
|
|
125
125
|
}),
|
|
@@ -146,7 +146,7 @@ test('a local plugin shadows an installed packaged plugin with the same NextSpar
|
|
|
146
146
|
const root = await project({
|
|
147
147
|
'package.json': JSON.stringify({
|
|
148
148
|
dependencies: {
|
|
149
|
-
next: '16.3.
|
|
149
|
+
next: '16.3.6',
|
|
150
150
|
'@example/plugin-search': '1.0.0',
|
|
151
151
|
},
|
|
152
152
|
}),
|
|
@@ -269,7 +269,7 @@ async function snapshot(root) {
|
|
|
269
269
|
async function buildableProject() {
|
|
270
270
|
const project = await directory()
|
|
271
271
|
await writeIn(project.root, 'nextspark.config.ts', 'export default {}\n')
|
|
272
|
-
await writeIn(project.root, 'package.json', '{"dependencies":{"next":"16.3.
|
|
272
|
+
await writeIn(project.root, 'package.json', '{"dependencies":{"next":"16.3.6"}}')
|
|
273
273
|
await writeIn(project.root, 'templates/pricing/page.tsx', 'export default function Pricing() { return null }\n')
|
|
274
274
|
return project
|
|
275
275
|
}
|
|
@@ -22,7 +22,7 @@ async function planFor(files) {
|
|
|
22
22
|
const root = temp()
|
|
23
23
|
try {
|
|
24
24
|
write(root, 'nextspark.config.ts', 'export default { plugins: [] }\n')
|
|
25
|
-
write(root, 'package.json', JSON.stringify({ name: 'wire', dependencies: { next: '16.3.
|
|
25
|
+
write(root, 'package.json', JSON.stringify({ name: 'wire', dependencies: { next: '16.3.6' } }))
|
|
26
26
|
write(root, 'config/theme.config.ts', "export const wireThemeConfig = { name: 'wire' }\n")
|
|
27
27
|
for (const [path, content] of Object.entries(files)) write(root, path, content)
|
|
28
28
|
const entities = await discoverAllEntities(getConfig(root), { includeCore: false })
|
|
@@ -73,7 +73,7 @@ const NOTES_CONFIG = `export const noteEntityConfig = {
|
|
|
73
73
|
function project(extra = {}) {
|
|
74
74
|
const root = temp()
|
|
75
75
|
write(root, 'nextspark.config.ts', 'export default { plugins: [] }\n')
|
|
76
|
-
write(root, 'package.json', JSON.stringify({ name: 'contracts-project', dependencies: { next: '16.3.
|
|
76
|
+
write(root, 'package.json', JSON.stringify({ name: 'contracts-project', dependencies: { next: '16.3.6' } }))
|
|
77
77
|
write(root, 'config/theme.config.ts', "export const contractsThemeConfig = { name: 'contracts' }\n")
|
|
78
78
|
write(root, 'entities/tasks/tasks.config.ts', TASKS_CONFIG)
|
|
79
79
|
write(root, 'entities/tasks/tasks.fields.ts', FIELDS)
|
|
@@ -201,7 +201,7 @@ export function renderEntity(contract, { source, shape } = {}) {
|
|
|
201
201
|
const lines = [CONTRACTS_HEADER]
|
|
202
202
|
if (source) lines.push(`// Source: ${source}`)
|
|
203
203
|
for (const warning of contract.warnings) lines.push(`// Warning: ${warning}`)
|
|
204
|
-
lines.push('', "import
|
|
204
|
+
lines.push('', "import * as z from 'zod'", "import type { EntityConfig } from '../schema/types'", "import { generateEntitySchemas } from '../schema/schema-generator'", `import { ${usedFromShape.join(', ')} } from '../schema/response-shape'`)
|
|
205
205
|
if (shared.length > 0) lines.push(`import { ${shared.join(', ')} } from '../fields'`)
|
|
206
206
|
lines.push('', `export const ${names.apiPath} = ${quote(`/api/v1/${contract.slug}`)}`, '')
|
|
207
207
|
|
|
@@ -246,7 +246,7 @@ export function renderEntity(contract, { source, shape } = {}) {
|
|
|
246
246
|
export function renderEnvelope() {
|
|
247
247
|
return `${CONTRACTS_HEADER}
|
|
248
248
|
|
|
249
|
-
import
|
|
249
|
+
import * as z from 'zod'
|
|
250
250
|
|
|
251
251
|
/** \`info\` of every response: the timestamp, plus whatever the endpoint adds (pagination, \`created\`). */
|
|
252
252
|
export const apiInfoSchema = z.looseObject({ timestamp: z.string() })
|
|
@@ -307,7 +307,7 @@ export const apiErrorResponseSchema = z.object({
|
|
|
307
307
|
export function renderFields() {
|
|
308
308
|
return `${CONTRACTS_HEADER}
|
|
309
309
|
|
|
310
|
-
import
|
|
310
|
+
import * as z from 'zod'
|
|
311
311
|
import { addressSchema, fileObjectSchema } from './schema/schema-generator'
|
|
312
312
|
import { mediaRefSchema } from './schema/media-ref'
|
|
313
313
|
|