okengine 0.15.2 → 0.17.0
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/manifest.v1.schema.json +21 -2
- package/package.json +1 -1
- package/site/content/docs/elements/clock.mdx +7 -0
- package/site/content/docs/elements/flow.mdx +20 -12
- package/site/content/docs/elements/gate.mdx +108 -3
- package/site/content/docs/elements/signal.mdx +46 -17
- package/site/content/docs/elements/store.mdx +36 -32
- package/site/content/docs/elements/vault.mdx +26 -0
- package/site/content/docs/get-started/project-structure.mdx +4 -4
- package/site/content/docs/reference/cli.md +1 -1
- package/site/content/docs/reference/client.mdx +75 -17
- package/site/content/docs/reference/configuration.mdx +7 -4
- package/site/content/docs/reference/errors.mdx +24 -17
- package/site/content/docs/reference/fx.mdx +39 -22
- package/site/content/docs/reference/security.md +1 -1
- package/src/auth/api-key-sql.test.ts +47 -0
- package/src/auth/api-key-sql.ts +261 -0
- package/src/auth/api-keys.ts +276 -14
- package/src/auth/auth.test.ts +56 -0
- package/src/auth/config.ts +24 -5
- package/src/auth/index.ts +62 -1
- package/src/auth/plugin.ts +6 -1
- package/src/auth/schema.ts +8 -0
- package/src/auth/sessions.ts +18 -0
- package/src/auth/tables.ts +32 -0
- package/src/auth/tenant-config.ts +74 -0
- package/src/auth/tenant-tables.ts +11 -0
- package/src/auth/tenants.test.ts +63 -0
- package/src/auth/tenants.ts +360 -0
- package/src/cli/attach-host-console.test.ts +37 -0
- package/src/cli/attach-host-console.ts +6 -0
- package/src/cli/build.ts +2 -2
- package/src/client/budget.test.ts +1 -1
- package/src/client/create.ts +75 -5
- package/src/client/index.ts +13 -0
- package/src/client/live.test.ts +422 -0
- package/src/client/live.ts +389 -0
- package/src/client/notes-contract.test.ts +41 -0
- package/src/client/types.ts +85 -6
- package/src/client-react/index.ts +85 -1
- package/src/client-react/use-live.test.ts +129 -0
- package/src/compiler/effects-infer.ts +37 -2
- package/src/compiler/extract.test.ts +143 -11
- package/src/compiler/extract.ts +210 -30
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +1 -2
- package/src/compiler/fixtures/skyport.expected.json +2 -3
- package/src/compiler/response.ts +41 -11
- package/src/console/server/access.test.ts +116 -8
- package/src/console/server/access.ts +70 -23
- package/src/console/server/app.ts +45 -11
- package/src/console/server/console.test.ts +6 -8
- package/src/console/server/dev-identities.test.ts +127 -0
- package/src/console/server/dev-identities.ts +229 -0
- package/src/console/server/flows-invoke.test.ts +21 -4
- package/src/console/server/flows.ts +113 -58
- package/src/console/server/gates.test.ts +24 -0
- package/src/console/server/gates.ts +10 -30
- package/src/console/server/index.ts +1 -0
- package/src/console/server/live.test.ts +26 -1
- package/src/console/server/live.ts +4 -2
- package/src/console/server/operator-db.ts +2 -0
- package/src/console/server/state.ts +29 -55
- package/src/console/server/store.test.ts +17 -0
- package/src/console/server/store.ts +43 -1
- package/src/console/ui-next/dist/assets/access-page-CXVWWMmD.js +4 -0
- package/src/console/ui-next/dist/assets/{agent-disclosure-C0X1fbWF.js → agent-disclosure-BP0Y0Sux.js} +1 -1
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-XDpAJO8f.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CS_O8L-r.js → highlighted-json-Awq7gYdu.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
- package/src/console/ui-next/dist/assets/index-BBj2QJCu.js +66 -0
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
- package/src/console/ui-next/dist/assets/observability-page-BFaay44m.js +4 -0
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
- package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-CS5-aETQ.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-Ca2Z-E52.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-BLbZhzyF.js → use-vault-list-uk4WVboC.js} +1 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-BmOeAFwg.js +2 -0
- package/src/console/ui-next/dist/index.html +11 -9
- package/src/console/ui-next/index.html +3 -3
- package/src/console/ui-next/src/client.ts +167 -0
- package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
- package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
- package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
- package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
- package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
- package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
- package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
- package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
- package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
- package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
- package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
- package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
- package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
- package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
- package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
- package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
- package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
- package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
- package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
- package/src/console/ui-next/src/features/flows/fixture.ts +0 -1
- package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
- package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +5 -1
- package/src/console/ui-next/src/features/units/lib/unit-tree.test.ts +15 -4
- package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
- package/src/console/ui-next/src/lib/document-meta.ts +2 -1
- package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
- package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
- package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
- package/src/console/ui-next/src/lib/shortcut.ts +3 -0
- package/src/console/ui-next/src/router.tsx +10 -1
- package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
- package/src/console/ui-next/ui-next-seed-manifest-surface.ts +2 -9
- package/src/console/ui-next/ui-next-seed-manifest.ts +0 -1
- package/src/console/ui-next/ui-next-seed-store.ts +43 -15
- package/src/console/xss.gate.test.ts +1 -0
- package/src/drivers/index.ts +2 -0
- package/src/drivers/journal-postgres.ts +12 -3
- package/src/drivers/pg-rls.ts +26 -2
- package/src/drivers/pg-vault-rls.ts +68 -0
- package/src/drivers/signal-engine.ts +69 -15
- package/src/drivers/signal-live-iter.ts +65 -0
- package/src/drivers/signal-nats.ts +2 -1
- package/src/drivers/signal-postgres.ts +95 -12
- package/src/drivers/signal-redis.ts +2 -1
- package/src/drivers/signal-retention.ts +64 -0
- package/src/drivers/signal-types.ts +35 -5
- package/src/elements/clock/declare.ts +64 -2
- package/src/elements/clock/reconcile.ts +98 -25
- package/src/elements/clock/runtime.ts +13 -2
- package/src/elements/clock.test.ts +28 -0
- package/src/elements/clock.ts +9 -1
- package/src/elements/gate/declare.ts +2 -0
- package/src/elements/gate/permissions.ts +12 -0
- package/src/elements/gate.ts +6 -1
- package/src/elements/signal/declare.ts +44 -10
- package/src/elements/signal/delivery-modes.test.ts +90 -4
- package/src/elements/signal/order-lifecycle.test.ts +20 -4
- package/src/elements/signal/runtime.ts +42 -0
- package/src/elements/signal.test.ts +21 -8
- package/src/elements/signal.ts +1 -1
- package/src/elements/store/cache.ts +1 -1
- package/src/elements/store/declare.ts +7 -0
- package/src/elements/store/rls-identity.test.ts +29 -0
- package/src/elements/store/rls-identity.ts +3 -0
- package/src/elements/store/runtime.ts +14 -0
- package/src/elements/store/schema-decl.ts +162 -44
- package/src/elements/store/schema-tenant.ts +41 -0
- package/src/elements/store/sql-rls-isolation.test.ts +39 -0
- package/src/elements/store/sql-select.types.test.ts +71 -0
- package/src/elements/store/sql-session.ts +54 -32
- package/src/elements/store.ts +5 -0
- package/src/elements/vault/builtin-adapter.ts +15 -2
- package/src/elements/vault/declare.ts +8 -0
- package/src/elements/vault/runtime.ts +7 -0
- package/src/elements/vault/sql-rls-isolation.test.ts +145 -0
- package/src/elements/vault/storage.ts +9 -0
- package/src/elements/vault/test-helpers.ts +2 -1
- package/src/i18n/catalogs/ar.ts +19 -0
- package/src/i18n/catalogs/en.ts +19 -0
- package/src/index.ts +2 -0
- package/src/kernel/adopt-routes.ts +56 -8
- package/src/kernel/api-key-host-persist.test.ts +143 -0
- package/src/kernel/app-tenant.ts +122 -0
- package/src/kernel/app.ts +196 -57
- package/src/kernel/auth-resolve.ts +13 -5
- package/src/kernel/boot-bind/clock.ts +9 -3
- package/src/kernel/boot.test.ts +4 -4
- package/src/kernel/boot.ts +2 -0
- package/src/kernel/budget.test.ts +1 -1
- package/src/kernel/clock-durable.ts +8 -0
- package/src/kernel/clock-per-tenant-name.ts +5 -0
- package/src/kernel/clock-reconcile.ts +8 -0
- package/src/kernel/errors-live-resume.ts +15 -0
- package/src/kernel/errors-tenant.ts +29 -0
- package/src/kernel/errors.registry.test.ts +31 -3
- package/src/kernel/errors.ts +43 -3
- package/src/kernel/flow.ts +26 -5
- package/src/kernel/fx-auth-keys.test.ts +88 -0
- package/src/kernel/fx-auth-keys.ts +203 -0
- package/src/kernel/fx-auth-tenants.test.ts +87 -0
- package/src/kernel/fx-auth-tenants.ts +286 -0
- package/src/kernel/fx-live-stream.ts +149 -0
- package/src/kernel/fx-live.test.ts +157 -0
- package/src/kernel/fx-runtime.ts +15 -0
- package/src/kernel/fx-tenant-store.ts +213 -0
- package/src/kernel/fx.test.ts +108 -1
- package/src/kernel/fx.ts +206 -28
- package/src/kernel/hooks.ts +2 -2
- package/src/kernel/http-resource.ts +9 -18
- package/src/kernel/index.ts +2 -0
- package/src/kernel/journal.ts +12 -0
- package/src/kernel/live-http.test.ts +78 -0
- package/src/kernel/live-http.ts +114 -0
- package/src/kernel/live-resume.test.ts +125 -0
- package/src/kernel/on.ts +51 -0
- package/src/kernel/pipeline-api-key.test.ts +192 -7
- package/src/kernel/pipeline-tenant.ts +49 -0
- package/src/kernel/pipeline.test.ts +1 -1
- package/src/kernel/pipeline.ts +41 -4
- package/src/kernel/resource-mount.test.ts +10 -27
- package/src/kernel/tenant-resolve.test.ts +101 -0
- package/src/kernel/tenant-resolve.ts +124 -0
- package/src/kernel/tenant-roles.test.ts +87 -0
- package/src/kernel/triggers.ts +59 -21
- package/src/manifest/diff.test.ts +11 -2
- package/src/manifest/diff.ts +53 -11
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +0 -1
- package/src/manifest/types.ts +58 -6
- package/src/release/build-lib.ts +14 -1
- package/src/release/limits.ts +2 -2
- package/src/release/measure.ts +55 -1
- package/src/client/live-gap.test.ts +0 -35
- package/src/console/ui-next/dist/assets/cache-glyph-F1FI122b.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-DEDCl_j3.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +0 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-3ptDzXbz.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-BvYrygyO.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +0 -1
- package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +0 -66
- package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +0 -2
- package/src/console/ui-next/dist/assets/link-COeyggt-.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-HK9-BO8z.js +0 -4
- package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +0 -54
- package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +0 -2
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access page URL search — selected key + sheet action.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useNavigate, useSearch } from "@tanstack/react-router";
|
|
6
|
+
import { useCallback } from "react";
|
|
7
|
+
|
|
8
|
+
/** Sheet opened from the URL. */
|
|
9
|
+
export type AccessAction = "create" | "edit" | "refresh" | "revoke" | "rotate";
|
|
10
|
+
|
|
11
|
+
/** Search params for `/access`. */
|
|
12
|
+
export interface AccessSearch {
|
|
13
|
+
readonly q?: string;
|
|
14
|
+
readonly key?: string;
|
|
15
|
+
readonly action?: AccessAction;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validate Access search params from the router.
|
|
20
|
+
*
|
|
21
|
+
* @param search - Raw search object
|
|
22
|
+
*/
|
|
23
|
+
export function validateAccessSearch(search: Record<string, unknown>): AccessSearch {
|
|
24
|
+
const q = typeof search.q === "string" && search.q.length > 0 ? search.q : undefined;
|
|
25
|
+
const key = typeof search.key === "string" && search.key.length > 0 ? search.key : undefined;
|
|
26
|
+
const action =
|
|
27
|
+
search.action === "create" ||
|
|
28
|
+
search.action === "edit" ||
|
|
29
|
+
search.action === "refresh" ||
|
|
30
|
+
search.action === "revoke" ||
|
|
31
|
+
search.action === "rotate"
|
|
32
|
+
? search.action
|
|
33
|
+
: undefined;
|
|
34
|
+
return {
|
|
35
|
+
...(q !== undefined ? { q } : {}),
|
|
36
|
+
...(key !== undefined ? { key } : {}),
|
|
37
|
+
...(action !== undefined ? { action } : {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read + write the Access page selection from the URL.
|
|
43
|
+
*/
|
|
44
|
+
export function useAccessSelection(): {
|
|
45
|
+
readonly query: string;
|
|
46
|
+
readonly selectedKey: string | null;
|
|
47
|
+
readonly action: AccessAction | null;
|
|
48
|
+
readonly setQuery: (q: string) => void;
|
|
49
|
+
readonly setSelectedKey: (key: string | null) => void;
|
|
50
|
+
readonly setAction: (action: AccessAction | null) => void;
|
|
51
|
+
} {
|
|
52
|
+
const search = useSearch({ strict: false }) as AccessSearch;
|
|
53
|
+
const navigate = useNavigate();
|
|
54
|
+
|
|
55
|
+
const query = typeof search.q === "string" ? search.q : "";
|
|
56
|
+
const selectedKey = typeof search.key === "string" ? search.key : null;
|
|
57
|
+
const action = search.action ?? null;
|
|
58
|
+
|
|
59
|
+
const patch = useCallback(
|
|
60
|
+
(next: Partial<AccessSearch>) => {
|
|
61
|
+
void navigate({
|
|
62
|
+
to: ".",
|
|
63
|
+
search: (prev: Record<string, unknown>) => validateAccessSearch({ ...prev, ...next }),
|
|
64
|
+
replace: true,
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
[navigate],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
query,
|
|
72
|
+
selectedKey,
|
|
73
|
+
action,
|
|
74
|
+
setQuery: (q) => {
|
|
75
|
+
patch({ q: q.length > 0 ? q : undefined });
|
|
76
|
+
},
|
|
77
|
+
setSelectedKey: (key) => {
|
|
78
|
+
patch({ key: key ?? undefined, action: undefined });
|
|
79
|
+
},
|
|
80
|
+
setAction: (next) => {
|
|
81
|
+
patch({ action: next ?? undefined });
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -18,6 +18,7 @@ describe("sanitizeReturnTo", () => {
|
|
|
18
18
|
expect(sanitizeReturnTo("/vault?name=STRIPE_KEY&action=rotate")).toBe(
|
|
19
19
|
"/vault?name=STRIPE_KEY&action=rotate",
|
|
20
20
|
);
|
|
21
|
+
expect(sanitizeReturnTo("/access?key=key_1")).toBe("/access?key=key_1");
|
|
21
22
|
expect(sanitizeReturnTo("/observability?window=7d")).toBe("/observability?window=7d");
|
|
22
23
|
expect(sanitizeReturnTo("/monitoring?window=7d")).toBe("/observability?window=7d");
|
|
23
24
|
});
|
|
@@ -107,6 +108,10 @@ describe("afterAuthLocation", () => {
|
|
|
107
108
|
to: "/vault",
|
|
108
109
|
search: { name: "STRIPE_KEY", action: "rotate-master" },
|
|
109
110
|
});
|
|
111
|
+
expect(afterAuthLocation("/access?key=key_1")).toEqual({
|
|
112
|
+
to: "/access",
|
|
113
|
+
search: { key: "key_1" },
|
|
114
|
+
});
|
|
110
115
|
expect(afterAuthLocation("/observability?window=7d&run=r1")).toEqual({
|
|
111
116
|
to: "/observability",
|
|
112
117
|
search: { window: "7d", run: "r1" },
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Post-auth return path — keep the operator on the module they were in
|
|
3
3
|
* after a session expires, instead of always landing on `/overview`.
|
|
4
4
|
*
|
|
5
|
-
* Only `/overview`, `/flows`, `/store`, `/vault`, and `/observability` (plus their
|
|
5
|
+
* Only `/overview`, `/flows`, `/store`, `/vault`, `/access`, and `/observability` (plus their
|
|
6
6
|
* search) are legal. `/monitoring` is rewritten to `/observability`. Anything else
|
|
7
7
|
* is dropped so `?next=` cannot be an open redirect.
|
|
8
8
|
*/
|
|
@@ -15,12 +15,20 @@ import {
|
|
|
15
15
|
type ObservabilitySearch,
|
|
16
16
|
} from "../observability/state/observability-selection.ts";
|
|
17
17
|
import { validateVaultSearch, type VaultSearch } from "../vault/state/vault-selection.ts";
|
|
18
|
+
import { validateAccessSearch, type AccessSearch } from "../access/state/access-selection.ts";
|
|
18
19
|
|
|
19
20
|
/** Default shell module when no safe return path is present. */
|
|
20
21
|
export const DEFAULT_AFTER_AUTH = "/overview" as const;
|
|
21
22
|
|
|
22
23
|
/** Shell pathnames that may be restored after login. */
|
|
23
|
-
const SHELL_PATHS = new Set([
|
|
24
|
+
const SHELL_PATHS = new Set([
|
|
25
|
+
"/overview",
|
|
26
|
+
"/flows",
|
|
27
|
+
"/store",
|
|
28
|
+
"/vault",
|
|
29
|
+
"/access",
|
|
30
|
+
"/observability",
|
|
31
|
+
]);
|
|
24
32
|
|
|
25
33
|
/** Retired live module — rewritten to {@link CANONICAL_OBSERVABILITY}. */
|
|
26
34
|
const LEGACY_MONITORING = "/monitoring";
|
|
@@ -40,6 +48,7 @@ export type AfterAuthLocation =
|
|
|
40
48
|
| { readonly to: "/flows"; readonly search: UnitsSearch }
|
|
41
49
|
| { readonly to: "/store"; readonly search: StoreSearch }
|
|
42
50
|
| { readonly to: "/vault"; readonly search: VaultSearch }
|
|
51
|
+
| { readonly to: "/access"; readonly search: AccessSearch }
|
|
43
52
|
| { readonly to: "/observability"; readonly search: ObservabilitySearch };
|
|
44
53
|
|
|
45
54
|
/**
|
|
@@ -111,6 +120,9 @@ export function afterAuthLocation(value: unknown): AfterAuthLocation {
|
|
|
111
120
|
if (url.pathname === "/vault") {
|
|
112
121
|
return { to: "/vault", search: validateVaultSearch(raw) };
|
|
113
122
|
}
|
|
123
|
+
if (url.pathname === "/access") {
|
|
124
|
+
return { to: "/access", search: validateAccessSearch(raw) };
|
|
125
|
+
}
|
|
114
126
|
if (url.pathname === CANONICAL_OBSERVABILITY) {
|
|
115
127
|
return { to: "/observability", search: validateObservabilitySearch(raw) };
|
|
116
128
|
}
|
|
@@ -123,6 +135,7 @@ export type AfterAuthNavigate = {
|
|
|
123
135
|
(opts: { to: "/flows"; search: UnitsSearch }): unknown;
|
|
124
136
|
(opts: { to: "/store"; search: StoreSearch }): unknown;
|
|
125
137
|
(opts: { to: "/vault"; search: VaultSearch }): unknown;
|
|
138
|
+
(opts: { to: "/access"; search: AccessSearch }): unknown;
|
|
126
139
|
(opts: { to: "/observability"; search: ObservabilitySearch }): unknown;
|
|
127
140
|
};
|
|
128
141
|
|
|
@@ -144,6 +157,9 @@ export function goAfterAuth(navigate: AfterAuthNavigate, value: unknown): void {
|
|
|
144
157
|
case "/vault":
|
|
145
158
|
void navigate({ to: "/vault", search: dest.search });
|
|
146
159
|
return;
|
|
160
|
+
case "/access":
|
|
161
|
+
void navigate({ to: "/access", search: dest.search });
|
|
162
|
+
return;
|
|
147
163
|
case "/observability":
|
|
148
164
|
void navigate({ to: "/observability", search: dest.search });
|
|
149
165
|
return;
|
|
@@ -6,7 +6,7 @@ import { authLabelClassName } from "@/components/auth-card";
|
|
|
6
6
|
import { ConsoleChrome } from "@/components/console-chrome";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* 404 for any path that is not `/`, `/overview`, `/flows`, `/store`, `/vault`, or `/observability`.
|
|
9
|
+
* 404 for any path that is not `/`, `/overview`, `/flows`, `/store`, `/vault`, `/access`, or `/observability`.
|
|
10
10
|
*/
|
|
11
11
|
export function NotFoundPage() {
|
|
12
12
|
return (
|
|
@@ -160,7 +160,11 @@ export function FlowContractPanel({
|
|
|
160
160
|
<MetaPill
|
|
161
161
|
icon={InternetAntenna03Icon}
|
|
162
162
|
label="live"
|
|
163
|
-
title=
|
|
163
|
+
title={
|
|
164
|
+
typeof row.flow.live === "string"
|
|
165
|
+
? `Live — SSE for ${row.flow.live}`
|
|
166
|
+
: "Live — SSE signal feed"
|
|
167
|
+
}
|
|
164
168
|
/>
|
|
165
169
|
) : null}
|
|
166
170
|
</FlowActivityStrip>
|
|
@@ -63,7 +63,11 @@ const TREE: readonly UnitGroup[] = [
|
|
|
63
63
|
}),
|
|
64
64
|
]),
|
|
65
65
|
group("orders", [
|
|
66
|
-
row("orders.onPlaced", { trigger: { signal: "order-placed" }
|
|
66
|
+
row("orders.onPlaced", { trigger: { signal: "order-placed" } }, "once"),
|
|
67
|
+
row("orders.events", {
|
|
68
|
+
trigger: { http: { method: "GET", path: "/orders/:orderId/events" } },
|
|
69
|
+
live: "order-status",
|
|
70
|
+
}),
|
|
67
71
|
row("orders.sync", { trigger: { cdc: { table: "orders" } }, plane: "operator" }),
|
|
68
72
|
row("orders.helper", {}),
|
|
69
73
|
]),
|
|
@@ -100,14 +104,19 @@ describe("filterUnitsAdvanced", () => {
|
|
|
100
104
|
const operators = filterUnitsAdvanced(TREE, { planes: ["operator"] });
|
|
101
105
|
expect(flowIds(operators)).toEqual(["billing.reconcile", "orders.sync"]);
|
|
102
106
|
const users = filterUnitsAdvanced(TREE, { planes: ["user"] });
|
|
103
|
-
expect(flowIds(users)).toEqual([
|
|
107
|
+
expect(flowIds(users)).toEqual([
|
|
108
|
+
"billing.charge",
|
|
109
|
+
"orders.onPlaced",
|
|
110
|
+
"orders.events",
|
|
111
|
+
"orders.helper",
|
|
112
|
+
]);
|
|
104
113
|
});
|
|
105
114
|
|
|
106
115
|
test("durable / live flags keep only flagged flows", () => {
|
|
107
116
|
expect(flowIds(filterUnitsAdvanced(TREE, { durableOnly: true }))).toEqual([
|
|
108
117
|
"billing.reconcile",
|
|
109
118
|
]);
|
|
110
|
-
expect(flowIds(filterUnitsAdvanced(TREE, { liveOnly: true }))).toEqual(["orders.
|
|
119
|
+
expect(flowIds(filterUnitsAdvanced(TREE, { liveOnly: true }))).toEqual(["orders.events"]);
|
|
111
120
|
});
|
|
112
121
|
|
|
113
122
|
test("facet dimensions AND together", () => {
|
|
@@ -218,8 +227,9 @@ describe("bandUnitTree", () => {
|
|
|
218
227
|
test("returns only populated trigger kind bands", () => {
|
|
219
228
|
const bands = bandUnitTree(TREE);
|
|
220
229
|
expect(bands.map((b) => b.id)).toEqual(["http", "signal", "cron", "cdc", "internal"]);
|
|
221
|
-
expect(bands[0]!.groups.map((g) => g.unit)).toEqual(["billing"]);
|
|
230
|
+
expect(bands[0]!.groups.map((g) => g.unit)).toEqual(["billing", "orders"]);
|
|
222
231
|
expect(bands[0]!.groups[0]!.flows.map((f) => f.id)).toEqual(["billing.charge"]);
|
|
232
|
+
expect(bands[0]!.groups[1]!.flows.map((f) => f.id)).toEqual(["orders.events"]);
|
|
223
233
|
expect(bands[1]!.groups.map((g) => g.unit)).toEqual(["orders"]);
|
|
224
234
|
expect(bands[1]!.groups[0]!.flows.map((f) => f.id)).toEqual(["orders.onPlaced"]);
|
|
225
235
|
expect(bands[2]!.groups.map((g) => g.unit)).toEqual(["billing"]);
|
|
@@ -254,6 +264,7 @@ describe("bandUnitTree", () => {
|
|
|
254
264
|
expect(unitTreeOpenKeys(bands)).toEqual([
|
|
255
265
|
"band:http",
|
|
256
266
|
"unit:http:billing",
|
|
267
|
+
"unit:http:orders",
|
|
257
268
|
"band:signal",
|
|
258
269
|
"unit:signal:orders",
|
|
259
270
|
"band:cron",
|
|
@@ -25,6 +25,7 @@ describe("consoleDocumentTitle", () => {
|
|
|
25
25
|
`Observability · ${CONSOLE_DOCUMENT_TITLE}`,
|
|
26
26
|
);
|
|
27
27
|
expect(consoleDocumentTitle("/vault")).toBe(`Vault · ${CONSOLE_DOCUMENT_TITLE}`);
|
|
28
|
+
expect(consoleDocumentTitle("/access")).toBe(`Access · ${CONSOLE_DOCUMENT_TITLE}`);
|
|
28
29
|
expect(consoleDocumentTitle("/monitoring")).toBe(`Observability · ${CONSOLE_DOCUMENT_TITLE}`);
|
|
29
30
|
});
|
|
30
31
|
|
|
@@ -7,7 +7,7 @@ export const CONSOLE_DOCUMENT_TITLE = "okengine Console";
|
|
|
7
7
|
|
|
8
8
|
/** Operator-facing `meta name="description"` / Open Graph copy. */
|
|
9
9
|
export const CONSOLE_DOCUMENT_DESCRIPTION =
|
|
10
|
-
"Operator Console for okengine — Flows, Store, Observability, and
|
|
10
|
+
"Operator Console for okengine — Flows, Store, Observability, Vault, and Access.";
|
|
11
11
|
|
|
12
12
|
const PAGE_TITLES: Record<string, string> = {
|
|
13
13
|
"/overview": "Overview",
|
|
@@ -15,6 +15,7 @@ const PAGE_TITLES: Record<string, string> = {
|
|
|
15
15
|
"/store": "Store",
|
|
16
16
|
"/observability": "Observability",
|
|
17
17
|
"/vault": "Vault",
|
|
18
|
+
"/access": "Access",
|
|
18
19
|
"/monitoring": "Observability",
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -12,6 +12,7 @@ describe("last-module-search", () => {
|
|
|
12
12
|
expect(isConsoleModulePath("/overview")).toBe(true);
|
|
13
13
|
expect(isConsoleModulePath("/flows")).toBe(true);
|
|
14
14
|
expect(isConsoleModulePath("/observability")).toBe(true);
|
|
15
|
+
expect(isConsoleModulePath("/access")).toBe(true);
|
|
15
16
|
expect(isConsoleModulePath("/monitoring")).toBe(false);
|
|
16
17
|
expect(isConsoleModulePath("/units")).toBe(false);
|
|
17
18
|
expect(isConsoleModulePath("/")).toBe(false);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Remember each Console module's last URL search so sidebar hops
|
|
3
|
-
* restore Overview / Flows / Store / Observability / Vault instead of wiping them.
|
|
3
|
+
* restore Overview / Flows / Store / Observability / Vault / Access instead of wiping them.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/** Authenticated Console modules that own URL search. */
|
|
@@ -10,6 +10,7 @@ export const CONSOLE_MODULE_PATHS = [
|
|
|
10
10
|
"/store",
|
|
11
11
|
"/observability",
|
|
12
12
|
"/vault",
|
|
13
|
+
"/access",
|
|
13
14
|
] as const;
|
|
14
15
|
|
|
15
16
|
/** Pathname for a Console module. */
|
|
@@ -21,6 +21,7 @@ describe("consoleShortcut", () => {
|
|
|
21
21
|
expect(consoleShortcut("store")).toEqual(modChord("3"));
|
|
22
22
|
expect(consoleShortcut("observability")).toEqual(modChord("4"));
|
|
23
23
|
expect(consoleShortcut("vault")).toEqual(modChord("5"));
|
|
24
|
+
expect(consoleShortcut("access")).toEqual(modChord("6"));
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
test("digits map to module paths", () => {
|
|
@@ -29,6 +30,7 @@ describe("consoleShortcut", () => {
|
|
|
29
30
|
expect(MODULE_DIGIT_PATH["3"]).toBe("/store");
|
|
30
31
|
expect(MODULE_DIGIT_PATH["4"]).toBe("/observability");
|
|
31
32
|
expect(MODULE_DIGIT_PATH["5"]).toBe("/vault");
|
|
33
|
+
expect(MODULE_DIGIT_PATH["6"]).toBe("/access");
|
|
32
34
|
});
|
|
33
35
|
});
|
|
34
36
|
|
|
@@ -63,6 +63,7 @@ export type ConsoleShortcutId =
|
|
|
63
63
|
| "flows"
|
|
64
64
|
| "store"
|
|
65
65
|
| "vault"
|
|
66
|
+
| "access"
|
|
66
67
|
| "observability";
|
|
67
68
|
|
|
68
69
|
const MODULE_DIGIT: Readonly<
|
|
@@ -73,6 +74,7 @@ const MODULE_DIGIT: Readonly<
|
|
|
73
74
|
store: "3",
|
|
74
75
|
observability: "4",
|
|
75
76
|
vault: "5",
|
|
77
|
+
access: "6",
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
/**
|
|
@@ -94,6 +96,7 @@ export const MODULE_DIGIT_PATH: Readonly<Record<string, ConsoleModulePath>> = {
|
|
|
94
96
|
"3": "/store",
|
|
95
97
|
"4": "/observability",
|
|
96
98
|
"5": "/vault",
|
|
99
|
+
"6": "/access",
|
|
97
100
|
};
|
|
98
101
|
|
|
99
102
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Console router — code-based TanStack Router (Vite SPA).
|
|
3
|
-
* Pre-auth `/` and authenticated shell `/overview` | `/flows` | `/store` | `/vault` | `/observability`.
|
|
3
|
+
* Pre-auth `/` and authenticated shell `/overview` | `/flows` | `/store` | `/vault` | `/access` | `/observability`.
|
|
4
4
|
* `/monitoring` is a one-shot search-preserving redirect to `/observability`.
|
|
5
5
|
* Authenticated pages are `lazyRouteComponent` so Vite splits them out of the entry.
|
|
6
6
|
* Any other path is a 404 — no legacy rewrites.
|
|
@@ -30,6 +30,7 @@ import { validateStoreSearch } from "./features/store/state/store-selection.ts";
|
|
|
30
30
|
import { validateUnitsSearch } from "./features/units/state/units-selection.ts";
|
|
31
31
|
import { validateObservabilitySearch } from "./features/observability/state/observability-selection.ts";
|
|
32
32
|
import { validateVaultSearch } from "./features/vault/state/vault-selection.ts";
|
|
33
|
+
import { validateAccessSearch } from "./features/access/state/access-selection.ts";
|
|
33
34
|
import { DocumentTitle } from "./components/document-title.tsx";
|
|
34
35
|
import { ShellLayout } from "./components/shell/shell-layout.tsx";
|
|
35
36
|
|
|
@@ -120,6 +121,13 @@ const vaultRoute = createRoute({
|
|
|
120
121
|
component: lazyRouteComponent(() => import("./features/vault/vault-page.tsx"), "VaultPage"),
|
|
121
122
|
});
|
|
122
123
|
|
|
124
|
+
const accessRoute = createRoute({
|
|
125
|
+
getParentRoute: () => authenticatedRoute,
|
|
126
|
+
path: "/access",
|
|
127
|
+
validateSearch: validateAccessSearch,
|
|
128
|
+
component: lazyRouteComponent(() => import("./features/access/access-page.tsx"), "AccessPage"),
|
|
129
|
+
});
|
|
130
|
+
|
|
123
131
|
const observabilityRoute = createRoute({
|
|
124
132
|
getParentRoute: () => authenticatedRoute,
|
|
125
133
|
path: "/observability",
|
|
@@ -146,6 +154,7 @@ const routeTree = rootRoute.addChildren([
|
|
|
146
154
|
flowsRoute,
|
|
147
155
|
storeRoute,
|
|
148
156
|
vaultRoute,
|
|
157
|
+
accessRoute,
|
|
149
158
|
observabilityRoute,
|
|
150
159
|
monitoringRedirectRoute,
|
|
151
160
|
]),
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { AUTH_TABLES } from "../../auth/tables.ts";
|
|
8
8
|
import { resolveAuthSchema } from "../../auth/schema.ts";
|
|
9
9
|
import type { DeclaredColumn, Table } from "../../manifest/types.ts";
|
|
10
|
+
import { KEEL_LADDER_GRANTS, KEEL_SCOPES } from "../server/dev-identities.ts";
|
|
10
11
|
|
|
11
12
|
const PII_COLUMNS = new Set([
|
|
12
13
|
"email",
|
|
@@ -163,9 +164,63 @@ export const UI_NEXT_SEED_APP_SYSTEM_TABLES: Record<string, Table> = {
|
|
|
163
164
|
const NOW = 1_723_622_400_000;
|
|
164
165
|
const LATER = NOW + 30 * 24 * 60 * 60 * 1000;
|
|
165
166
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
{
|
|
167
|
+
/** Ten-rung owner → guest ladder (demo + member keep historic ids). */
|
|
168
|
+
const LADDER = [
|
|
169
|
+
{
|
|
170
|
+
id: "user_demo",
|
|
171
|
+
email: "demo@example.com",
|
|
172
|
+
name: "Demo User",
|
|
173
|
+
status: "active",
|
|
174
|
+
role: "owner",
|
|
175
|
+
},
|
|
176
|
+
{ id: "user_admin", email: "admin@example.com", name: "Admin", status: "active", role: "admin" },
|
|
177
|
+
{
|
|
178
|
+
id: "user_workspace",
|
|
179
|
+
email: "workspace@example.com",
|
|
180
|
+
name: "Workspace Admin",
|
|
181
|
+
status: "active",
|
|
182
|
+
role: "workspace_admin",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: "user_pm",
|
|
186
|
+
email: "pm@example.com",
|
|
187
|
+
name: "Project Manager",
|
|
188
|
+
status: "active",
|
|
189
|
+
role: "project_manager",
|
|
190
|
+
},
|
|
191
|
+
{ id: "user_lead", email: "lead@example.com", name: "Lead", status: "active", role: "lead" },
|
|
192
|
+
{
|
|
193
|
+
id: "user_dev",
|
|
194
|
+
email: "developer@example.com",
|
|
195
|
+
name: "Developer",
|
|
196
|
+
status: "active",
|
|
197
|
+
role: "developer",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: "user_writer",
|
|
201
|
+
email: "writer@example.com",
|
|
202
|
+
name: "Contributor",
|
|
203
|
+
status: "active",
|
|
204
|
+
role: "contributor",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: "user_member",
|
|
208
|
+
email: "member@example.com",
|
|
209
|
+
name: "Member",
|
|
210
|
+
status: "active",
|
|
211
|
+
role: "member",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "user_commenter",
|
|
215
|
+
email: "commenter@example.com",
|
|
216
|
+
name: "Commenter",
|
|
217
|
+
status: "active",
|
|
218
|
+
role: "commenter",
|
|
219
|
+
},
|
|
220
|
+
{ id: "user_guest", email: "guest@example.com", name: "Guest", status: "active", role: "guest" },
|
|
221
|
+
] as const;
|
|
222
|
+
|
|
223
|
+
const STORY = [
|
|
169
224
|
{ id: "idn_aria", email: "aria@keel.dev", name: "Aria Chen", status: "active" },
|
|
170
225
|
{ id: "idn_ben", email: "ben@keel.dev", name: "Ben Okonkwo", status: "active" },
|
|
171
226
|
{ id: "idn_cai", email: "cai@keel.dev", name: "Cai Moreno", status: "active" },
|
|
@@ -185,6 +240,8 @@ const PEOPLE = [
|
|
|
185
240
|
{ id: "idn_zio", email: "zio@keel.dev", name: "Zio Hart", status: "active" },
|
|
186
241
|
] as const;
|
|
187
242
|
|
|
243
|
+
const PEOPLE = [...LADDER, ...STORY];
|
|
244
|
+
|
|
188
245
|
/**
|
|
189
246
|
* Seeded rows for app-plane system tables (never secret values).
|
|
190
247
|
*/
|
|
@@ -209,49 +266,43 @@ export const UI_NEXT_SEED_APP_SYSTEM_ROWS: Readonly<Record<string, readonly obje
|
|
|
209
266
|
updated_at: NOW,
|
|
210
267
|
})),
|
|
211
268
|
[AUTH_TABLES.identityRoles]: [
|
|
212
|
-
...
|
|
269
|
+
...LADDER.filter((p) => p.role !== "guest").map((p) => ({
|
|
270
|
+
id: `ir_${p.id}_${p.role}`,
|
|
271
|
+
identity_id: p.id,
|
|
272
|
+
role_id: `role_${p.role}`,
|
|
273
|
+
})),
|
|
274
|
+
...STORY.map((p) => ({
|
|
213
275
|
id: `ir_${p.id}_member`,
|
|
214
276
|
identity_id: p.id,
|
|
215
277
|
role_id: "role_member",
|
|
216
278
|
})),
|
|
217
|
-
{ id: "
|
|
218
|
-
{ id: "ir_user_demo_staff", identity_id: "user_demo", role_id: "role_staff" },
|
|
219
|
-
],
|
|
220
|
-
[AUTH_TABLES.roles]: [
|
|
221
|
-
{ id: "role_member", name: "member", plane: "user", description: "Signed-in workspace member" },
|
|
222
|
-
{ id: "role_staff", name: "staff", plane: "user", description: "May accept triage" },
|
|
223
|
-
{
|
|
224
|
-
id: "role_issue_write",
|
|
225
|
-
name: "issue:write",
|
|
226
|
-
plane: "user",
|
|
227
|
-
description: "May create and update issues",
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
id: "role_team_admin",
|
|
231
|
-
name: "team:admin",
|
|
232
|
-
plane: "user",
|
|
233
|
-
description: "May create teams and close cycles",
|
|
234
|
-
},
|
|
235
|
-
],
|
|
236
|
-
[AUTH_TABLES.roleGrants]: [
|
|
237
|
-
{ id: "rg_1", role_id: "role_member", action: "member" },
|
|
238
|
-
{ id: "rg_2", role_id: "role_staff", action: "member" },
|
|
239
|
-
{ id: "rg_3", role_id: "role_staff", action: "triage:accept" },
|
|
240
|
-
{ id: "rg_4", role_id: "role_issue_write", action: "issue:write" },
|
|
241
|
-
{ id: "rg_5", role_id: "role_issue_write", action: "project:admin" },
|
|
242
|
-
{ id: "rg_6", role_id: "role_issue_write", action: "comment:write" },
|
|
243
|
-
{ id: "rg_7", role_id: "role_issue_write", action: "files:write" },
|
|
244
|
-
{ id: "rg_8", role_id: "role_team_admin", action: "team:admin" },
|
|
245
|
-
{ id: "rg_9", role_id: "role_team_admin", action: "member:admin" },
|
|
246
|
-
{ id: "rg_10", role_id: "role_team_admin", action: "webhook:admin" },
|
|
279
|
+
{ id: "ir_idn_aria_pm", identity_id: "idn_aria", role_id: "role_project_manager" },
|
|
247
280
|
],
|
|
281
|
+
[AUTH_TABLES.roles]: Object.keys(KEEL_LADDER_GRANTS).map((role) => ({
|
|
282
|
+
id: `role_${role}`,
|
|
283
|
+
name: role,
|
|
284
|
+
plane: "user",
|
|
285
|
+
description:
|
|
286
|
+
role === "owner"
|
|
287
|
+
? "Full workspace authorization"
|
|
288
|
+
: role === "guest"
|
|
289
|
+
? "Least authorization — no write scopes"
|
|
290
|
+
: `Keel ${role.replaceAll("_", " ")}`,
|
|
291
|
+
})),
|
|
292
|
+
[AUTH_TABLES.roleGrants]: Object.entries(KEEL_LADDER_GRANTS).flatMap(([role, actions]) =>
|
|
293
|
+
actions.map((action, i) => ({
|
|
294
|
+
id: `rg_${role}_${i + 1}`,
|
|
295
|
+
role_id: `role_${role}`,
|
|
296
|
+
action,
|
|
297
|
+
})),
|
|
298
|
+
),
|
|
248
299
|
[AUTH_TABLES.apiKeys]: [
|
|
249
300
|
{
|
|
250
301
|
id: "key_keel_ci",
|
|
251
302
|
plane: "user",
|
|
252
303
|
hash: "sha256$keel_ci",
|
|
253
304
|
name: "Keel CI",
|
|
254
|
-
scopes: "member,
|
|
305
|
+
scopes: "member,task:write",
|
|
255
306
|
expires_at: null,
|
|
256
307
|
created_at: NOW,
|
|
257
308
|
last_used_at: NOW - 3_600_000,
|
|
@@ -279,7 +330,7 @@ export const UI_NEXT_SEED_APP_SYSTEM_ROWS: Readonly<Record<string, readonly obje
|
|
|
279
330
|
created_at: NOW,
|
|
280
331
|
expires_at: LATER,
|
|
281
332
|
last_active_at: NOW,
|
|
282
|
-
scopes: "
|
|
333
|
+
scopes: KEEL_LADDER_GRANTS.project_manager.join(","),
|
|
283
334
|
audience: "oke-app",
|
|
284
335
|
},
|
|
285
336
|
{
|
|
@@ -291,7 +342,7 @@ export const UI_NEXT_SEED_APP_SYSTEM_ROWS: Readonly<Record<string, readonly obje
|
|
|
291
342
|
created_at: NOW,
|
|
292
343
|
expires_at: LATER,
|
|
293
344
|
last_active_at: NOW,
|
|
294
|
-
scopes: "
|
|
345
|
+
scopes: KEEL_SCOPES.join(","),
|
|
295
346
|
audience: "oke-app",
|
|
296
347
|
},
|
|
297
348
|
],
|
|
@@ -81,7 +81,7 @@ function httpFlow(spec: {
|
|
|
81
81
|
path: string;
|
|
82
82
|
gates?: readonly string[];
|
|
83
83
|
plane?: "user" | "operator";
|
|
84
|
-
live?:
|
|
84
|
+
live?: string;
|
|
85
85
|
durable?: boolean;
|
|
86
86
|
cache?: boolean | string;
|
|
87
87
|
in?: JsonSchema;
|
|
@@ -94,7 +94,7 @@ function httpFlow(spec: {
|
|
|
94
94
|
trigger: { http: { method: spec.method, path: spec.path } },
|
|
95
95
|
plane: spec.plane ?? "user",
|
|
96
96
|
...(spec.gates ? { gates: [...spec.gates] } : {}),
|
|
97
|
-
...(spec.live ? { live:
|
|
97
|
+
...(spec.live ? { live: spec.live } : {}),
|
|
98
98
|
...(spec.durable ? { durable: true } : {}),
|
|
99
99
|
...(spec.cache !== undefined ? { cache: spec.cache } : {}),
|
|
100
100
|
...(spec.in ? { in: spec.in } : {}),
|
|
@@ -118,7 +118,6 @@ function crud(spec: {
|
|
|
118
118
|
readGates?: readonly string[];
|
|
119
119
|
writeGates?: readonly string[];
|
|
120
120
|
skip?: readonly ("list" | "get" | "create" | "update" | "delete")[];
|
|
121
|
-
liveList?: boolean;
|
|
122
121
|
createIn?: JsonSchema;
|
|
123
122
|
updateIn?: JsonSchema;
|
|
124
123
|
createEmits?: readonly string[];
|
|
@@ -133,7 +132,6 @@ function crud(spec: {
|
|
|
133
132
|
method: "GET",
|
|
134
133
|
path: spec.collection,
|
|
135
134
|
gates: read,
|
|
136
|
-
live: spec.liveList,
|
|
137
135
|
in: KEEL_LIST_IN,
|
|
138
136
|
out: KEEL_LIST_OUT,
|
|
139
137
|
effects: { reads: [spec.table] },
|
|
@@ -365,7 +363,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
|
|
|
365
363
|
readGates: MEMBER,
|
|
366
364
|
writeGates: COMMENT_WRITE,
|
|
367
365
|
skip: ["create"],
|
|
368
|
-
liveList: true,
|
|
369
366
|
updateIn: COMMENT_IN,
|
|
370
367
|
sourceDir: "src/flows/comments",
|
|
371
368
|
}),
|
|
@@ -386,7 +383,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
|
|
|
386
383
|
readGates: MEMBER,
|
|
387
384
|
writeGates: PROJECT_ADMIN,
|
|
388
385
|
skip: ["create"],
|
|
389
|
-
liveList: true,
|
|
390
386
|
sourceDir: "src/flows/projects",
|
|
391
387
|
}),
|
|
392
388
|
"projects.archive": httpFlow({
|
|
@@ -491,7 +487,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
|
|
|
491
487
|
item: "/spaces/:id",
|
|
492
488
|
readGates: MEMBER,
|
|
493
489
|
writeGates: PROJECT_ADMIN,
|
|
494
|
-
liveList: true,
|
|
495
490
|
createIn: {
|
|
496
491
|
type: "object",
|
|
497
492
|
required: ["key", "name"],
|
|
@@ -610,7 +605,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
|
|
|
610
605
|
readGates: MEMBER,
|
|
611
606
|
writeGates: MEMBER,
|
|
612
607
|
skip: ["create"],
|
|
613
|
-
liveList: true,
|
|
614
608
|
sourceDir: "src/flows/inbox",
|
|
615
609
|
}),
|
|
616
610
|
|
|
@@ -618,7 +612,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
|
|
|
618
612
|
method: "GET",
|
|
619
613
|
path: "/me/tasks",
|
|
620
614
|
gates: MEMBER,
|
|
621
|
-
live: true,
|
|
622
615
|
in: KEEL_LIST_IN,
|
|
623
616
|
out: KEEL_LIST_OUT,
|
|
624
617
|
effects: { reads: ["sql:tasks", "sql:task_assignees"] },
|