@getstrata/bootstrap 0.2.68 → 0.4.3
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/CHANGELOG.md +39 -10
- package/README.md +10 -10
- package/dist/_.._/_.._/index.html +2 -2
- package/dist/bootstrap/createSpaRoutes.d.ts +9 -2
- package/dist/bootstrap/dogfoodApp.d.ts +1 -1
- package/dist/bootstrap/httpKernel.d.ts +4 -4
- package/dist/bootstrap/public-api.d.ts +1 -1
- package/dist/bootstrap/schemaTarget.d.ts +5 -0
- package/dist/bootstrap/server.d.ts +1 -1
- package/dist/bootstrap/web/index.d.ts +1 -1
- package/dist/bootstrap/web/routing.d.ts +2 -2
- package/dist/bootstrap/web/session.d.ts +1 -0
- package/dist/entries/buildModuleRoutes.js +2 -2
- package/dist/entries/buildWebModuleRoutes.js +2 -2
- package/dist/entries/context.js +41 -5
- package/dist/entries/createRoutes.js +57 -37
- package/dist/entries/createSpaRoutes.js +48 -24
- package/dist/entries/createWebRoutes.js +2 -2
- package/dist/entries/dependencies.js +41 -5
- package/dist/entries/httpKernel.js +2 -2
- package/dist/entries/providers.js +41 -5
- package/dist/entries/secretsGuard.js +13 -8
- package/dist/entries/web/routing.js +4 -3
- package/dist/entries/web/session.js +47 -26
- package/dist/index.js +105 -42
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @getstrata/bootstrap changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.3
|
|
4
|
+
|
|
5
|
+
- Cookie session `mapUser` / row mapping can pass `email_verified_at` through as `emailVerifiedAt` so HTML email verification works.
|
|
6
|
+
- Session insert binds `expires_at` as an ISO string so SQLite cookie login does not fail parameter binding.
|
|
7
|
+
|
|
8
|
+
## 0.4.2
|
|
9
|
+
|
|
10
|
+
- `HttpKernel.wrapApi` uses `withJsonErrorHandling`. Hybrid views do not turn API errors into HTML.
|
|
11
|
+
|
|
12
|
+
## 0.4.1
|
|
13
|
+
|
|
14
|
+
- `createSpaRoutes` reads `SPA_PREFIX`, registers `prefix`, `prefix/`, and `prefix/*`, and accepts `distDirectory` plus `wrap`. Hybrid still does not redirect `/`.
|
|
15
|
+
- `appEnvSchema` defaults `SPA_PREFIX` from core `DEFAULT_SPA_PREFIX`.
|
|
16
|
+
- Peer `@getstrata/core` `^0.7.1`.
|
|
17
|
+
|
|
18
|
+
## 0.4.0
|
|
19
|
+
|
|
20
|
+
- Peer `@getstrata/core` `^0.7.0`.
|
|
21
|
+
- Production `SESSION_SECRET` is required whenever views are on (`FRONTEND_MODE=server-htmx` or `hybrid`).
|
|
22
|
+
- Hybrid SPA routes stay under `/app/*`. They do not redirect `/`, so staff HTML keeps the home path.
|
|
23
|
+
- `appEnvSchema` validates `FRONTEND_MODE` with the core pattern, including `hybrid`.
|
|
24
|
+
- `HttpKernel.wrapApi` maps thrown HTTP errors to JSON the same way `wrapWeb` maps them to HTML.
|
|
25
|
+
|
|
26
|
+
## 0.3.0
|
|
27
|
+
|
|
28
|
+
- Peer `@getstrata/core` `^0.6.0`.
|
|
29
|
+
- Cookie session SQL uses the current SQL dialect for placeholders, `NOW()`, and `NULLS LAST`.
|
|
30
|
+
- Kernel comments describe guest, verified-email, and password-confirm gates without referring to another framework.
|
|
31
|
+
|
|
3
32
|
## 0.2.68
|
|
4
33
|
|
|
5
34
|
- `configureModulesDirectory()` clears the discovered-module cache when the directory changes, so OpenAPI/`createApp` can load HiroApp after core tests pointed at empty fixtures.
|
|
@@ -13,17 +42,17 @@
|
|
|
13
42
|
|
|
14
43
|
## 0.2.66
|
|
15
44
|
|
|
16
|
-
- `assertProductionSecrets()` is feature-gated for every app. API tokens no longer imply
|
|
17
|
-
- **Breaking:** `HttpKernel.wrapWebGuest()` defaults `home` to `/` instead of `/organizations`. Apps with an org home should pass that path (
|
|
45
|
+
- `assertProductionSecrets()` is feature-gated for every app. API tokens no longer imply the previous in-repo app's encryption/CORS/OAuth/public-read checklist. Published test token strings are still denied. the previous in-repo app's extra production profile lives in the app (`src/config/productionSecrets.ts`).
|
|
46
|
+
- **Breaking:** `HttpKernel.wrapWebGuest()` defaults `home` to `/` instead of `/organizations`. Apps with an org home should pass that path (the previous in-repo app already does).
|
|
18
47
|
- `createWebRoutes()` no longer seeds a `/` → `/organizations` redirect. The in-repo app owns `/` via its organization module.
|
|
19
48
|
|
|
20
49
|
## 0.2.65
|
|
21
50
|
|
|
22
|
-
- App listener discovery reads `src/listeners` from `process.cwd()` so a built `@getstrata/bootstrap` bundle still finds
|
|
51
|
+
- App listener discovery reads `src/listeners` from `process.cwd()` so a built `@getstrata/bootstrap` bundle still finds the previous in-repo app registrars (`import.meta.dir` after `build:bootstrap` is the package dist). Listener boot calls each registrar every time (registrars are idempotent per `eventBus`).
|
|
23
52
|
|
|
24
53
|
## 0.2.64
|
|
25
54
|
|
|
26
|
-
- `registerDefaultJobs()` no longer registers
|
|
55
|
+
- `registerDefaultJobs()` no longer registers the previous in-repo app `webhook.dispatch`. Apps that dispatch model webhooks should call `registerWebhookJobs()` (the previous in-repo app's webhook provider and `queue:work` do).
|
|
27
56
|
|
|
28
57
|
## 0.2.63
|
|
29
58
|
|
|
@@ -31,7 +60,7 @@
|
|
|
31
60
|
|
|
32
61
|
## 0.2.62
|
|
33
62
|
|
|
34
|
-
- `HttpKernel.wrapWebPasswordConfirm()` is
|
|
63
|
+
- `HttpKernel.wrapWebPasswordConfirm()` is the previous PHP framework `password.confirm` for HTML routes. Peer `@getstrata/core` `^0.5.75`.
|
|
35
64
|
|
|
36
65
|
## 0.2.61
|
|
37
66
|
|
|
@@ -43,7 +72,7 @@
|
|
|
43
72
|
|
|
44
73
|
## 0.2.59
|
|
45
74
|
|
|
46
|
-
- `HttpKernel.wrapWebGuest()` is
|
|
75
|
+
- `HttpKernel.wrapWebGuest()` is the previous PHP framework `guest` / `RedirectIfAuthenticated`. Signed-in HTML users redirect to `/organizations` (override the home path). Peer `@getstrata/core` `^0.5.72`.
|
|
47
76
|
|
|
48
77
|
## 0.2.58
|
|
49
78
|
|
|
@@ -59,7 +88,7 @@
|
|
|
59
88
|
|
|
60
89
|
## 0.2.55
|
|
61
90
|
|
|
62
|
-
- HttpKernel and route builders read `isViewsEnabled` / `isSpaEnabled` from `@getstrata/core/runtime/frontendMode` instead of
|
|
91
|
+
- HttpKernel and route builders read `isViewsEnabled` / `isSpaEnabled` from `@getstrata/core/runtime/frontendMode` instead of the previous in-repo app `src/config/frontend`.
|
|
63
92
|
|
|
64
93
|
## 0.2.54
|
|
65
94
|
|
|
@@ -83,7 +112,7 @@
|
|
|
83
112
|
|
|
84
113
|
## 0.2.50
|
|
85
114
|
|
|
86
|
-
- `createCookieSessionAuthManager` / `CookieSessionGuard` turn `CookieSessionStore` into an `AuthGuard` / `AuthManager` for `CORE_AUTH_TOKEN`. Supply a `mapUser` mapper;
|
|
87
|
-
- `checkDatabase()` / `createHealthRoutes()` ping the bound database client, not
|
|
88
|
-
- `assertProductionSecrets()` is feature-gated. A production HTMX app with `SESSION_SECRET` (32+), `AUTH_DEV_HEADERS=false`, and feature flags off does not need
|
|
115
|
+
- `createCookieSessionAuthManager` / `CookieSessionGuard` turn `CookieSessionStore` into an `AuthGuard` / `AuthManager` for `CORE_AUTH_TOKEN`. Supply a `mapUser` mapper; the previous in-repo app abilities are not baked in. Omit `sql` to read the client from `bindDatabaseConnection()` on every call.
|
|
116
|
+
- `checkDatabase()` / `createHealthRoutes()` ping the bound database client, not the previous in-repo app's private connection holder. `/health` stays `{ status: "ok" }` unless `{ pingOnHealth: true }`. Extra JSON fields are optional.
|
|
117
|
+
- `assertProductionSecrets()` is feature-gated. A production HTMX app with `SESSION_SECRET` (32+), `AUTH_DEV_HEADERS=false`, and feature flags off does not need the previous in-repo app API tokens. the previous in-repo app's current production env (rotated tokens, CORS, pepper, expiry) still passes.
|
|
89
118
|
- Re-exports `CORE_ABILITY_CHECKER_TOKEN` and `CORE_AUTH_USER_DIRECTORY_TOKEN`.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @getstrata/bootstrap
|
|
2
2
|
|
|
3
|
-
Application bootstrap for Strata
|
|
3
|
+
Application bootstrap for Strata apps: HttpKernel, service container, and web utilities.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Application bootstrap for Strata sibling apps: HttpKernel, service container, an
|
|
|
8
8
|
bun add @getstrata/bootstrap @getstrata/core
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Web utilities
|
|
11
|
+
## Web utilities
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
14
|
import {
|
|
@@ -20,22 +20,22 @@ import {
|
|
|
20
20
|
} from "@getstrata/bootstrap";
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
`CookieSessionStore`
|
|
23
|
+
`CookieSessionStore` can read `is_admin` from the user row for `wrapWebGlobalAdmin`. Pass `loadSessionUser` when your `users` table does not match the default `SELECT u.*`.
|
|
24
24
|
|
|
25
|
-
## HttpKernel
|
|
25
|
+
## HttpKernel
|
|
26
26
|
|
|
27
27
|
```typescript
|
|
28
28
|
import { createHttpKernel, createAppContext, coreProviders } from "@getstrata/bootstrap";
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`createAppContext()` does not call `assertProductionSecrets`. That helper is feature-gated
|
|
31
|
+
`createAppContext()` does not call `assertProductionSecrets`. That helper is feature-gated (tokens, CORS, OAuth, encryption only when those features are on). HTML apps can call it without API tokens when those flags are off. See [docs/BUILDING-APPS.md](../../docs/BUILDING-APPS.md) and [docs/PRODUCTION.md](../../docs/PRODUCTION.md).
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
HTML apps should bind `createCookieSessionAuthManager` from `@getstrata/bootstrap/web/session` instead of HMAC `SessionGuard`. Use `signIn` / `signOut` (or the redirect helpers). Pass `mapUser` to map roles in the app.
|
|
34
34
|
|
|
35
|
-
`wrapWeb` applies the web group (CSRF + flash) and `withErrorHandling`, so CSRF `ForbiddenError` becomes an HTML 403
|
|
35
|
+
`wrapWeb` applies the web group (CSRF + flash) and `withErrorHandling`, so CSRF `ForbiddenError` becomes an HTML 403 when views are on (`FRONTEND_MODE=server-htmx` or `hybrid`). `wrapWebGuest` sends signed-in users to `/` by default (pass a string or `(user) => path` to override). `wrapWebLogin` / `wrapWebRegister` include that web group plus throttle. Do not wrap them with `wrapWeb` again. The throttle callback should return an HTML form at 429.
|
|
36
36
|
|
|
37
|
-
`
|
|
37
|
+
`createSpaRoutes` / `mergeSpaRoutes` serve `SPA_PREFIX` (default `/app`) as `prefix`, `prefix/`, and `prefix/*`. Hybrid does not redirect `/`. Pass `distDirectory` and `wrap` when the app is not the process cwd. Apps should not copy a second static-file server.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
`registerDefaultJobs()` registers `cache.invalidate-tags` and `audit.export` only. Apps that dispatch model webhooks should call `registerWebhookJobs()` themselves.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
These subpaths assemble leftover fixture HTTP for framework tests and are not a generic starter API: `@getstrata/bootstrap/createRoutes`, `@getstrata/bootstrap/schedule`, `@getstrata/bootstrap/createWebRoutes`. New apps should use `buildWebModuleRoutes` / `buildModuleRoutes`. Generated examples use `createApp()` in `apps/hiroapp`.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>
|
|
6
|
+
<title>Strata</title>
|
|
7
7
|
<style>
|
|
8
8
|
:root {
|
|
9
9
|
color-scheme: dark;
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
<script type="module" crossorigin src="../../index-sfreg6q3.js"></script></head>
|
|
79
79
|
|
|
80
80
|
<body>
|
|
81
|
-
<h1>
|
|
81
|
+
<h1>Strata</h1>
|
|
82
82
|
<p class="hint">
|
|
83
83
|
Framework reference domain for organizations, projects, tasks, and
|
|
84
84
|
comments.
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import type { RouteHandler } from "@getstrata/core/http/middleware";
|
|
1
2
|
import type { AppDependencies, AppRouteMap } from "./contracts";
|
|
2
3
|
declare const SPA_DIST_DIRECTORY: string;
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
type SpaRouteOptions = {
|
|
5
|
+
prefix?: string;
|
|
6
|
+
distDirectory?: string;
|
|
7
|
+
wrap?: (handler: RouteHandler) => RouteHandler;
|
|
8
|
+
};
|
|
9
|
+
declare function createSpaRoutes(_dependencies: AppDependencies, options?: SpaRouteOptions): AppRouteMap;
|
|
10
|
+
declare function mergeSpaRoutes(dependencies: AppDependencies, routes: AppRouteMap, options?: SpaRouteOptions): AppRouteMap;
|
|
11
|
+
export type { SpaRouteOptions };
|
|
5
12
|
export { createSpaRoutes, mergeSpaRoutes, SPA_DIST_DIRECTORY };
|
|
@@ -12,19 +12,19 @@ declare class HttpKernel {
|
|
|
12
12
|
wrap(groups: MiddlewareGroupName | MiddlewareGroupName[], handler: RouteHandler): RouteHandler;
|
|
13
13
|
wrapApi(handler: RouteHandler): RouteHandler;
|
|
14
14
|
wrapWeb(handler: RouteHandler): RouteHandler;
|
|
15
|
-
/**
|
|
15
|
+
/** Signed-in HTML users redirect to `home` instead of seeing guest pages. */
|
|
16
16
|
wrapWebGuest(handler: RouteHandler, home?: WebGuestHome): RouteHandler;
|
|
17
17
|
wrapWebPublicRead(handler: RouteHandler): RouteHandler;
|
|
18
18
|
wrapWebAuthenticated(handler: RouteHandler): RouteHandler;
|
|
19
|
-
/** Signed-in HTML without
|
|
19
|
+
/** Signed-in HTML without the email-verified gate (logout, verification notice). */
|
|
20
20
|
wrapWebAuthenticatedAllowUnverified(handler: RouteHandler): RouteHandler;
|
|
21
21
|
wrapWebVerified(handler: RouteHandler): RouteHandler;
|
|
22
|
-
/**
|
|
22
|
+
/** Requires a fresh signed password-confirmation cookie. */
|
|
23
23
|
wrapWebPasswordConfirm(handler: RouteHandler): RouteHandler;
|
|
24
24
|
wrapWebAbility(ability: string, handler: RouteHandler): RouteHandler;
|
|
25
25
|
wrapWebGlobalAdmin(handler: RouteHandler): RouteHandler;
|
|
26
26
|
wrapAuthenticated(handler: RouteHandler): RouteHandler;
|
|
27
|
-
/**
|
|
27
|
+
/** JSON routes that require a verified email. No-op when `FEATURE_EMAIL_VERIFICATION` is off. */
|
|
28
28
|
wrapVerified(handler: RouteHandler): RouteHandler;
|
|
29
29
|
wrapPublicRead(handler: RouteHandler): RouteHandler;
|
|
30
30
|
wrapGlobalAdmin(handler: RouteHandler): RouteHandler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @getstrata/bootstrap
|
|
2
|
+
* @getstrata/bootstrap: application shell for Strata apps.
|
|
3
3
|
*/
|
|
4
4
|
export type { ScheduledTask } from "@getstrata/core/scheduler/schedule";
|
|
5
5
|
export { appSchedule, runDueScheduledTasks, Schedule } from "@getstrata/core/scheduler/schedule";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Web-focused bootstrap utilities for
|
|
2
|
+
* Web-focused bootstrap utilities for HTML and cookie-session apps.
|
|
3
3
|
*/
|
|
4
4
|
export { createCsrfProtection, type ParsedForm, parseFormBody } from "./forms.ts";
|
|
5
5
|
export { createRouteKernel, routeParams, toRouteRequest, wrapSecuredRouteModelByKey, wrapWebLogin, wrapWebRegister, } from "./routing.ts";
|
|
@@ -10,7 +10,7 @@ export declare function routeParams(request: Request): Record<string, string>;
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function toRouteRequest<TParams extends Record<string, string>>(request: Request): RouteRequest<TParams>;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Secured route-model binding for string keys (slugs, UUIDs).
|
|
14
14
|
* Public HTML show pages should look up the model in the controller instead.
|
|
15
15
|
* If this wrapper is still used on HTML, a missing model becomes a styled 404
|
|
16
16
|
* and GET ETags are skipped for HTML / composite objects unless `etag: true`.
|
|
@@ -24,5 +24,5 @@ export declare function wrapSecuredRouteModelByKey<TParams extends Record<string
|
|
|
24
24
|
export declare function wrapWebLogin(kernel: HttpKernel, handler: RouteHandler, onThrottled: (request: Request) => Response | Promise<Response>): RouteHandler;
|
|
25
25
|
/** Web group (CSRF + flash + HTML errors) plus register throttle. Do not wrap with wrapWeb again. */
|
|
26
26
|
export declare function wrapWebRegister(kernel: HttpKernel, handler: RouteHandler, onThrottled: (request: Request) => Response | Promise<Response>): RouteHandler;
|
|
27
|
-
/** Convenience alias
|
|
27
|
+
/** Convenience alias for `createHttpKernel`. */
|
|
28
28
|
export declare function createRouteKernel(dependencies: AppDependencies): HttpKernel;
|
|
@@ -28,7 +28,7 @@ import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/require
|
|
|
28
28
|
import { createRequirePasswordConfirmMiddleware } from "@getstrata/core/http/requirePasswordConfirmMiddleware";
|
|
29
29
|
import { createRequireVerifiedMiddleware } from "@getstrata/core/http/requireVerifiedMiddleware";
|
|
30
30
|
import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
|
|
31
|
-
import { withErrorHandling } from "@getstrata/core/http/response";
|
|
31
|
+
import { withErrorHandling, withJsonErrorHandling } from "@getstrata/core/http/response";
|
|
32
32
|
import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
|
|
33
33
|
import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
|
|
34
34
|
import { createValidateSignatureMiddleware } from "@getstrata/core/http/signedUrl";
|
|
@@ -176,7 +176,7 @@ class HttpKernel {
|
|
|
176
176
|
return withMiddleware(...middleware)(handler);
|
|
177
177
|
}
|
|
178
178
|
wrapApi(handler) {
|
|
179
|
-
return this.wrap(["api", "authenticated"], handler);
|
|
179
|
+
return withJsonErrorHandling(this.wrap(["api", "authenticated"], handler));
|
|
180
180
|
}
|
|
181
181
|
wrapWeb(handler) {
|
|
182
182
|
return withErrorHandling(this.wrap("web", handler));
|
|
@@ -31,7 +31,7 @@ import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/require
|
|
|
31
31
|
import { createRequirePasswordConfirmMiddleware } from "@getstrata/core/http/requirePasswordConfirmMiddleware";
|
|
32
32
|
import { createRequireVerifiedMiddleware } from "@getstrata/core/http/requireVerifiedMiddleware";
|
|
33
33
|
import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
|
|
34
|
-
import { withErrorHandling } from "@getstrata/core/http/response";
|
|
34
|
+
import { withErrorHandling, withJsonErrorHandling } from "@getstrata/core/http/response";
|
|
35
35
|
import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
|
|
36
36
|
import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
|
|
37
37
|
import { createValidateSignatureMiddleware } from "@getstrata/core/http/signedUrl";
|
|
@@ -179,7 +179,7 @@ class HttpKernel {
|
|
|
179
179
|
return withMiddleware(...middleware)(handler);
|
|
180
180
|
}
|
|
181
181
|
wrapApi(handler) {
|
|
182
|
-
return this.wrap(["api", "authenticated"], handler);
|
|
182
|
+
return withJsonErrorHandling(this.wrap(["api", "authenticated"], handler));
|
|
183
183
|
}
|
|
184
184
|
wrapWeb(handler) {
|
|
185
185
|
return withErrorHandling(this.wrap("web", handler));
|
package/dist/entries/context.js
CHANGED
|
@@ -96,17 +96,19 @@ function resetDiscoverModulesForTests() {
|
|
|
96
96
|
state.modulesReady = undefined;
|
|
97
97
|
}
|
|
98
98
|
// ../../src/bootstrap/providers/auth.ts
|
|
99
|
+
import { BasicAuthGuard } from "@getstrata/core/auth/basicAuthGuard";
|
|
99
100
|
import {
|
|
100
101
|
AuthManager,
|
|
101
102
|
CompositeGuard,
|
|
102
103
|
DatabaseTokenGuard,
|
|
103
104
|
GuestGuard
|
|
104
105
|
} from "@getstrata/core/auth/guard";
|
|
106
|
+
import { JwtGuard } from "@getstrata/core/auth/jwtGuard";
|
|
105
107
|
import { SessionGuard } from "@getstrata/core/auth/sessionGuard";
|
|
106
108
|
|
|
107
109
|
// ../../src/config/auth.ts
|
|
108
110
|
var authConfig = {
|
|
109
|
-
allowDevHeaders:
|
|
111
|
+
allowDevHeaders: process.env.AUTH_DEV_HEADERS === "true",
|
|
110
112
|
tokenDefaultAbilities: ["*"]
|
|
111
113
|
};
|
|
112
114
|
|
|
@@ -141,11 +143,22 @@ var authProvider = {
|
|
|
141
143
|
name: "core.auth",
|
|
142
144
|
register({ container, config }) {
|
|
143
145
|
config.set("auth.allowDevHeaders", authConfig.allowDevHeaders);
|
|
144
|
-
const
|
|
146
|
+
const apiGuard = new DatabaseTokenGuard(container);
|
|
147
|
+
const sessionGuard = new SessionGuard(container);
|
|
148
|
+
const jwtGuard = new JwtGuard;
|
|
149
|
+
const basicGuard = new BasicAuthGuard(container);
|
|
150
|
+
const guards = [apiGuard, jwtGuard, basicGuard, sessionGuard];
|
|
145
151
|
if (authConfig.allowDevHeaders) {
|
|
146
152
|
guards.push(new GuestGuard);
|
|
147
153
|
}
|
|
148
|
-
|
|
154
|
+
const auth = new AuthManager(new CompositeGuard(guards));
|
|
155
|
+
auth.registerGuard("api", apiGuard);
|
|
156
|
+
auth.registerGuard("access_token", apiGuard);
|
|
157
|
+
auth.registerGuard("jwt", jwtGuard);
|
|
158
|
+
auth.registerGuard("basic", basicGuard);
|
|
159
|
+
auth.registerGuard("web", sessionGuard);
|
|
160
|
+
auth.registerGuard("session", sessionGuard);
|
|
161
|
+
container.set(CORE_AUTH_TOKEN, auth);
|
|
149
162
|
}
|
|
150
163
|
};
|
|
151
164
|
var auth_default = authProvider;
|
|
@@ -206,8 +219,12 @@ var queueConfig = {
|
|
|
206
219
|
};
|
|
207
220
|
// ../../src/bootstrap/env.ts
|
|
208
221
|
import { defineEnvSchema } from "@getstrata/core/config/envSchema";
|
|
222
|
+
import { DEFAULT_SPA_PREFIX, FRONTEND_MODE_PATTERN } from "@getstrata/core/runtime/frontendMode";
|
|
209
223
|
var appEnvSchema = defineEnvSchema({
|
|
210
|
-
DATABASE_URL: {
|
|
224
|
+
DATABASE_URL: {
|
|
225
|
+
required: true,
|
|
226
|
+
pattern: /^(postgres(ql)?|mysql|sqlite):\/\//i
|
|
227
|
+
},
|
|
211
228
|
PORT: {
|
|
212
229
|
integer: true,
|
|
213
230
|
minimum: 1,
|
|
@@ -235,9 +252,21 @@ var appEnvSchema = defineEnvSchema({
|
|
|
235
252
|
pattern: /^(sync|async|redis)$/
|
|
236
253
|
},
|
|
237
254
|
AUTH_DEV_HEADERS: {
|
|
238
|
-
default: "
|
|
255
|
+
default: "false",
|
|
239
256
|
pattern: /^(true|false|0|1)$/
|
|
240
257
|
},
|
|
258
|
+
DB_CONNECTION: {
|
|
259
|
+
default: "",
|
|
260
|
+
pattern: /^(pgsql|postgres|postgresql|mysql|mariadb|sqlite)?$/i
|
|
261
|
+
},
|
|
262
|
+
AUTH_DEFAULT_GUARD: {
|
|
263
|
+
default: "web"
|
|
264
|
+
},
|
|
265
|
+
JWT_TTL_SECONDS: {
|
|
266
|
+
integer: true,
|
|
267
|
+
minimum: 60,
|
|
268
|
+
default: "3600"
|
|
269
|
+
},
|
|
241
270
|
APP_ENV: {
|
|
242
271
|
default: "local"
|
|
243
272
|
},
|
|
@@ -247,6 +276,13 @@ var appEnvSchema = defineEnvSchema({
|
|
|
247
276
|
APP_URL: {
|
|
248
277
|
default: "http://localhost:3000"
|
|
249
278
|
},
|
|
279
|
+
FRONTEND_MODE: {
|
|
280
|
+
default: "api",
|
|
281
|
+
pattern: FRONTEND_MODE_PATTERN
|
|
282
|
+
},
|
|
283
|
+
SPA_PREFIX: {
|
|
284
|
+
default: DEFAULT_SPA_PREFIX
|
|
285
|
+
},
|
|
250
286
|
API_PREFIX: {
|
|
251
287
|
default: "/api/v1"
|
|
252
288
|
},
|
|
@@ -3,11 +3,11 @@ var __jsonParse = (a) => JSON.parse(a);
|
|
|
3
3
|
|
|
4
4
|
// ../../src/bootstrap/createRoutes.ts
|
|
5
5
|
import { jsonResponse as jsonResponse3 } from "@getstrata/core/http/response";
|
|
6
|
-
import { isSpaEnabled as isSpaEnabled2, isViewsEnabled as
|
|
6
|
+
import { isSpaEnabled as isSpaEnabled2, isViewsEnabled as isViewsEnabled4 } from "@getstrata/core/runtime/frontendMode";
|
|
7
7
|
import { notFoundHtmlResponse as notFoundHtmlResponse2 } from "@getstrata/core/view";
|
|
8
8
|
|
|
9
9
|
// ../../index.html
|
|
10
|
-
var strata_default = __jsonParse("{\"index\":\"_.._/_.._/index.html\",\"files\":[{\"input\":\"../../index.html\",\"path\":\"./index-sfreg6q3.js\",\"loader\":\"js\",\"isEntry\":true,\"headers\":{\"etag\":\"Oc_YLpCmV6M\",\"content-type\":\"text/javascript;charset=utf-8\"}},{\"input\":\"../../index.html\",\"path\":\"_.._/_.._/index.html\",\"loader\":\"html\",\"isEntry\":true,\"headers\":{\"etag\":\"
|
|
10
|
+
var strata_default = __jsonParse("{\"index\":\"_.._/_.._/index.html\",\"files\":[{\"input\":\"../../index.html\",\"path\":\"./index-sfreg6q3.js\",\"loader\":\"js\",\"isEntry\":true,\"headers\":{\"etag\":\"Oc_YLpCmV6M\",\"content-type\":\"text/javascript;charset=utf-8\"}},{\"input\":\"../../index.html\",\"path\":\"_.._/_.._/index.html\",\"loader\":\"html\",\"isEntry\":true,\"headers\":{\"etag\":\"ojosdcZCk7g\",\"content-type\":\"text/html;charset=utf-8\"}}]}");
|
|
11
11
|
|
|
12
12
|
// ../../src/config/app.ts
|
|
13
13
|
var appConfig = {
|
|
@@ -45,7 +45,7 @@ import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/require
|
|
|
45
45
|
import { createRequirePasswordConfirmMiddleware } from "@getstrata/core/http/requirePasswordConfirmMiddleware";
|
|
46
46
|
import { createRequireVerifiedMiddleware } from "@getstrata/core/http/requireVerifiedMiddleware";
|
|
47
47
|
import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
|
|
48
|
-
import { withErrorHandling } from "@getstrata/core/http/response";
|
|
48
|
+
import { withErrorHandling, withJsonErrorHandling } from "@getstrata/core/http/response";
|
|
49
49
|
import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
|
|
50
50
|
import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
|
|
51
51
|
import { createValidateSignatureMiddleware } from "@getstrata/core/http/signedUrl";
|
|
@@ -193,7 +193,7 @@ class HttpKernel {
|
|
|
193
193
|
return withMiddleware(...middleware)(handler);
|
|
194
194
|
}
|
|
195
195
|
wrapApi(handler) {
|
|
196
|
-
return this.wrap(["api", "authenticated"], handler);
|
|
196
|
+
return withJsonErrorHandling(this.wrap(["api", "authenticated"], handler));
|
|
197
197
|
}
|
|
198
198
|
wrapWeb(handler) {
|
|
199
199
|
return withErrorHandling(this.wrap("web", handler));
|
|
@@ -497,44 +497,68 @@ function buildModuleRoutes(dependencies, options = {}) {
|
|
|
497
497
|
// ../../src/bootstrap/createSpaRoutes.ts
|
|
498
498
|
import { join as join2 } from "path";
|
|
499
499
|
import { jsonResponse } from "@getstrata/core/http/response";
|
|
500
|
-
import { isSpaEnabled } from "@getstrata/core/runtime/frontendMode";
|
|
500
|
+
import { isSpaEnabled, isViewsEnabled as isViewsEnabled2, readSpaPrefix } from "@getstrata/core/runtime/frontendMode";
|
|
501
501
|
var SPA_DIST_DIRECTORY = join2(process.cwd(), "frontend/dist");
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
502
|
+
function relativeSpaPath(pathname, prefix) {
|
|
503
|
+
if (pathname === prefix || pathname === `${prefix}/`) {
|
|
504
|
+
return "";
|
|
505
|
+
}
|
|
506
|
+
if (pathname.startsWith(`${prefix}/`)) {
|
|
507
|
+
return pathname.slice(prefix.length + 1);
|
|
508
|
+
}
|
|
509
|
+
return "";
|
|
510
|
+
}
|
|
511
|
+
function createSpaDocumentHandler(prefix, distDirectory) {
|
|
512
|
+
const indexFilePath = join2(distDirectory, "index.html");
|
|
513
|
+
return async (request) => {
|
|
514
|
+
const pathname = new URL(request.url).pathname;
|
|
515
|
+
if (pathname.startsWith("/api/")) {
|
|
516
|
+
return new Response("Not found", { status: 404 });
|
|
517
|
+
}
|
|
518
|
+
const relativePath = relativeSpaPath(pathname, prefix);
|
|
519
|
+
const assetFile = Bun.file(join2(distDirectory, relativePath));
|
|
520
|
+
if (relativePath.length > 0 && await assetFile.exists()) {
|
|
521
|
+
return new Response(assetFile);
|
|
522
|
+
}
|
|
523
|
+
const indexFile = Bun.file(indexFilePath);
|
|
524
|
+
if (await indexFile.exists()) {
|
|
525
|
+
return new Response(indexFile, {
|
|
526
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
return jsonResponse({
|
|
530
|
+
error: "SPA build not found. Run `bun run frontend:build` in your app."
|
|
531
|
+
}, { status: 503 });
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function createSpaRoutes(_dependencies, options = {}) {
|
|
535
|
+
const prefix = options.prefix ?? readSpaPrefix();
|
|
536
|
+
const distDirectory = options.distDirectory ?? SPA_DIST_DIRECTORY;
|
|
537
|
+
const wrap = options.wrap ?? ((handler2) => handler2);
|
|
538
|
+
const handler = wrap(createSpaDocumentHandler(prefix, distDirectory));
|
|
539
|
+
const routes = {
|
|
540
|
+
[prefix]: handler,
|
|
541
|
+
[`${prefix}/`]: handler,
|
|
542
|
+
[`${prefix}/*`]: handler
|
|
523
543
|
};
|
|
544
|
+
if (!isViewsEnabled2()) {
|
|
545
|
+
routes["/"] = async () => Response.redirect(`${prefix}/`, 302);
|
|
546
|
+
}
|
|
547
|
+
return routes;
|
|
524
548
|
}
|
|
525
|
-
function mergeSpaRoutes(dependencies, routes) {
|
|
549
|
+
function mergeSpaRoutes(dependencies, routes, options) {
|
|
526
550
|
if (!isSpaEnabled()) {
|
|
527
551
|
return routes;
|
|
528
552
|
}
|
|
529
553
|
return {
|
|
530
|
-
...createSpaRoutes(dependencies),
|
|
554
|
+
...createSpaRoutes(dependencies, options),
|
|
531
555
|
...routes
|
|
532
556
|
};
|
|
533
557
|
}
|
|
534
558
|
|
|
535
559
|
// ../../src/bootstrap/createWebRoutes.ts
|
|
536
560
|
import { join as join3 } from "path";
|
|
537
|
-
import { isViewsEnabled as
|
|
561
|
+
import { isViewsEnabled as isViewsEnabled3 } from "@getstrata/core/runtime/frontendMode";
|
|
538
562
|
import { notFoundHtmlResponse } from "@getstrata/core/view";
|
|
539
563
|
|
|
540
564
|
// ../../src/bootstrap/buildWebModuleRoutes.ts
|
|
@@ -584,7 +608,7 @@ function createWebRoutes(dependencies, options = {}) {
|
|
|
584
608
|
return wrappedRoutes;
|
|
585
609
|
}
|
|
586
610
|
function mergeWebRoutes(dependencies, routes, options = {}) {
|
|
587
|
-
if (!
|
|
611
|
+
if (!isViewsEnabled3()) {
|
|
588
612
|
return routes;
|
|
589
613
|
}
|
|
590
614
|
return {
|
|
@@ -594,12 +618,8 @@ function mergeWebRoutes(dependencies, routes, options = {}) {
|
|
|
594
618
|
}
|
|
595
619
|
|
|
596
620
|
// ../../src/bootstrap/dogfoodApp.ts
|
|
597
|
-
function readDogfoodApp() {
|
|
598
|
-
const value = (process.env.DOGFOOD_APP ?? "hiroapp").trim().toLowerCase();
|
|
599
|
-
return value === "workhub" ? "workhub" : "hiroapp";
|
|
600
|
-
}
|
|
601
621
|
function isHiroappDogfood() {
|
|
602
|
-
return
|
|
622
|
+
return true;
|
|
603
623
|
}
|
|
604
624
|
|
|
605
625
|
// ../../src/bootstrap/health.ts
|
|
@@ -757,7 +777,7 @@ function createRoutes(dependencies) {
|
|
|
757
777
|
const baseRoutes = {
|
|
758
778
|
...healthRoutes,
|
|
759
779
|
...metricsRoutes,
|
|
760
|
-
...
|
|
780
|
+
...isViewsEnabled4() || isSpaEnabled2() ? {} : { "/": strata_default },
|
|
761
781
|
...wrappedModuleRoutes,
|
|
762
782
|
[notFoundApiPath]: async () => {
|
|
763
783
|
registerRoute2("GET", notFoundApiPath, ["global", "api"]);
|
|
@@ -765,7 +785,7 @@ function createRoutes(dependencies) {
|
|
|
765
785
|
},
|
|
766
786
|
"/*": async () => {
|
|
767
787
|
registerRoute2("GET", "/*", []);
|
|
768
|
-
if (
|
|
788
|
+
if (isViewsEnabled4()) {
|
|
769
789
|
return notFoundHtmlResponse2();
|
|
770
790
|
}
|
|
771
791
|
if (isSpaEnabled2()) {
|