@getstrata/core 0.5.49 → 0.5.51
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 +4 -2
- package/dist/core/auth/membershipContextMiddleware.d.ts +1 -1
- package/dist/core/auth/scimAuthMiddleware.d.ts +1 -1
- package/dist/core/database/errors.d.ts +1 -1
- package/dist/core/facades/index.d.ts +1 -1
- package/dist/core/http/authMiddleware.d.ts +2 -2
- package/dist/core/http/authorizeMiddleware.d.ts +2 -2
- package/dist/core/http/index.d.ts +1 -1
- package/dist/core/http/requireAuthMiddleware.d.ts +1 -1
- package/dist/core/http/requireWebAuthMiddleware.d.ts +1 -1
- package/dist/core/http/securedRouteModelBinding.d.ts +1 -1
- package/dist/core/logging/requestLoggingMiddleware.d.ts +1 -1
- package/dist/core/queue/failedJobRepository.d.ts +1 -1
- package/dist/core/queue/failedJobTable.d.ts +1 -1
- package/dist/core/runtime/applicationRegistry.d.ts +3 -3
- package/dist/core/tracing/tracingMiddleware.d.ts +1 -1
- package/dist/entries/audit/exportAuditLogs.js +9 -175
- package/dist/entries/auth/membershipMiddleware.js +2 -74
- package/dist/entries/auth/scimAuthMiddleware.js +10 -93
- package/dist/entries/auth/sessionGuard.js +0 -6
- package/dist/entries/database/errors.js +6 -66
- package/dist/entries/database/model.js +2 -65
- package/dist/entries/http/authMiddleware.js +1 -23
- package/dist/entries/http/authorizeMiddleware.js +2 -89
- package/dist/entries/http/bodySizeLimitMiddleware.js +1 -66
- package/dist/entries/http/conditionalResponse.js +3 -66
- package/dist/entries/http/csrfMiddleware.js +2 -65
- package/dist/entries/http/etag.js +3 -66
- package/dist/entries/http/formRequest.js +5 -107
- package/dist/entries/http/pagination.js +8 -110
- package/dist/entries/http/parseFormBody.js +1 -66
- package/dist/entries/http/parseMultipartUpload.js +3 -66
- package/dist/entries/http/requireAbilityMiddleware.js +2 -89
- package/dist/entries/http/requireAuthMiddleware.js +1 -66
- package/dist/entries/http/requireGlobalAdminMiddleware.js +4 -128
- package/dist/entries/http/requireWebAuthMiddleware.js +2 -65
- package/dist/entries/http/routeModelBinding.js +3 -108
- package/dist/entries/http/securedRouteModelBinding.js +15 -203
- package/dist/entries/http/throttleMiddleware.js +2 -44
- package/dist/entries/http/webErrorResponse.js +25 -93
- package/dist/entries/http/webFormRequest.js +8 -109
- package/dist/entries/jobs/dispatchWebhookJob.js +5 -174
- package/dist/entries/logging/requestLoggingMiddleware.js +2 -25
- package/dist/entries/queue/createAppQueue.js +4 -2324
- package/dist/entries/queue/failedJobRepository.js +4 -2324
- package/dist/entries/queue/publicQueue.js +4 -2324
- package/dist/entries/queue/queueMetrics.js +4 -2324
- package/dist/entries/security/safeFetch.js +1 -68
- package/dist/entries/security/safeUrl.js +1 -68
- package/dist/entries/security/stripeWebhook.js +1 -68
- package/dist/entries/tenant/databaseTenantContext.js +3 -105
- package/dist/entries/tenant/tenantDatabaseScope.js +3 -58
- package/dist/entries/validation/rules.js +1 -66
- package/dist/entries/view.js +5 -16
- package/dist/framework/public-api.d.ts +3 -2
- package/dist/index.js +3 -0
- package/package.json +2 -2
|
@@ -1,113 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
3
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
4
|
-
function createAsyncContextStore(key) {
|
|
5
|
-
const symbol = Symbol.for(key);
|
|
6
|
-
const globalRecord = globalThis;
|
|
7
|
-
const existing = globalRecord[symbol];
|
|
8
|
-
if (existing) {
|
|
9
|
-
return existing;
|
|
10
|
-
}
|
|
11
|
-
const store = new AsyncLocalStorage;
|
|
12
|
-
globalRecord[symbol] = store;
|
|
13
|
-
return store;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// ../../src/core/auth/authContext.ts
|
|
17
|
-
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
18
|
-
function runWithAuthUser(user, callback) {
|
|
19
|
-
return authContext.run(user, callback);
|
|
20
|
-
}
|
|
21
|
-
function currentAuthUser() {
|
|
22
|
-
return authContext.getStore() ?? null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ../../src/core/errors/http.ts
|
|
26
|
-
class HttpError extends Error {
|
|
27
|
-
status;
|
|
28
|
-
details;
|
|
29
|
-
constructor(status, message, details) {
|
|
30
|
-
super(message);
|
|
31
|
-
this.name = new.target.name;
|
|
32
|
-
this.status = status;
|
|
33
|
-
this.details = details;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
class BadRequestError extends HttpError {
|
|
38
|
-
constructor(message = "Bad Request", details) {
|
|
39
|
-
super(400, message, details);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
class NotFoundError extends HttpError {
|
|
44
|
-
constructor(message = "Not Found", details) {
|
|
45
|
-
super(404, message, details);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
class ConflictError extends HttpError {
|
|
50
|
-
constructor(message = "Conflict", details) {
|
|
51
|
-
super(409, message, details);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
class UnprocessableEntityError extends HttpError {
|
|
56
|
-
constructor(message = "Unprocessable Entity", details) {
|
|
57
|
-
super(422, message, details);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
class ValidationError extends HttpError {
|
|
62
|
-
constructor(message = "Validation failed", details) {
|
|
63
|
-
super(422, message, details);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
class ForbiddenError extends HttpError {
|
|
68
|
-
constructor(message = "Forbidden", details) {
|
|
69
|
-
super(403, message, details);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
class UnauthorizedError extends HttpError {
|
|
74
|
-
constructor(message = "Unauthorized", details) {
|
|
75
|
-
super(401, message, details);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
class PayloadTooLargeError extends HttpError {
|
|
80
|
-
constructor(message = "Payload Too Large", details) {
|
|
81
|
-
super(413, message, details);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
class PreconditionFailedError extends HttpError {
|
|
86
|
-
constructor(message = "Precondition Failed", details) {
|
|
87
|
-
super(412, message, details);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ../../src/core/tenant/tenantContext.ts
|
|
92
|
-
var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
93
|
-
function runWithTenant(tenant, callback) {
|
|
94
|
-
return tenantContext.run(tenant, callback);
|
|
95
|
-
}
|
|
96
|
-
function currentTenant() {
|
|
97
|
-
return tenantContext.getStore() ?? null;
|
|
98
|
-
}
|
|
99
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
100
|
-
switch (plan) {
|
|
101
|
-
case "enterprise":
|
|
102
|
-
return 4;
|
|
103
|
-
case "pro":
|
|
104
|
-
return 2;
|
|
105
|
-
default:
|
|
106
|
-
return 1;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
2
|
// ../../src/core/http/validation.ts
|
|
3
|
+
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
4
|
+
import { BadRequestError } from "@getstrata/core/errors/http";
|
|
5
|
+
import { currentTenantId } from "@getstrata/core/tenant/tenantContext";
|
|
111
6
|
function getQueryParams(request) {
|
|
112
7
|
if (!request) {
|
|
113
8
|
return new URLSearchParams;
|
|
@@ -1,186 +1,14 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/core/
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return existing;
|
|
10
|
-
}
|
|
11
|
-
const store = new AsyncLocalStorage;
|
|
12
|
-
globalRecord[symbol] = store;
|
|
13
|
-
return store;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// ../../src/core/auth/authContext.ts
|
|
17
|
-
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
18
|
-
function runWithAuthUser(user, callback) {
|
|
19
|
-
return authContext.run(user, callback);
|
|
20
|
-
}
|
|
21
|
-
function currentAuthUser() {
|
|
22
|
-
return authContext.getStore() ?? null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ../../src/core/errors/http.ts
|
|
26
|
-
class HttpError extends Error {
|
|
27
|
-
status;
|
|
28
|
-
details;
|
|
29
|
-
constructor(status, message, details) {
|
|
30
|
-
super(message);
|
|
31
|
-
this.name = new.target.name;
|
|
32
|
-
this.status = status;
|
|
33
|
-
this.details = details;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
class BadRequestError extends HttpError {
|
|
38
|
-
constructor(message = "Bad Request", details) {
|
|
39
|
-
super(400, message, details);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
class NotFoundError extends HttpError {
|
|
44
|
-
constructor(message = "Not Found", details) {
|
|
45
|
-
super(404, message, details);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
class ConflictError extends HttpError {
|
|
50
|
-
constructor(message = "Conflict", details) {
|
|
51
|
-
super(409, message, details);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
class UnprocessableEntityError extends HttpError {
|
|
56
|
-
constructor(message = "Unprocessable Entity", details) {
|
|
57
|
-
super(422, message, details);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
class ValidationError extends HttpError {
|
|
62
|
-
constructor(message = "Validation failed", details) {
|
|
63
|
-
super(422, message, details);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
class ForbiddenError extends HttpError {
|
|
68
|
-
constructor(message = "Forbidden", details) {
|
|
69
|
-
super(403, message, details);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
class UnauthorizedError extends HttpError {
|
|
74
|
-
constructor(message = "Unauthorized", details) {
|
|
75
|
-
super(401, message, details);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
class PayloadTooLargeError extends HttpError {
|
|
80
|
-
constructor(message = "Payload Too Large", details) {
|
|
81
|
-
super(413, message, details);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
class PreconditionFailedError extends HttpError {
|
|
86
|
-
constructor(message = "Precondition Failed", details) {
|
|
87
|
-
super(412, message, details);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ../../src/core/contracts/di.ts
|
|
92
|
-
var requiredDependencyKeys = [
|
|
93
|
-
"container",
|
|
94
|
-
"cache",
|
|
95
|
-
"storage"
|
|
96
|
-
];
|
|
97
|
-
function getRequiredDependency(dependencies, key) {
|
|
98
|
-
const dependency = dependencies[key];
|
|
99
|
-
if (dependency === undefined) {
|
|
100
|
-
throw new Error(`Required dependency "${key}" is not registered.`);
|
|
101
|
-
}
|
|
102
|
-
return dependency;
|
|
103
|
-
}
|
|
104
|
-
function assertAppDependenciesComplete(dependencies) {
|
|
105
|
-
for (const key of requiredDependencyKeys) {
|
|
106
|
-
getRequiredDependency(dependencies, key);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
function resolveService(dependencies, token) {
|
|
110
|
-
return dependencies.container.resolve(token);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ../../src/core/contracts/serviceTokens.ts
|
|
114
|
-
var CORE_CONFIG_TOKEN = "core.config";
|
|
115
|
-
var CORE_CACHE_TOKEN = "core.cache";
|
|
116
|
-
var CORE_QUEUE_TOKEN = "core.queue";
|
|
117
|
-
var CORE_EVENT_BUS_TOKEN = "core.eventBus";
|
|
118
|
-
var CORE_POLICY_GATE_TOKEN = "core.policyGate";
|
|
119
|
-
var CORE_AUTH_TOKEN = "core.auth";
|
|
120
|
-
var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
|
|
121
|
-
|
|
122
|
-
// ../../src/core/logging/logger.ts
|
|
123
|
-
class Logger {
|
|
124
|
-
channel;
|
|
125
|
-
constructor(channel = "app") {
|
|
126
|
-
this.channel = channel;
|
|
127
|
-
}
|
|
128
|
-
write(level, message, context = {}) {
|
|
129
|
-
const entry = {
|
|
130
|
-
level,
|
|
131
|
-
channel: this.channel,
|
|
132
|
-
message,
|
|
133
|
-
timestamp: new Date().toISOString(),
|
|
134
|
-
...context
|
|
135
|
-
};
|
|
136
|
-
const line = JSON.stringify(entry);
|
|
137
|
-
if (level === "error") {
|
|
138
|
-
console.error(line);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
console.log(line);
|
|
142
|
-
}
|
|
143
|
-
debug(message, context) {
|
|
144
|
-
this.write("debug", message, context);
|
|
145
|
-
}
|
|
146
|
-
info(message, context) {
|
|
147
|
-
this.write("info", message, context);
|
|
148
|
-
}
|
|
149
|
-
warn(message, context) {
|
|
150
|
-
this.write("warn", message, context);
|
|
151
|
-
}
|
|
152
|
-
error(message, context) {
|
|
153
|
-
this.write("error", message, context);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
var appLogger = new Logger("app");
|
|
2
|
+
// ../../src/core/http/securedRouteModelBinding.ts
|
|
3
|
+
import { currentAuthUser as currentAuthUser2 } from "@getstrata/core/auth/authContext";
|
|
4
|
+
import { BadRequestError as BadRequestError2 } from "@getstrata/core/errors/http";
|
|
5
|
+
import {
|
|
6
|
+
resolveApplicationAuth,
|
|
7
|
+
resolveApplicationPolicyGate
|
|
8
|
+
} from "@getstrata/core/runtime/applicationRegistry";
|
|
157
9
|
|
|
158
|
-
// ../../src/core/
|
|
159
|
-
|
|
160
|
-
var activeContext;
|
|
161
|
-
function readStoredApplicationContext() {
|
|
162
|
-
if (activeContext) {
|
|
163
|
-
return activeContext;
|
|
164
|
-
}
|
|
165
|
-
const globalContext = globalThis[APPLICATION_CONTEXT_KEY];
|
|
166
|
-
if (globalContext) {
|
|
167
|
-
activeContext = globalContext;
|
|
168
|
-
}
|
|
169
|
-
return activeContext;
|
|
170
|
-
}
|
|
171
|
-
function requireActiveApplicationContext() {
|
|
172
|
-
const context = readStoredApplicationContext();
|
|
173
|
-
if (!context) {
|
|
174
|
-
throw new Error("The application context has not been bootstrapped.");
|
|
175
|
-
}
|
|
176
|
-
return context;
|
|
177
|
-
}
|
|
178
|
-
function resolveApplicationAuth() {
|
|
179
|
-
return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
|
|
180
|
-
}
|
|
181
|
-
function resolveApplicationPolicyGate() {
|
|
182
|
-
return requireActiveApplicationContext().container.resolve(CORE_POLICY_GATE_TOKEN);
|
|
183
|
-
}
|
|
10
|
+
// ../../src/core/http/etag.ts
|
|
11
|
+
import { PreconditionFailedError } from "@getstrata/core/errors/http";
|
|
184
12
|
|
|
185
13
|
// ../../src/core/crypto/nonCryptographicHash.ts
|
|
186
14
|
function nonCryptographicDigest(input) {
|
|
@@ -277,26 +105,10 @@ function applyConditionalGet(request, response, etag) {
|
|
|
277
105
|
});
|
|
278
106
|
}
|
|
279
107
|
|
|
280
|
-
// ../../src/core/tenant/tenantContext.ts
|
|
281
|
-
var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
282
|
-
function runWithTenant(tenant, callback) {
|
|
283
|
-
return tenantContext.run(tenant, callback);
|
|
284
|
-
}
|
|
285
|
-
function currentTenant() {
|
|
286
|
-
return tenantContext.getStore() ?? null;
|
|
287
|
-
}
|
|
288
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
289
|
-
switch (plan) {
|
|
290
|
-
case "enterprise":
|
|
291
|
-
return 4;
|
|
292
|
-
case "pro":
|
|
293
|
-
return 2;
|
|
294
|
-
default:
|
|
295
|
-
return 1;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
108
|
// ../../src/core/http/validation.ts
|
|
109
|
+
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
110
|
+
import { BadRequestError } from "@getstrata/core/errors/http";
|
|
111
|
+
import { currentTenantId } from "@getstrata/core/tenant/tenantContext";
|
|
300
112
|
function getQueryParams(request) {
|
|
301
113
|
if (!request) {
|
|
302
114
|
return new URLSearchParams;
|
|
@@ -330,7 +142,7 @@ function securedBindRouteModel(param, resolver, authorization, handler) {
|
|
|
330
142
|
const model = await resolver(id, request);
|
|
331
143
|
const gate = resolveApplicationPolicyGate();
|
|
332
144
|
const auth = resolveApplicationAuth();
|
|
333
|
-
const user =
|
|
145
|
+
const user = currentAuthUser2() ?? await auth.resolve(request);
|
|
334
146
|
gate.authorize(authorization.resource, authorization.action, user, model);
|
|
335
147
|
if (isEtagEnabled() && isMutatingPolicyAction(authorization.action)) {
|
|
336
148
|
assertIfMatch(request, etagFromResource(model), {
|
|
@@ -348,12 +160,12 @@ function securedBindRouteModelByKey(param, resolver, authorization, handler) {
|
|
|
348
160
|
return async (request) => {
|
|
349
161
|
const key = String(request.params[param] ?? "").trim();
|
|
350
162
|
if (!key) {
|
|
351
|
-
throw new
|
|
163
|
+
throw new BadRequestError2(`Missing route parameter "${String(param)}".`);
|
|
352
164
|
}
|
|
353
165
|
const model = await resolver(key, request);
|
|
354
166
|
const gate = resolveApplicationPolicyGate();
|
|
355
167
|
const auth = resolveApplicationAuth();
|
|
356
|
-
const user =
|
|
168
|
+
const user = currentAuthUser2() ?? await auth.resolve(request);
|
|
357
169
|
gate.authorize(authorization.resource, authorization.action, user, model);
|
|
358
170
|
if (isEtagEnabled() && isMutatingPolicyAction(authorization.action)) {
|
|
359
171
|
assertIfMatch(request, etagFromResource(model), {
|
|
@@ -1,50 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/http/throttleMiddleware.ts
|
|
3
|
+
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
4
|
+
import { currentTenant, rateLimitMultiplierForPlan } from "@getstrata/core/tenant/tenantContext";
|
|
3
5
|
var {RedisClient } = globalThis.Bun;
|
|
4
|
-
|
|
5
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
6
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
7
|
-
function createAsyncContextStore(key) {
|
|
8
|
-
const symbol = Symbol.for(key);
|
|
9
|
-
const globalRecord = globalThis;
|
|
10
|
-
const existing = globalRecord[symbol];
|
|
11
|
-
if (existing) {
|
|
12
|
-
return existing;
|
|
13
|
-
}
|
|
14
|
-
const store = new AsyncLocalStorage;
|
|
15
|
-
globalRecord[symbol] = store;
|
|
16
|
-
return store;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// ../../src/core/auth/authContext.ts
|
|
20
|
-
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
21
|
-
function runWithAuthUser(user, callback) {
|
|
22
|
-
return authContext.run(user, callback);
|
|
23
|
-
}
|
|
24
|
-
function currentAuthUser() {
|
|
25
|
-
return authContext.getStore() ?? null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// ../../src/core/tenant/tenantContext.ts
|
|
29
|
-
var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
30
|
-
function runWithTenant(tenant, callback) {
|
|
31
|
-
return tenantContext.run(tenant, callback);
|
|
32
|
-
}
|
|
33
|
-
function currentTenant() {
|
|
34
|
-
return tenantContext.getStore() ?? null;
|
|
35
|
-
}
|
|
36
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
37
|
-
switch (plan) {
|
|
38
|
-
case "enterprise":
|
|
39
|
-
return 4;
|
|
40
|
-
case "pro":
|
|
41
|
-
return 2;
|
|
42
|
-
default:
|
|
43
|
-
return 1;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ../../src/core/http/throttleMiddleware.ts
|
|
48
6
|
function resolveThrottleIdentity(request) {
|
|
49
7
|
const user = currentAuthUser();
|
|
50
8
|
if (user?.tokenId !== undefined) {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// ../../src/core/http/webErrorResponse.ts
|
|
3
|
+
import { HttpError as HttpError2, UnauthorizedError as UnauthorizedError2, ValidationError } from "@getstrata/core/errors/http";
|
|
4
|
+
|
|
2
5
|
// ../../src/config/frontend.ts
|
|
3
6
|
function readFrontendMode() {
|
|
4
7
|
const mode = (process.env.FRONTEND_MODE ?? "api").trim();
|
|
@@ -14,73 +17,13 @@ function isViewsEnabled() {
|
|
|
14
17
|
return readFrontendMode() === "server-htmx";
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
// ../../src/core/errors/http.ts
|
|
18
|
-
class HttpError extends Error {
|
|
19
|
-
status;
|
|
20
|
-
details;
|
|
21
|
-
constructor(status, message, details) {
|
|
22
|
-
super(message);
|
|
23
|
-
this.name = new.target.name;
|
|
24
|
-
this.status = status;
|
|
25
|
-
this.details = details;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
class BadRequestError extends HttpError {
|
|
30
|
-
constructor(message = "Bad Request", details) {
|
|
31
|
-
super(400, message, details);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
class NotFoundError extends HttpError {
|
|
36
|
-
constructor(message = "Not Found", details) {
|
|
37
|
-
super(404, message, details);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
class ConflictError extends HttpError {
|
|
42
|
-
constructor(message = "Conflict", details) {
|
|
43
|
-
super(409, message, details);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
class UnprocessableEntityError extends HttpError {
|
|
48
|
-
constructor(message = "Unprocessable Entity", details) {
|
|
49
|
-
super(422, message, details);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
class ValidationError extends HttpError {
|
|
54
|
-
constructor(message = "Validation failed", details) {
|
|
55
|
-
super(422, message, details);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
class ForbiddenError extends HttpError {
|
|
60
|
-
constructor(message = "Forbidden", details) {
|
|
61
|
-
super(403, message, details);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
class UnauthorizedError extends HttpError {
|
|
66
|
-
constructor(message = "Unauthorized", details) {
|
|
67
|
-
super(401, message, details);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
class PayloadTooLargeError extends HttpError {
|
|
72
|
-
constructor(message = "Payload Too Large", details) {
|
|
73
|
-
super(413, message, details);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
class PreconditionFailedError extends HttpError {
|
|
78
|
-
constructor(message = "Precondition Failed", details) {
|
|
79
|
-
super(412, message, details);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
20
|
// ../../src/core/database/errors.ts
|
|
21
|
+
import {
|
|
22
|
+
BadRequestError,
|
|
23
|
+
ConflictError,
|
|
24
|
+
HttpError,
|
|
25
|
+
UnprocessableEntityError
|
|
26
|
+
} from "@getstrata/core/errors/http";
|
|
84
27
|
function isPostgresError(error) {
|
|
85
28
|
return typeof error === "object" && error !== null && (("errno" in error) || ("code" in error));
|
|
86
29
|
}
|
|
@@ -182,6 +125,10 @@ function htmlResponse(html, init = {}) {
|
|
|
182
125
|
function isHtmxRequest(request) {
|
|
183
126
|
return request.headers.get("HX-Request") === "true";
|
|
184
127
|
}
|
|
128
|
+
// ../../src/core/view/webLayoutData.ts
|
|
129
|
+
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
130
|
+
import { currentRequestMeta as currentRequestMeta2 } from "@getstrata/core/http/requestMetaContext";
|
|
131
|
+
|
|
185
132
|
// ../../src/bootstrap/config.ts
|
|
186
133
|
import {
|
|
187
134
|
CORE_AUTH_TOKEN,
|
|
@@ -297,12 +244,6 @@ function registerDefaultDatabasePool(connection) {
|
|
|
297
244
|
defaultPool.connection = connection;
|
|
298
245
|
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
299
246
|
}
|
|
300
|
-
function getDefaultDatabasePool() {
|
|
301
|
-
if (!defaultPool.connection) {
|
|
302
|
-
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
303
|
-
}
|
|
304
|
-
return defaultPool.connection;
|
|
305
|
-
}
|
|
306
247
|
function getDefaultDatabaseQuery() {
|
|
307
248
|
if (!defaultQuery.connection) {
|
|
308
249
|
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
@@ -373,7 +314,7 @@ var apiTokenTable = defineTable({
|
|
|
373
314
|
// ../../src/modules/user/authService.ts
|
|
374
315
|
import { verifyPassword } from "@getstrata/core/auth/password";
|
|
375
316
|
import { revealMfaSecret } from "@getstrata/core/crypto/mfaSecret";
|
|
376
|
-
import { UnauthorizedError
|
|
317
|
+
import { UnauthorizedError } from "@getstrata/core/errors/http";
|
|
377
318
|
import { logSecurityEvent } from "@getstrata/core/security/securityEvents";
|
|
378
319
|
import { resolveDefaultTokenExpiryDays } from "@getstrata/core/security/tokenExpiry";
|
|
379
320
|
import { verifyTotp } from "@getstrata/core/security/totp";
|
|
@@ -438,22 +379,22 @@ class AuthService {
|
|
|
438
379
|
const user = await this.users.findByEmail(email);
|
|
439
380
|
if (!user?.password_hash) {
|
|
440
381
|
logSecurityEvent("auth_login_failed", { reason: "unknown_user", email });
|
|
441
|
-
throw new
|
|
382
|
+
throw new UnauthorizedError("Invalid credentials.");
|
|
442
383
|
}
|
|
443
384
|
const valid = await verifyPassword(password, user.password_hash);
|
|
444
385
|
if (!valid) {
|
|
445
386
|
logSecurityEvent("auth_login_failed", { reason: "invalid_password", user_id: user.id });
|
|
446
|
-
throw new
|
|
387
|
+
throw new UnauthorizedError("Invalid credentials.");
|
|
447
388
|
}
|
|
448
389
|
if (isFeatureEnabled("emailVerification") && !user.email_verified_at) {
|
|
449
390
|
logSecurityEvent("auth_login_blocked", { reason: "email_unverified", user_id: user.id });
|
|
450
|
-
throw new
|
|
391
|
+
throw new UnauthorizedError("Email address is not verified.");
|
|
451
392
|
}
|
|
452
393
|
if (isFeatureEnabled("mfa") && user.mfa_enabled) {
|
|
453
394
|
const mfaSecret = revealMfaSecret(user.mfa_secret);
|
|
454
395
|
if (!mfaSecret || !options.mfaCode || !verifyTotp(mfaSecret, options.mfaCode)) {
|
|
455
396
|
logSecurityEvent("auth_login_failed", { reason: "invalid_mfa", user_id: user.id });
|
|
456
|
-
throw new
|
|
397
|
+
throw new UnauthorizedError("Invalid MFA code.");
|
|
457
398
|
}
|
|
458
399
|
}
|
|
459
400
|
logSecurityEvent("auth_login_success", { user_id: user.id, method: "password" });
|
|
@@ -466,7 +407,7 @@ class AuthService {
|
|
|
466
407
|
async loginWithOAuth(providerName, code) {
|
|
467
408
|
const provider = this.oauthProviders.get(providerName);
|
|
468
409
|
if (!provider) {
|
|
469
|
-
throw new
|
|
410
|
+
throw new UnauthorizedError("Unsupported OAuth provider.");
|
|
470
411
|
}
|
|
471
412
|
const profile = await provider.exchangeCode(code);
|
|
472
413
|
const user = await this.findOrCreateOAuthUser(providerName, profile);
|
|
@@ -480,7 +421,7 @@ class AuthService {
|
|
|
480
421
|
buildOAuthAuthorizationUrl(providerName, state) {
|
|
481
422
|
const provider = this.oauthProviders.get(providerName);
|
|
482
423
|
if (!provider) {
|
|
483
|
-
throw new
|
|
424
|
+
throw new UnauthorizedError("Unsupported OAuth provider.");
|
|
484
425
|
}
|
|
485
426
|
return provider.getAuthorizationUrl(state);
|
|
486
427
|
}
|
|
@@ -523,7 +464,7 @@ var notificationTable = defineTable2({
|
|
|
523
464
|
});
|
|
524
465
|
|
|
525
466
|
// ../../src/modules/user/notificationService.ts
|
|
526
|
-
import { NotFoundError
|
|
467
|
+
import { NotFoundError } from "@getstrata/core/errors/http";
|
|
527
468
|
|
|
528
469
|
// ../../src/modules/user/oauthIdentityRepository.ts
|
|
529
470
|
import { BaseRepository as BaseRepository3, defineTable as defineTable3 } from "@getstrata/core/database";
|
|
@@ -567,21 +508,12 @@ var userTable = defineTable4({
|
|
|
567
508
|
|
|
568
509
|
// ../../src/modules/user/tokenService.ts
|
|
569
510
|
import { hashApiToken } from "@getstrata/core/auth/tokenHash";
|
|
570
|
-
import { ForbiddenError
|
|
511
|
+
import { ForbiddenError, NotFoundError as NotFoundError2 } from "@getstrata/core/errors/http";
|
|
571
512
|
import { resolveDefaultTokenExpiryDays as resolveDefaultTokenExpiryDays2 } from "@getstrata/core/security/tokenExpiry";
|
|
572
513
|
|
|
573
514
|
// ../../src/modules/user/provider.ts
|
|
574
515
|
var tokenServiceToken = CORE_TOKEN_SERVICE_TOKEN;
|
|
575
516
|
|
|
576
|
-
// ../../src/core/auth/authContext.ts
|
|
577
|
-
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
578
|
-
function runWithAuthUser(user, callback) {
|
|
579
|
-
return authContext.run(user, callback);
|
|
580
|
-
}
|
|
581
|
-
function currentAuthUser() {
|
|
582
|
-
return authContext.getStore() ?? null;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
517
|
// ../../src/core/http/csrfToken.ts
|
|
586
518
|
import { timingSafeEqual } from "crypto";
|
|
587
519
|
|
|
@@ -805,7 +737,7 @@ function withFlashClear(response) {
|
|
|
805
737
|
// ../../src/core/view/webLayoutData.ts
|
|
806
738
|
async function resolveWebLayoutData(container, request) {
|
|
807
739
|
const csrfToken = request ? resolveCsrfTokenForRequest(request) : "";
|
|
808
|
-
const flash = request ?
|
|
740
|
+
const flash = request ? currentRequestMeta2().flash ?? pullFlash(request) : null;
|
|
809
741
|
const authUser = currentAuthUser();
|
|
810
742
|
if (!authUser) {
|
|
811
743
|
return { authUser: null, csrfToken, flash };
|
|
@@ -885,8 +817,8 @@ function webErrorResponse(error, request) {
|
|
|
885
817
|
if (!request || !isViewsEnabled() || requestPrefersJson(request)) {
|
|
886
818
|
return null;
|
|
887
819
|
}
|
|
888
|
-
const mappedError = error instanceof
|
|
889
|
-
if (mappedError instanceof
|
|
820
|
+
const mappedError = error instanceof HttpError2 ? error : mapDatabaseError(error);
|
|
821
|
+
if (mappedError instanceof UnauthorizedError2) {
|
|
890
822
|
const redirectTarget = encodeURIComponent(new URL(request.url).pathname);
|
|
891
823
|
return Response.redirect(`/login?redirect=${redirectTarget}`, 302);
|
|
892
824
|
}
|