@nextsparkjs/core 0.1.0-beta.195 → 0.1.0-beta.197
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 -0
- package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
- package/dist/components/auth/forms/SignupForm.js +55 -45
- package/dist/components/billing/InvoicesPagination.js +2 -2
- package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/config-panel.js +3 -2
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
- package/dist/components/dashboard/navigation/DynamicNavigation.js +2 -2
- package/dist/components/settings/layouts/SettingsSidebar.d.ts.map +1 -1
- package/dist/components/settings/layouts/SettingsSidebar.js +0 -1
- package/dist/components/teams/CreateTeamButton.js +2 -2
- package/dist/components/teams/CreateTeamDialog.js +1 -1
- package/dist/components/teams/InviteMemberButton.js +3 -3
- package/dist/components/teams/InviteMemberDialog.js +1 -1
- package/dist/components/teams/TeamPendingInvitations.js +2 -2
- package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
- package/dist/components/users/tables/UsersTable.js +3 -4
- package/dist/hooks/useAuthReadiness.d.ts +1 -0
- package/dist/hooks/useAuthReadiness.d.ts.map +1 -1
- package/dist/hooks/useAuthReadiness.js +3 -1
- package/dist/lib/actions/entity.actions.js +2 -2
- package/dist/lib/actions/team.actions.js +2 -2
- package/dist/lib/actions/user.actions.js +4 -4
- package/dist/lib/api/api-error.d.ts +2 -0
- package/dist/lib/api/api-error.d.ts.map +1 -1
- package/dist/lib/api/api-error.js +3 -1
- package/dist/lib/api/auth/dual-auth.d.ts +2 -1
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +25 -6
- package/dist/lib/api/auth/scope-policy.d.ts +6 -3
- package/dist/lib/api/auth/scope-policy.d.ts.map +1 -1
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +7 -0
- package/dist/lib/api/cors-response.d.ts +3 -2
- package/dist/lib/api/cors-response.d.ts.map +1 -1
- package/dist/lib/api/cors-response.js +8 -6
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +66 -15
- package/dist/lib/api/entity-handler.js +2 -2
- package/dist/lib/api/helpers.d.ts.map +1 -1
- package/dist/lib/api/helpers.js +9 -24
- package/dist/lib/api/permission-middleware.d.ts.map +1 -1
- package/dist/lib/api/permission-middleware.js +0 -2
- package/dist/lib/api/post-categories.d.ts +29 -0
- package/dist/lib/api/post-categories.d.ts.map +1 -0
- package/dist/lib/api/post-categories.js +33 -0
- package/dist/lib/api/rate-limit.d.ts +8 -0
- package/dist/lib/api/rate-limit.d.ts.map +1 -1
- package/dist/lib/api/rate-limit.js +34 -3
- package/dist/lib/api/request-origin.d.ts.map +1 -1
- package/dist/lib/api/request-origin.js +1 -1
- package/dist/lib/auth/auth-methods.d.ts +5 -2
- package/dist/lib/auth/auth-methods.d.ts.map +1 -1
- package/dist/lib/auth/auth-methods.js +4 -2
- package/dist/lib/auth/authorization-session.d.ts +14 -0
- package/dist/lib/auth/authorization-session.d.ts.map +1 -0
- package/dist/lib/auth/authorization-session.js +10 -0
- package/dist/lib/auth/request-session.d.ts +5 -0
- package/dist/lib/auth/request-session.d.ts.map +1 -1
- package/dist/lib/auth/request-session.js +3 -0
- package/dist/lib/auth/runtime-readiness.d.ts +7 -4
- package/dist/lib/auth/runtime-readiness.d.ts.map +1 -1
- package/dist/lib/auth/runtime-readiness.js +9 -6
- package/dist/lib/auth/sample-accounts.d.ts +18 -0
- package/dist/lib/auth/sample-accounts.d.ts.map +1 -0
- package/dist/lib/auth/sample-accounts.js +25 -0
- package/dist/lib/auth/suspension.d.ts +22 -0
- package/dist/lib/auth/suspension.d.ts.map +1 -0
- package/dist/lib/auth/suspension.js +43 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +20 -17
- package/dist/lib/cache/public-entity-cache.d.ts +21 -0
- package/dist/lib/cache/public-entity-cache.d.ts.map +1 -0
- package/dist/lib/cache/public-entity-cache.js +25 -0
- package/dist/lib/config/app.config.d.ts.map +1 -1
- package/dist/lib/config/app.config.js +6 -7
- package/dist/lib/config/types.d.ts +8 -6
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/db-pool.d.ts +31 -0
- package/dist/lib/db-pool.d.ts.map +1 -0
- package/dist/lib/db-pool.js +75 -0
- package/dist/lib/db.d.ts +1 -1
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +11 -14
- package/dist/lib/entities/api-generator.js +5 -5
- package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
- package/dist/lib/entities/entity-hooks.js +4 -0
- package/dist/lib/mcp/errors.d.ts +2 -0
- package/dist/lib/mcp/errors.d.ts.map +1 -1
- package/dist/lib/mcp/errors.js +3 -1
- package/dist/lib/mcp/index.d.ts +1 -1
- package/dist/lib/mcp/index.d.ts.map +1 -1
- package/dist/lib/mcp/index.js +2 -1
- package/dist/lib/oauth/token-refresh.d.ts.map +1 -1
- package/dist/lib/oauth/token-refresh.js +3 -6
- package/dist/lib/permissions/check.d.ts +9 -0
- package/dist/lib/permissions/check.d.ts.map +1 -1
- package/dist/lib/permissions/check.js +11 -1
- package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.js +41 -7
- package/dist/lib/services/transactional-meta.service.d.ts.map +1 -1
- package/dist/lib/services/transactional-meta.service.js +7 -8
- package/dist/lib/teams/actions.d.ts.map +1 -1
- package/dist/lib/teams/actions.js +0 -1
- package/dist/lib/validation.d.ts +5 -0
- package/dist/lib/validation.d.ts.map +1 -1
- package/dist/lib/validation.js +2 -0
- package/dist/messages/de/billing.json +12 -1
- package/dist/messages/de/common.json +2 -0
- package/dist/messages/de/docs.json +1 -0
- package/dist/messages/de/features.json +16 -4
- package/dist/messages/de/home.json +5 -0
- package/dist/messages/de/index.d.ts +68 -25
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/index.js +2 -0
- package/dist/messages/de/index.ts +2 -0
- package/dist/messages/de/settings.json +1 -0
- package/dist/messages/de/teams.json +3 -18
- package/dist/messages/en/billing.json +1 -1
- package/dist/messages/en/common.json +2 -0
- package/dist/messages/en/docs.json +1 -0
- package/dist/messages/en/features.json +16 -4
- package/dist/messages/en/home.json +5 -0
- package/dist/messages/en/index.d.ts +58 -25
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/index.js +2 -0
- package/dist/messages/en/index.ts +2 -0
- package/dist/messages/en/settings.json +1 -0
- package/dist/messages/en/teams.json +3 -18
- package/dist/messages/es/billing.json +1 -1
- package/dist/messages/es/common.json +2 -0
- package/dist/messages/es/docs.json +1 -0
- package/dist/messages/es/features.json +16 -4
- package/dist/messages/es/home.json +5 -0
- package/dist/messages/es/index.d.ts +41 -8
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/index.js +2 -0
- package/dist/messages/es/index.ts +2 -0
- package/dist/messages/es/settings.json +1 -0
- package/dist/messages/es/teams.json +3 -0
- package/dist/messages/fr/billing.json +12 -1
- package/dist/messages/fr/common.json +2 -0
- package/dist/messages/fr/docs.json +1 -0
- package/dist/messages/fr/features.json +16 -4
- package/dist/messages/fr/home.json +5 -0
- package/dist/messages/fr/index.d.ts +68 -25
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/index.js +2 -0
- package/dist/messages/fr/index.ts +2 -0
- package/dist/messages/fr/settings.json +1 -0
- package/dist/messages/fr/teams.json +3 -18
- package/dist/messages/it/billing.json +12 -1
- package/dist/messages/it/common.json +2 -0
- package/dist/messages/it/docs.json +1 -0
- package/dist/messages/it/features.json +16 -4
- package/dist/messages/it/home.json +5 -0
- package/dist/messages/it/index.d.ts +68 -25
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/index.js +2 -0
- package/dist/messages/it/index.ts +2 -0
- package/dist/messages/it/settings.json +1 -0
- package/dist/messages/it/teams.json +3 -18
- package/dist/messages/pt/billing.json +12 -1
- package/dist/messages/pt/common.json +2 -0
- package/dist/messages/pt/docs.json +1 -0
- package/dist/messages/pt/features.json +16 -4
- package/dist/messages/pt/home.json +5 -0
- package/dist/messages/pt/index.d.ts +68 -25
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/index.js +2 -0
- package/dist/messages/pt/index.ts +2 -0
- package/dist/messages/pt/settings.json +1 -0
- package/dist/messages/pt/teams.json +3 -18
- package/dist/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/dist/migrations/030_taxonomies_team_writes.sql +29 -0
- package/dist/migrations/031_users_role_suspended.sql +41 -0
- package/dist/migrations/090_sample_data.sql +10 -0
- package/dist/routes/(auth)/signup/page.cc.d.ts +3 -1
- package/dist/routes/(auth)/signup/page.cc.d.ts.map +1 -1
- package/dist/routes/(auth)/signup/page.cc.js +2 -2
- package/dist/routes/(auth)/signup/page.d.ts +3 -1
- package/dist/routes/(auth)/signup/page.d.ts.map +1 -1
- package/dist/routes/(auth)/signup/page.js +4 -3
- package/dist/routes/_internal/area-access.js +2 -2
- package/dist/routes/_internal/entity-permission-layout.d.ts.map +1 -1
- package/dist/routes/_internal/entity-permission-layout.js +0 -7
- package/dist/routes/api/devtools/config/entities/route.js +2 -2
- package/dist/routes/api/devtools/config/theme/route.js +2 -2
- package/dist/routes/api/devtools/tests/[...path]/route.js +2 -2
- package/dist/routes/api/devtools/tests/route.js +2 -2
- package/dist/routes/api/mcp/route.d.ts.map +1 -1
- package/dist/routes/api/mcp/route.js +7 -1
- package/dist/routes/api/superadmin/subscriptions/route.js +2 -2
- package/dist/routes/api/superadmin/teams/[teamId]/route.js +2 -2
- package/dist/routes/api/superadmin/teams/route.js +2 -2
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts +5 -3
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/users/[userId]/route.js +46 -23
- package/dist/routes/api/superadmin/users/route.js +2 -2
- package/dist/routes/api/user/delete-account/route.d.ts.map +1 -1
- package/dist/routes/api/user/delete-account/route.js +2 -3
- package/dist/routes/api/user/plan-flags/route.d.ts.map +1 -1
- package/dist/routes/api/user/plan-flags/route.js +5 -5
- package/dist/routes/api/user/profile/route.js +3 -3
- package/dist/routes/api/v1/auth/signup-with-invite/route.d.ts.map +1 -1
- package/dist/routes/api/v1/auth/signup-with-invite/route.js +80 -44
- package/dist/routes/api/v1/media/upload/route.d.ts +2 -1
- package/dist/routes/api/v1/media/upload/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/upload/route.js +10 -2
- package/dist/routes/api/v1/post-categories/[id]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/[id]/route.js +39 -34
- package/dist/routes/api/v1/post-categories/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/route.js +18 -14
- package/dist/routes/api/v1/team-invitations/route.d.ts.map +1 -1
- package/dist/routes/api/v1/team-invitations/route.js +12 -5
- package/dist/routes/api/v1/teams/[teamId]/members/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/[teamId]/members/route.js +12 -10
- package/dist/routes/api/v1/teams/switch/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/switch/route.js +4 -1
- package/dist/routes/dashboard/settings/password/page.d.ts.map +1 -1
- package/dist/routes/dashboard/settings/password/page.js +10 -12
- package/dist/routes/devtools/config/page.js +1 -1
- package/dist/routes/devtools/page.js +1 -1
- package/dist/routes/devtools/scheduled-actions/page.js +1 -1
- package/dist/routes/devtools/tests/[[...path]]/page.js +1 -1
- package/dist/routes/superadmin/users/page.js +2 -2
- package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/dist/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/dist/templates/next.config.mjs +11 -31
- package/dist/templates/projects/blog/package.json +1 -1
- package/dist/templates/projects/crm/package.json +1 -1
- package/dist/templates/projects/productivity/package.json +1 -1
- package/dist/templates/projects/starter/README.md +2 -0
- package/dist/templates/projects/starter/config/app.config.ts +8 -0
- package/dist/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/dist/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/dist/templates/proxy.ts +18 -8
- package/dist/templates/public/uploads/README.md +5 -94
- package/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/migrations/030_taxonomies_team_writes.sql +29 -0
- package/migrations/031_users_role_suspended.sql +41 -0
- package/migrations/090_sample_data.sql +10 -0
- package/package.json +3 -3
- 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/host/README.md +2 -2
- package/scripts/build/registry/host/__tests__/facade-emitter.test.mjs +14 -13
- 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-proxy-areas.test.mjs +99 -1
- package/scripts/build/registry/host/__tests__/host-robustness.test.mjs +1 -1
- package/scripts/build/registry/host/facade-emitter.mjs +1 -1
- package/scripts/build/registry/host/next-route-exports.json +2 -2
- package/scripts/build/registry/host/proxy-areas.mjs +92 -2
- package/scripts/db/run-migrations.mjs +30 -10
- package/scripts/db/sample-data.mjs +44 -0
- package/scripts/db/verify-theme-migrations.mjs +7 -1
- package/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/templates/next.config.mjs +11 -31
- package/templates/projects/blog/package.json +1 -1
- package/templates/projects/crm/package.json +1 -1
- package/templates/projects/productivity/package.json +1 -1
- package/templates/projects/starter/README.md +2 -0
- package/templates/projects/starter/config/app.config.ts +8 -0
- package/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/templates/proxy.ts +18 -8
- package/templates/public/uploads/README.md +5 -94
- package/tests/jest/__mocks__/@nextsparkjs/registries/api-presets-registry.ts +2 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/mcp-registry.ts +2 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +5 -4
- package/dist/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/dist/templates/public/uploads/.gitignore +0 -6
- package/dist/templates/public/uploads/temp/.gitkeep +0 -1
- package/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/templates/public/uploads/.gitignore +0 -6
- package/templates/public/uploads/temp/.gitkeep +0 -1
|
@@ -3,6 +3,8 @@ import { getUserDefaultTeamId } from "../../teams/dashboard-team.js";
|
|
|
3
3
|
import { NextResponse } from "next/server";
|
|
4
4
|
import { auth } from "../../auth.js";
|
|
5
5
|
import { validateApiKey } from "../auth.js";
|
|
6
|
+
import { isSuspendedRole } from "../../auth/suspension.js";
|
|
7
|
+
import { apiKeyRateLimitResponse } from "../rate-limit.js";
|
|
6
8
|
import { queryOne } from "../../db.js";
|
|
7
9
|
import { TeamMemberService } from "../../services/team-member.service.js";
|
|
8
10
|
import { TeamService } from "../../services/team.service.js";
|
|
@@ -31,6 +33,26 @@ async function authenticateRequest(request, options = {}) {
|
|
|
31
33
|
error: failure
|
|
32
34
|
};
|
|
33
35
|
}
|
|
36
|
+
const rateLimitResponse = apiKeyRateLimitResponse(request, {
|
|
37
|
+
userId: apiKeyResult.user.id,
|
|
38
|
+
keyId: apiKeyResult.keyId,
|
|
39
|
+
scopes: apiKeyResult.scopes ?? []
|
|
40
|
+
});
|
|
41
|
+
if (rateLimitResponse) {
|
|
42
|
+
return {
|
|
43
|
+
success: false,
|
|
44
|
+
type: "api-key",
|
|
45
|
+
user: apiKeyResult.user,
|
|
46
|
+
scopes: apiKeyResult.scopes,
|
|
47
|
+
keyId: apiKeyResult.keyId,
|
|
48
|
+
rateLimitResponse,
|
|
49
|
+
error: {
|
|
50
|
+
code: "RATE_LIMIT_EXCEEDED",
|
|
51
|
+
status: 429,
|
|
52
|
+
message: "Rate limit exceeded"
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
34
56
|
return applyActAsOverride(request, apiKeyResult);
|
|
35
57
|
}
|
|
36
58
|
const sessionResult = await trySessionAuth(request);
|
|
@@ -120,15 +142,11 @@ async function tryApiKeyAuth(request) {
|
|
|
120
142
|
async function trySessionAuth(request) {
|
|
121
143
|
var _a;
|
|
122
144
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
console.log("[dual-auth] Session result:", session ? "found" : "not found");
|
|
126
|
-
if (!(session == null ? void 0 : session.user)) {
|
|
127
|
-
console.log("[dual-auth] No session.user found");
|
|
145
|
+
const session = await auth.api.getSession({ headers: request.headers, query: { disableCookieCache: true } });
|
|
146
|
+
if (!(session == null ? void 0 : session.user) || isSuspendedRole(session.user.role)) {
|
|
128
147
|
return { success: false, type: "none", user: null };
|
|
129
148
|
}
|
|
130
149
|
const defaultTeamId = await getUserDefaultTeamId(session.user.id);
|
|
131
|
-
console.log("[dual-auth] Session auth successful for user:", session.user.id);
|
|
132
150
|
return {
|
|
133
151
|
success: true,
|
|
134
152
|
type: "session",
|
|
@@ -223,6 +241,7 @@ function createAuthError(message = "Authentication required", status = 401) {
|
|
|
223
241
|
);
|
|
224
242
|
}
|
|
225
243
|
function createAuthFailureResponse(authResult) {
|
|
244
|
+
if (authResult.rateLimitResponse) return authResult.rateLimitResponse;
|
|
226
245
|
const failure = authResult.error ?? authenticationFailed();
|
|
227
246
|
return NextResponse.json(
|
|
228
247
|
{
|
|
@@ -22,11 +22,14 @@
|
|
|
22
22
|
* visible 403 on an integration instead of a silently over-privileged
|
|
23
23
|
* credential. Before, `scopes` were populated and nobody was obliged to look.
|
|
24
24
|
*/
|
|
25
|
-
export type DualAuthFailureCode = 'AUTHENTICATION_FAILED' | 'INSUFFICIENT_SCOPE' | 'SCOPE_NOT_DECLARED';
|
|
25
|
+
export type DualAuthFailureCode = 'AUTHENTICATION_FAILED' | 'INSUFFICIENT_SCOPE' | 'SCOPE_NOT_DECLARED' | 'RATE_LIMIT_EXCEEDED';
|
|
26
26
|
export interface DualAuthFailure {
|
|
27
27
|
code: DualAuthFailureCode;
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* 401 when no usable credential was presented, 403 when a valid key was rejected on scope, 429 when a valid
|
|
30
|
+
* key is over its per-key limit.
|
|
31
|
+
*/
|
|
32
|
+
status: 401 | 403 | 429;
|
|
30
33
|
message: string;
|
|
31
34
|
}
|
|
32
35
|
export interface AuthenticateOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-policy.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/scope-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,uBAAuB,GACvB,oBAAoB,GACpB,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"scope-policy.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/scope-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,uBAAuB,GACvB,oBAAoB,GACpB,oBAAoB,GACpB,qBAAqB,CAAA;AAEzB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAA;IACzB;;;OAGG;IACH,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACjC;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,IAAI,eAAe,CAEtD;AAED,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,mBAAmB,CAAC,eAAe,CAAC,GAAG,MAAM,EAAE,CAKrG;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACrC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,GAC/B,OAAO,CAIT;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAU/D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACrC,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,MAAM,GACZ,eAAe,GAAG,IAAI,CAuBxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/lib/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO1C;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAEnF;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAQD,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAEtE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/lib/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO1C;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAEnF;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAQD,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAEtE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAsHrF;AA8CD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3D;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5E;AAID;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAM/E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAMhF;AAiBD;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU;QAG1C,MAAM;WACH,MAAM;eACF,MAAM,GAAG,IAAI;cACd,MAAM,GAAG,IAAI;UACjB,MAAM;GAWjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC;IACT,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC,CAyED"}
|
package/dist/lib/api/auth.js
CHANGED
|
@@ -11,6 +11,8 @@ function getValidatedApiKey(request) {
|
|
|
11
11
|
return validatedKeys.get(request) ?? null;
|
|
12
12
|
}
|
|
13
13
|
async function validateApiKey(request) {
|
|
14
|
+
const validatedBefore = validatedKeys.get(request);
|
|
15
|
+
if (validatedBefore) return validatedBefore;
|
|
14
16
|
const startTime = Date.now();
|
|
15
17
|
try {
|
|
16
18
|
const apiKey = presentedApiKey(request.headers);
|
|
@@ -63,6 +65,11 @@ async function validateApiKey(request) {
|
|
|
63
65
|
await constantTimeDelay(startTime);
|
|
64
66
|
return null;
|
|
65
67
|
}
|
|
68
|
+
const owner = await queryOne('SELECT role FROM "users" WHERE id = $1', [keyData.userId]);
|
|
69
|
+
if ((owner == null ? void 0 : owner.role) === "suspended") {
|
|
70
|
+
await constantTimeDelay(startTime);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
66
73
|
if (keyData.failedAttempts && keyData.failedAttempts > 0) {
|
|
67
74
|
resetFailedAttempts(keyData.id).catch(console.error);
|
|
68
75
|
}
|
|
@@ -13,7 +13,7 @@ export declare const CORS_ALLOW_HEADERS: string;
|
|
|
13
13
|
* cookie-authenticated writes only from it (checkRequestOrigin).
|
|
14
14
|
*/
|
|
15
15
|
export declare function isAppOnlyCorsPath(pathname: string): boolean;
|
|
16
|
-
/** A request's path, for corsGrant (undefined when its URL does not parse). */
|
|
16
|
+
/** A request's path, for corsGrant (undefined when its URL does not parse, which corsGrant treats as app-only). */
|
|
17
17
|
export declare function requestPathname(request: Request): string | undefined;
|
|
18
18
|
/** The app's own origins: NEXT_PUBLIC_APP_URL and BETTER_AUTH_URL. */
|
|
19
19
|
export declare function appOrigins(): string[];
|
|
@@ -26,7 +26,8 @@ export declare function appOrigins(): string[];
|
|
|
26
26
|
* response names no origin and sends no credentials header.
|
|
27
27
|
* Under the admin and developer areas (isAppOnlyCorsPath, given the request's
|
|
28
28
|
* `pathname`), only the app's own origin is granted, with credentials; no
|
|
29
|
-
* other origin, listed or not.
|
|
29
|
+
* other origin, listed or not. A `pathname` that is undefined (not known)
|
|
30
|
+
* gets the same answer as those areas.
|
|
30
31
|
*/
|
|
31
32
|
export declare function corsGrant(origin: string, config: CorsConfig, env: string, pathname?: string): {
|
|
32
33
|
origin: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors-response.d.ts","sourceRoot":"","sources":["../../../src/lib/api/cors-response.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG/D,KAAK,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;AAEhD,eAAO,MAAM,kBAAkB,2CAA2C,CAAA;AAC1E,eAAO,MAAM,kBAAkB,QAAuF,CAAA;AAKtH;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"cors-response.d.ts","sourceRoot":"","sources":["../../../src/lib/api/cors-response.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG/D,KAAK,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;AAEhD,eAAO,MAAM,kBAAkB,2CAA2C,CAAA;AAC1E,eAAO,MAAM,kBAAkB,QAAuF,CAAA;AAKtH;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAM3D;AAED,mHAAmH;AACnH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAEpE;AAED,sEAAsE;AACtE,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAKrC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAYjD;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAGnD;AAED,oGAAoG;AACpG,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GACrD,IAAI,CAWN;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,CAa7E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAE5D"}
|
|
@@ -6,11 +6,13 @@ const CORS_ALLOW_METHODS = "GET, POST, PUT, PATCH, DELETE, OPTIONS";
|
|
|
6
6
|
const CORS_ALLOW_HEADERS = ["Content-Type", "Authorization", "X-API-Key", ...CLIENT_REQUEST_HEADERS].join(", ");
|
|
7
7
|
const APP_ONLY_PATH = /(^|\/)api\/(v1\/)?(superadmin|devtools)(\/|$)/;
|
|
8
8
|
function isAppOnlyCorsPath(pathname) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const path = pathname.split("/").map((segment) => {
|
|
10
|
+
try {
|
|
11
|
+
return decodeURIComponent(segment);
|
|
12
|
+
} catch {
|
|
13
|
+
return segment;
|
|
14
|
+
}
|
|
15
|
+
}).join("/");
|
|
14
16
|
return APP_ONLY_PATH.test(path.toLowerCase().replace(/\/{2,}/g, "/"));
|
|
15
17
|
}
|
|
16
18
|
function requestPathname(request) {
|
|
@@ -32,7 +34,7 @@ function appOrigins() {
|
|
|
32
34
|
}
|
|
33
35
|
function corsGrant(origin, config, env, pathname) {
|
|
34
36
|
const normalizedOrigin = normalizeOrigin(origin);
|
|
35
|
-
if (pathname
|
|
37
|
+
if (pathname === void 0 || isAppOnlyCorsPath(pathname)) {
|
|
36
38
|
return appOrigins().includes(normalizedOrigin) ? { origin: normalizedOrigin, credentials: true } : null;
|
|
37
39
|
}
|
|
38
40
|
const listed = isOriginAllowed(normalizedOrigin, getCorsOrigins(config, env));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic-handler.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/entity/generic-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AA+wBvD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAEnF;AA8lBD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAErF;
|
|
1
|
+
{"version":3,"file":"generic-handler.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/entity/generic-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AA+wBvD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAEnF;AA8lBD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAErF;AAwcD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAEpJ;AAoND;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAEtJ;AA4bD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAEtJ;AAwKD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAEtF"}
|
|
@@ -5,7 +5,7 @@ import { generateEntitySchemas } from "../../entities/schema-generator.js";
|
|
|
5
5
|
import { publicSlugIssue, otherBasePathSegments } from "../../entities/public-slug.js";
|
|
6
6
|
import { getAllEntityConfigs } from "../../entities/registry.js";
|
|
7
7
|
import { entityResponseSystemColumns, taxonomyResponseFields } from "../../entities/portable/response-shape.js";
|
|
8
|
-
import { queryWithRLS, mutateWithRLS, queryOneWithRLS } from "../../db.js";
|
|
8
|
+
import { queryWithRLS, mutateWithRLS, queryOneWithRLS, getTransactionClient } from "../../db.js";
|
|
9
9
|
import {
|
|
10
10
|
createApiResponse,
|
|
11
11
|
createApiError,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from "../helpers.js";
|
|
23
23
|
import { beforeEntityCreate, afterEntityCreate, beforeEntityUpdate, afterEntityUpdate, beforeEntityDelete, afterEntityDelete, beforeEntityRead, afterEntityRead } from "../../entities/entity-hooks.js";
|
|
24
24
|
import { logGenericHandlerUsage } from "./audit-log.js";
|
|
25
|
-
import { checkPermission } from "../../permissions/check.js";
|
|
25
|
+
import { checkPermission, declaredPublishPermission, publishPermissionFor } from "../../permissions/check.js";
|
|
26
26
|
import { extractPatternIds } from "../../blocks/pattern-resolver.js";
|
|
27
27
|
import { isPatternReference } from "../../../types/pattern-reference.js";
|
|
28
28
|
import { PatternUsageService } from "../../services/pattern-usage.service.js";
|
|
@@ -122,7 +122,7 @@ async function includeTaxonomiesInData(entityConfig, items, userId) {
|
|
|
122
122
|
};
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
|
-
async function processTaxonomyRelations(entityConfig, entityId, data, userId, isUpdate = false) {
|
|
125
|
+
async function processTaxonomyRelations(entityConfig, entityId, data, userId, teamId, isUpdate = false) {
|
|
126
126
|
var _a, _b, _c;
|
|
127
127
|
if (!((_a = entityConfig.taxonomies) == null ? void 0 : _a.enabled) || !((_c = (_b = entityConfig.taxonomies) == null ? void 0 : _b.types) == null ? void 0 : _c.length)) {
|
|
128
128
|
return;
|
|
@@ -146,8 +146,9 @@ async function processTaxonomyRelations(entityConfig, entityId, data, userId, is
|
|
|
146
146
|
const taxonomyId = typeof taxonomyIds[i] === "object" && taxonomyIds[i] !== null ? taxonomyIds[i].id : taxonomyIds[i];
|
|
147
147
|
await mutateWithRLS(
|
|
148
148
|
`INSERT INTO entity_taxonomy_relations ("entityType", "entityId", "taxonomyId", "order")
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
SELECT $1, $2, id, $4 FROM taxonomies
|
|
150
|
+
WHERE id = $3 AND type = $5 AND ("teamId" IS NULL OR "teamId" = $6)`,
|
|
151
|
+
[entityType, entityId, taxonomyId, i + 1, taxonomyType.type, teamId],
|
|
151
152
|
userId
|
|
152
153
|
);
|
|
153
154
|
}
|
|
@@ -350,13 +351,11 @@ async function checkAuthPermission(authResult, entitySlug, action, teamId, reque
|
|
|
350
351
|
}
|
|
351
352
|
async function validateTeamMembership(userId, teamId) {
|
|
352
353
|
try {
|
|
353
|
-
console.log("[GenericHandler] Validating team membership:", { userId, teamId, teamIdLength: teamId == null ? void 0 : teamId.length });
|
|
354
354
|
const member = await queryOneWithRLS(
|
|
355
355
|
'SELECT id FROM "team_members" WHERE "teamId" = $1 AND "userId" = $2',
|
|
356
356
|
[teamId, userId],
|
|
357
357
|
userId
|
|
358
358
|
);
|
|
359
|
-
console.log("[GenericHandler] Team membership result:", { found: !!member, memberId: member == null ? void 0 : member.id });
|
|
360
359
|
return !!member;
|
|
361
360
|
} catch (error) {
|
|
362
361
|
console.error("[GenericHandler] Error validating team membership:", error);
|
|
@@ -367,7 +366,6 @@ async function validateTeamContextWithBypass(request, authResult, userId) {
|
|
|
367
366
|
const teamId = getTeamIdFromRequest(request);
|
|
368
367
|
const canBypass = await canBypassTeamContext(authResult, request);
|
|
369
368
|
if (canBypass) {
|
|
370
|
-
console.log("[GenericHandler] Admin bypass active:", { userId, teamId: teamId || "cross-team" });
|
|
371
369
|
return { valid: true, teamId, isBypass: true };
|
|
372
370
|
}
|
|
373
371
|
if (!teamId) {
|
|
@@ -788,7 +786,7 @@ async function handleGenericCreate(request) {
|
|
|
788
786
|
return runWithAuditLog(request, (audit) => handleGenericCreateImpl(request, audit));
|
|
789
787
|
}
|
|
790
788
|
async function handleGenericCreateImpl(request, audit) {
|
|
791
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
789
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
792
790
|
try {
|
|
793
791
|
const resolution = await resolveEntityFromUrl(request.nextUrl.pathname);
|
|
794
792
|
if (!resolution.isValidEntity || !resolution.entityConfig) {
|
|
@@ -889,6 +887,13 @@ async function handleGenericCreateImpl(request, audit) {
|
|
|
889
887
|
);
|
|
890
888
|
return addCorsHeaders(response2, request);
|
|
891
889
|
}
|
|
890
|
+
const createStatusField = entityConfig.fields.find((f) => f.name === "status");
|
|
891
|
+
const createStatus = validatedData.status ?? (createStatusField == null ? void 0 : createStatusField.defaultValue);
|
|
892
|
+
if (publishPermissionFor(entityConfig.slug, createStatus)) {
|
|
893
|
+
const publishDenied = await checkAuthPermission(authResult, entityConfig.slug, "publish", teamId, request);
|
|
894
|
+
if (publishDenied) return publishDenied;
|
|
895
|
+
}
|
|
896
|
+
const checkStoredStatus = createStatus === void 0 && !!createStatusField && !!declaredPublishPermission(entityConfig.slug);
|
|
892
897
|
if (idStrategy === "serial") {
|
|
893
898
|
insertFields = ['"userId"', '"teamId"'];
|
|
894
899
|
placeholders = ["$1", "$2"];
|
|
@@ -987,8 +992,28 @@ async function handleGenericCreateImpl(request, audit) {
|
|
|
987
992
|
INSERT INTO "${tableName}" (${insertFields.join(", ")})
|
|
988
993
|
VALUES (${placeholders.join(", ")}) RETURNING *
|
|
989
994
|
`;
|
|
990
|
-
|
|
991
|
-
|
|
995
|
+
let insertResult;
|
|
996
|
+
if (checkStoredStatus) {
|
|
997
|
+
const tx = await getTransactionClient(authResult.user.id);
|
|
998
|
+
try {
|
|
999
|
+
insertResult = await tx.mutate(insertQuery, values);
|
|
1000
|
+
if (((_g = insertResult.rows[0]) == null ? void 0 : _g.status) === "published") {
|
|
1001
|
+
const publishDenied = await checkAuthPermission(authResult, entityConfig.slug, "publish", teamId, request);
|
|
1002
|
+
if (publishDenied) {
|
|
1003
|
+
await tx.rollback();
|
|
1004
|
+
return publishDenied;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
await tx.commit();
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
await tx.rollback().catch(() => {
|
|
1010
|
+
});
|
|
1011
|
+
throw error;
|
|
1012
|
+
}
|
|
1013
|
+
} else {
|
|
1014
|
+
insertResult = await mutateWithRLS(insertQuery, values, authResult.user.id);
|
|
1015
|
+
}
|
|
1016
|
+
const createdEntityId = String((_h = insertResult.rows[0]) == null ? void 0 : _h.id);
|
|
992
1017
|
if (limitSlug) {
|
|
993
1018
|
UsageService.track({
|
|
994
1019
|
teamId,
|
|
@@ -1018,8 +1043,8 @@ async function handleGenericCreateImpl(request, audit) {
|
|
|
1018
1043
|
`;
|
|
1019
1044
|
const createdItems = await queryWithRLS(selectQuery, [createdEntityId], authResult.user.id);
|
|
1020
1045
|
const createdItem = createdItems[0];
|
|
1021
|
-
if ((
|
|
1022
|
-
await processTaxonomyRelations(entityConfig, createdEntityId, body, authResult.user.id, false);
|
|
1046
|
+
if ((_i = entityConfig.taxonomies) == null ? void 0 : _i.enabled) {
|
|
1047
|
+
await processTaxonomyRelations(entityConfig, createdEntityId, body, authResult.user.id, teamId, false);
|
|
1023
1048
|
}
|
|
1024
1049
|
const metadataWasProvided = metas && typeof metas === "object" && Object.keys(metas).length > 0;
|
|
1025
1050
|
if (metadataWasProvided) {
|
|
@@ -1031,7 +1056,7 @@ async function handleGenericCreateImpl(request, audit) {
|
|
|
1031
1056
|
);
|
|
1032
1057
|
}
|
|
1033
1058
|
let responseItem = createdItem;
|
|
1034
|
-
if ((
|
|
1059
|
+
if ((_j = entityConfig.taxonomies) == null ? void 0 : _j.enabled) {
|
|
1035
1060
|
const itemsWithTaxonomies = await includeTaxonomiesInData(
|
|
1036
1061
|
entityConfig,
|
|
1037
1062
|
[createdItem],
|
|
@@ -1330,6 +1355,18 @@ async function handleGenericUpdateImpl(request, audit, { params }) {
|
|
|
1330
1355
|
);
|
|
1331
1356
|
return addCorsHeaders(response2, request);
|
|
1332
1357
|
}
|
|
1358
|
+
let statusRead;
|
|
1359
|
+
if ("status" in entityData && entityConfig.fields.some((f) => f.name === "status") && declaredPublishPermission(entityConfig.slug)) {
|
|
1360
|
+
statusRead = (await queryWithRLS(
|
|
1361
|
+
`SELECT "status" FROM "${tableName}" WHERE id = $1 AND "teamId" = $2`,
|
|
1362
|
+
[id, teamId],
|
|
1363
|
+
authResult.user.id
|
|
1364
|
+
))[0];
|
|
1365
|
+
if (statusRead && publishPermissionFor(entityConfig.slug, validatedData.status, statusRead.status)) {
|
|
1366
|
+
const publishDenied = await checkAuthPermission(authResult, entityConfig.slug, "publish", teamId, request);
|
|
1367
|
+
if (publishDenied) return publishDenied;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1333
1370
|
const updates = [];
|
|
1334
1371
|
const values = [];
|
|
1335
1372
|
let paramCount = 1;
|
|
@@ -1428,6 +1465,10 @@ async function handleGenericUpdateImpl(request, audit, { params }) {
|
|
|
1428
1465
|
whereConditions.push(`"${updateOwnershipFilter.field}" = $${paramCount++}`);
|
|
1429
1466
|
values.push(updateOwnershipFilter.value);
|
|
1430
1467
|
}
|
|
1468
|
+
if (statusRead) {
|
|
1469
|
+
whereConditions.push(`"status" IS NOT DISTINCT FROM $${paramCount++}`);
|
|
1470
|
+
values.push(statusRead.status);
|
|
1471
|
+
}
|
|
1431
1472
|
const updateQuery = `
|
|
1432
1473
|
UPDATE "${tableName}"
|
|
1433
1474
|
SET ${updates.join(", ")}
|
|
@@ -1436,6 +1477,16 @@ async function handleGenericUpdateImpl(request, audit, { params }) {
|
|
|
1436
1477
|
`;
|
|
1437
1478
|
const result = await mutateWithRLS(updateQuery, values, authResult.user.id);
|
|
1438
1479
|
if (!result.rows || result.rows.length === 0) {
|
|
1480
|
+
if (statusRead) {
|
|
1481
|
+
const [now] = await queryWithRLS(
|
|
1482
|
+
`SELECT "status" FROM "${tableName}" WHERE id = $1 AND "teamId" = $2`,
|
|
1483
|
+
[id, teamId],
|
|
1484
|
+
authResult.user.id
|
|
1485
|
+
);
|
|
1486
|
+
if (now && now.status !== statusRead.status) {
|
|
1487
|
+
return addCorsHeaders(createApiError("The record changed while it was being saved. Reload it and try again.", 409, void 0, "STATUS_CHANGED"), request);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1439
1490
|
const response2 = createApiError("Item not found", 404);
|
|
1440
1491
|
return addCorsHeaders(response2, request);
|
|
1441
1492
|
}
|
|
@@ -1455,7 +1506,7 @@ async function handleGenericUpdateImpl(request, audit, { params }) {
|
|
|
1455
1506
|
updatedItem = result[0];
|
|
1456
1507
|
}
|
|
1457
1508
|
if ((_g = entityConfig.taxonomies) == null ? void 0 : _g.enabled) {
|
|
1458
|
-
await processTaxonomyRelations(entityConfig, id, body, authResult.user.id, true);
|
|
1509
|
+
await processTaxonomyRelations(entityConfig, id, body, authResult.user.id, teamId, true);
|
|
1459
1510
|
}
|
|
1460
1511
|
if (metadataWasProvided) {
|
|
1461
1512
|
await processEntityMetadata(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getAuthorizationSession } from "../auth/authorization-session.js";
|
|
2
2
|
import { entityRegistry, ensureInitialized } from "../entities/registry.js";
|
|
3
3
|
import {
|
|
4
4
|
createApiResponse,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { afterEntityCreate, afterEntityUpdate, afterEntityDelete } from "../entities/entity-hooks.js";
|
|
14
14
|
async function checkBasicAuth(request, entityName) {
|
|
15
15
|
try {
|
|
16
|
-
const session = await
|
|
16
|
+
const session = await getAuthorizationSession(request.headers);
|
|
17
17
|
if (!session) {
|
|
18
18
|
return {
|
|
19
19
|
allowed: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAsC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/api/helpers.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { getValidatedApiKey, validateApiKey } from "./auth.js";
|
|
3
3
|
import { mutateWithRLS, queryWithRLS } from "../db.js";
|
|
4
|
-
import {
|
|
4
|
+
import { apiKeyRateLimitResponse } from "./rate-limit.js";
|
|
5
5
|
import { getRecordedClientIp } from "./client-ip.js";
|
|
6
6
|
import { getApplicationConfig } from "../config/index.js";
|
|
7
7
|
import { auth } from "../auth.js";
|
|
8
|
+
import { isSuspendedRole } from "../auth/suspension.js";
|
|
8
9
|
import { MetaService } from "../services/meta.service.js";
|
|
9
10
|
import { ScopeService } from "../services/scope.service.js";
|
|
10
11
|
import { getChildEntities } from "../entities/queries.js";
|
|
@@ -25,9 +26,10 @@ async function validateAndAuthenticateRequest(request, options = {}) {
|
|
|
25
26
|
const session = await auth.api.getSession({
|
|
26
27
|
headers: new Headers({
|
|
27
28
|
cookie: cookieHeader
|
|
28
|
-
})
|
|
29
|
+
}),
|
|
30
|
+
query: { disableCookieCache: true }
|
|
29
31
|
});
|
|
30
|
-
if ((_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id) {
|
|
32
|
+
if (((_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id) && !isSuspendedRole(session.user.role)) {
|
|
31
33
|
const userRole = session.user.role || "member";
|
|
32
34
|
const userFlags = session.user.flags || [];
|
|
33
35
|
const sessionAuth = {
|
|
@@ -36,7 +38,6 @@ async function validateAndAuthenticateRequest(request, options = {}) {
|
|
|
36
38
|
flags: userFlags,
|
|
37
39
|
isSession: true
|
|
38
40
|
};
|
|
39
|
-
console.log(`[Auth] Session authentication successful for user ${session.user.id} with role ${userRole}`);
|
|
40
41
|
return { auth: sessionAuth };
|
|
41
42
|
}
|
|
42
43
|
} catch (sessionError) {
|
|
@@ -58,7 +59,8 @@ async function validateAndAuthenticateRequest(request, options = {}) {
|
|
|
58
59
|
)
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
|
-
|
|
62
|
+
const rateLimitResponse = apiKeyRateLimitResponse(request, apiKeyAuth);
|
|
63
|
+
return rateLimitResponse ? { auth: apiKeyAuth, rateLimitResponse } : { auth: apiKeyAuth };
|
|
62
64
|
}
|
|
63
65
|
function generateScopesForRole(role, flags = []) {
|
|
64
66
|
const baseScopes = getBaseScopesFromRegistry();
|
|
@@ -103,25 +105,8 @@ async function validateAndAuthenticateApiRequest(request) {
|
|
|
103
105
|
if (!auth2) {
|
|
104
106
|
throw new Error("Invalid API key");
|
|
105
107
|
}
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
function applyApiKeyRateLimit(auth2) {
|
|
109
|
-
const rateLimitResult = checkRateLimit(auth2.keyId);
|
|
110
|
-
if (!rateLimitResult.allowed) {
|
|
111
|
-
const response = NextResponse.json(
|
|
112
|
-
{
|
|
113
|
-
success: false,
|
|
114
|
-
error: "Rate limit exceeded",
|
|
115
|
-
message: `Too many requests. Limit: ${rateLimitResult.limit} requests per minute`,
|
|
116
|
-
code: "RATE_LIMIT_EXCEEDED",
|
|
117
|
-
retryAfter: Math.ceil((rateLimitResult.resetTime - Date.now()) / 1e3)
|
|
118
|
-
},
|
|
119
|
-
{ status: 429 }
|
|
120
|
-
);
|
|
121
|
-
addRateLimitHeaders(response, auth2.keyId, auth2.scopes);
|
|
122
|
-
return { auth: auth2, rateLimitResponse: response };
|
|
123
|
-
}
|
|
124
|
-
return { auth: auth2 };
|
|
108
|
+
const rateLimitResponse = apiKeyRateLimitResponse(request, auth2);
|
|
109
|
+
return rateLimitResponse ? { auth: auth2, rateLimitResponse } : { auth: auth2 };
|
|
125
110
|
}
|
|
126
111
|
function getApiAuth(request) {
|
|
127
112
|
const auth2 = getValidatedApiKey(request);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission-middleware.d.ts","sourceRoot":"","sources":["../../../src/lib/api/permission-middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAc1C;;GAEG;AACH,eAAO,MAAM,WAAW,SAAS,CAAA;AAEjC;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAElF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAOjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"permission-middleware.d.ts","sourceRoot":"","sources":["../../../src/lib/api/permission-middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAc1C;;GAEG;AACH,eAAO,MAAM,WAAW,SAAS,CAAA;AAEjC;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAElF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAOjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC,CA8BtE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GACpB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAK9B"}
|
|
@@ -21,9 +21,7 @@ function getActionFromMethod(method, isCollection) {
|
|
|
21
21
|
}
|
|
22
22
|
async function checkEntityPermission(userId, teamId, entitySlug, action) {
|
|
23
23
|
const permission = getEntityPermission(entitySlug, action);
|
|
24
|
-
console.log(`[PermMiddleware] Checking permission: ${permission} for user ${userId} in team ${teamId}`);
|
|
25
24
|
const hasPermission = await checkPermission(userId, teamId, permission);
|
|
26
|
-
console.log(`[PermMiddleware] Result: ${hasPermission}`);
|
|
27
25
|
if (!hasPermission) {
|
|
28
26
|
return {
|
|
29
27
|
allowed: false,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
/**
|
|
3
|
+
* Post categories (`taxonomies` rows of type `post_category`) belong to a team: they are created in the caller's
|
|
4
|
+
* team (`x-team-id`) and read, changed and deleted only there, under the same team-role permissions as the posts
|
|
5
|
+
* entity (`posts.list`, `posts.create`, `posts.update`, `posts.delete`). Rows without a team (the only kind before
|
|
6
|
+
* categories were scoped) stay readable by every team and by visitors; only a superadmin can change or delete them.
|
|
7
|
+
*/
|
|
8
|
+
/** A refusal the route returns as it is. */
|
|
9
|
+
export type Refusal = {
|
|
10
|
+
response: NextResponse;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The team whose categories a read sees, besides the rows without a team. No `x-team-id`: none (a visitor, or a
|
|
14
|
+
* caller without a team context, sees only the rows without a team). With one: the caller must be able to list
|
|
15
|
+
* posts there.
|
|
16
|
+
*/
|
|
17
|
+
export declare function categoryReadTeam(request: NextRequest): Promise<{
|
|
18
|
+
teamId: string | null;
|
|
19
|
+
} | Refusal>;
|
|
20
|
+
export interface CategoryWriter {
|
|
21
|
+
userId: string;
|
|
22
|
+
/** The caller's team (`x-team-id`), when the caller holds `posts.<action>` there. */
|
|
23
|
+
teamId: string | null;
|
|
24
|
+
/** A superadmin may change or delete the rows without a team. */
|
|
25
|
+
superadmin: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Who writes, and in which team, after the API-key scope and the team-role check for `posts.<action>`. */
|
|
28
|
+
export declare function categoryWriter(request: NextRequest, action: 'create' | 'update' | 'delete'): Promise<CategoryWriter | Refusal>;
|
|
29
|
+
//# sourceMappingURL=post-categories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-categories.d.ts","sourceRoot":"","sources":["../../../src/lib/api/post-categories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAIvD;;;;;GAKG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,EAAE,YAAY,CAAA;CAAE,CAAA;AAOhD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,CASzG;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,iEAAiE;IACjE,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,2GAA2G;AAC3G,wBAAsB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC,cAAc,GAAG,OAAO,CAAC,CAYpI"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { authenticateRequest, createAuthFailureResponse } from "./auth/dual-auth.js";
|
|
3
|
+
import { checkPermission } from "../permissions/check.js";
|
|
4
|
+
const deny = (status, code, error) => ({ response: NextResponse.json({ success: false, error, code }, { status }) });
|
|
5
|
+
const isSuperadmin = (auth) => {
|
|
6
|
+
var _a;
|
|
7
|
+
return ((_a = auth.user) == null ? void 0 : _a.role) === "superadmin";
|
|
8
|
+
};
|
|
9
|
+
async function categoryReadTeam(request) {
|
|
10
|
+
const teamId = request.headers.get("x-team-id");
|
|
11
|
+
if (!teamId) return { teamId: null };
|
|
12
|
+
const auth = await authenticateRequest(request, { requiredScope: "posts:read" });
|
|
13
|
+
if (!auth.success || !auth.user) return { response: createAuthFailureResponse(auth) };
|
|
14
|
+
if (!isSuperadmin(auth) && !await checkPermission(auth.user.id, teamId, "posts.list")) {
|
|
15
|
+
return deny(403, "PERMISSION_DENIED", "Permission denied: insufficient permissions for posts.list");
|
|
16
|
+
}
|
|
17
|
+
return { teamId };
|
|
18
|
+
}
|
|
19
|
+
async function categoryWriter(request, action) {
|
|
20
|
+
const auth = await authenticateRequest(request, { requiredScope: action === "delete" ? "posts:delete" : "posts:write" });
|
|
21
|
+
if (!auth.success || !auth.user) return { response: createAuthFailureResponse(auth) };
|
|
22
|
+
const superadmin = isSuperadmin(auth);
|
|
23
|
+
const requested = request.headers.get("x-team-id");
|
|
24
|
+
const teamId = requested && await checkPermission(auth.user.id, requested, `posts.${action}`) ? requested : null;
|
|
25
|
+
if (!teamId && !(superadmin && action !== "create")) {
|
|
26
|
+
return requested ? deny(403, "PERMISSION_DENIED", `Permission denied: insufficient permissions for posts.${action}`) : deny(400, "TEAM_CONTEXT_REQUIRED", "Team context required. Include x-team-id header.");
|
|
27
|
+
}
|
|
28
|
+
return { userId: auth.user.id, teamId, superadmin };
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
categoryReadTeam,
|
|
32
|
+
categoryWriter
|
|
33
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { type ApiKeyAuth } from './auth';
|
|
2
3
|
import { type RateLimitTier } from '../rate-limit-redis';
|
|
3
4
|
export interface RateLimitResult {
|
|
4
5
|
allowed: boolean;
|
|
@@ -10,6 +11,13 @@ export interface RateLimitResult {
|
|
|
10
11
|
* Verifica el rate limit para un identificador específico
|
|
11
12
|
*/
|
|
12
13
|
export declare function checkRateLimit(identifier: string, limit?: number, windowMs?: number): RateLimitResult;
|
|
14
|
+
/**
|
|
15
|
+
* The per-key limit (checkRateLimit, by the key's id) for a request authenticated with `auth`: a 429 ready to
|
|
16
|
+
* return when the key is over it, null otherwise. Counted once per request, however many times the request is
|
|
17
|
+
* authenticated (authenticateRequest, validateAndAuthenticate*Request). The per-address limit of the route
|
|
18
|
+
* wrapper (withRateLimitTier) still applies on top. It is a safety cap, so DISABLE_RATE_LIMITING does not turn it off.
|
|
19
|
+
*/
|
|
20
|
+
export declare function apiKeyRateLimitResponse(request: Request, auth: ApiKeyAuth): NextResponse | null;
|
|
13
21
|
/**
|
|
14
22
|
* Rate limit types for distributed rate limiting
|
|
15
23
|
* Re-export from rate-limit-redis for convenience
|
|
@@ -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;
|
|
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;AAGxD,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGzD,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAI7B,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;AAKD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CA0B/F;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;;;;;;GAMG;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,CA2BvE;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;AA4DD,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,IAqCb,SAAS,WAAW,EAAE,GAAG,MAAM,CAAC,KAAG,OAAO,CAAC,YAAY,CAAC,CAIvE"}
|