@urateam/dashboard 0.1.2 → 0.1.5

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.
Files changed (115) hide show
  1. package/dist/__tests__/audit.test.d.ts +2 -0
  2. package/dist/__tests__/audit.test.d.ts.map +1 -0
  3. package/dist/__tests__/audit.test.js +219 -0
  4. package/dist/__tests__/audit.test.js.map +1 -0
  5. package/dist/__tests__/auth-routes.test.d.ts +2 -0
  6. package/dist/__tests__/auth-routes.test.d.ts.map +1 -0
  7. package/dist/__tests__/auth-routes.test.js +116 -0
  8. package/dist/__tests__/auth-routes.test.js.map +1 -0
  9. package/dist/__tests__/bootstrap-integration.test.d.ts +2 -0
  10. package/dist/__tests__/bootstrap-integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/bootstrap-integration.test.js +75 -0
  12. package/dist/__tests__/bootstrap-integration.test.js.map +1 -0
  13. package/dist/__tests__/cost-chart.test.d.ts +2 -0
  14. package/dist/__tests__/cost-chart.test.d.ts.map +1 -0
  15. package/dist/__tests__/cost-chart.test.js +51 -0
  16. package/dist/__tests__/cost-chart.test.js.map +1 -0
  17. package/dist/__tests__/cost.test.d.ts +2 -0
  18. package/dist/__tests__/cost.test.d.ts.map +1 -0
  19. package/dist/__tests__/cost.test.js +197 -0
  20. package/dist/__tests__/cost.test.js.map +1 -0
  21. package/dist/__tests__/helpers/license.d.ts +8 -0
  22. package/dist/__tests__/helpers/license.d.ts.map +1 -0
  23. package/dist/__tests__/helpers/license.js +71 -0
  24. package/dist/__tests__/helpers/license.js.map +1 -0
  25. package/dist/__tests__/rbac-middleware.test.d.ts +2 -0
  26. package/dist/__tests__/rbac-middleware.test.d.ts.map +1 -0
  27. package/dist/__tests__/rbac-middleware.test.js +59 -0
  28. package/dist/__tests__/rbac-middleware.test.js.map +1 -0
  29. package/dist/__tests__/retry-route.test.d.ts +2 -0
  30. package/dist/__tests__/retry-route.test.d.ts.map +1 -0
  31. package/dist/__tests__/retry-route.test.js +115 -0
  32. package/dist/__tests__/retry-route.test.js.map +1 -0
  33. package/dist/__tests__/sso-integration.test.d.ts +2 -0
  34. package/dist/__tests__/sso-integration.test.d.ts.map +1 -0
  35. package/dist/__tests__/sso-integration.test.js +91 -0
  36. package/dist/__tests__/sso-integration.test.js.map +1 -0
  37. package/dist/__tests__/sso-middleware.test.d.ts +2 -0
  38. package/dist/__tests__/sso-middleware.test.d.ts.map +1 -0
  39. package/dist/__tests__/sso-middleware.test.js +66 -0
  40. package/dist/__tests__/sso-middleware.test.js.map +1 -0
  41. package/dist/__tests__/users-routes.test.d.ts +2 -0
  42. package/dist/__tests__/users-routes.test.d.ts.map +1 -0
  43. package/dist/__tests__/users-routes.test.js +127 -0
  44. package/dist/__tests__/users-routes.test.js.map +1 -0
  45. package/dist/middleware/rbac.d.ts +4 -0
  46. package/dist/middleware/rbac.d.ts.map +1 -0
  47. package/dist/middleware/rbac.js +21 -0
  48. package/dist/middleware/rbac.js.map +1 -0
  49. package/dist/middleware/sso.d.ts +9 -0
  50. package/dist/middleware/sso.d.ts.map +1 -0
  51. package/dist/middleware/sso.js +47 -0
  52. package/dist/middleware/sso.js.map +1 -0
  53. package/dist/routes/audit.d.ts +4 -0
  54. package/dist/routes/audit.d.ts.map +1 -0
  55. package/dist/routes/audit.js +123 -0
  56. package/dist/routes/audit.js.map +1 -0
  57. package/dist/routes/auth.d.ts +10 -0
  58. package/dist/routes/auth.d.ts.map +1 -0
  59. package/dist/routes/auth.js +105 -0
  60. package/dist/routes/auth.js.map +1 -0
  61. package/dist/routes/config.d.ts.map +1 -1
  62. package/dist/routes/config.js +4 -2
  63. package/dist/routes/config.js.map +1 -1
  64. package/dist/routes/coordination.d.ts.map +1 -1
  65. package/dist/routes/coordination.js +5 -3
  66. package/dist/routes/coordination.js.map +1 -1
  67. package/dist/routes/cost.d.ts +10 -0
  68. package/dist/routes/cost.d.ts.map +1 -0
  69. package/dist/routes/cost.js +104 -0
  70. package/dist/routes/cost.js.map +1 -0
  71. package/dist/routes/errors.d.ts.map +1 -1
  72. package/dist/routes/errors.js +4 -2
  73. package/dist/routes/errors.js.map +1 -1
  74. package/dist/routes/runs.d.ts +9 -1
  75. package/dist/routes/runs.d.ts.map +1 -1
  76. package/dist/routes/runs.js +82 -9
  77. package/dist/routes/runs.js.map +1 -1
  78. package/dist/routes/tokens.d.ts.map +1 -1
  79. package/dist/routes/tokens.js +4 -2
  80. package/dist/routes/tokens.js.map +1 -1
  81. package/dist/routes/users.d.ts +8 -0
  82. package/dist/routes/users.d.ts.map +1 -0
  83. package/dist/routes/users.js +64 -0
  84. package/dist/routes/users.js.map +1 -0
  85. package/dist/server.d.ts +13 -1
  86. package/dist/server.d.ts.map +1 -1
  87. package/dist/server.js +65 -7
  88. package/dist/server.js.map +1 -1
  89. package/dist/static/style.css +648 -0
  90. package/dist/views/audit.d.ts +29 -0
  91. package/dist/views/audit.d.ts.map +1 -0
  92. package/dist/views/audit.js +144 -0
  93. package/dist/views/audit.js.map +1 -0
  94. package/dist/views/cost.d.ts +28 -0
  95. package/dist/views/cost.d.ts.map +1 -0
  96. package/dist/views/cost.js +173 -0
  97. package/dist/views/cost.js.map +1 -0
  98. package/dist/views/forbidden.d.ts +7 -0
  99. package/dist/views/forbidden.d.ts.map +1 -0
  100. package/dist/views/forbidden.js +18 -0
  101. package/dist/views/forbidden.js.map +1 -0
  102. package/dist/views/layout.d.ts +11 -1
  103. package/dist/views/layout.d.ts.map +1 -1
  104. package/dist/views/layout.js +12 -18
  105. package/dist/views/layout.js.map +1 -1
  106. package/dist/views/run-detail.d.ts +1 -1
  107. package/dist/views/run-detail.d.ts.map +1 -1
  108. package/dist/views/run-detail.js +6 -1
  109. package/dist/views/run-detail.js.map +1 -1
  110. package/dist/views/users.d.ts +16 -0
  111. package/dist/views/users.d.ts.map +1 -0
  112. package/dist/views/users.js +44 -0
  113. package/dist/views/users.js.map +1 -0
  114. package/package.json +7 -5
  115. package/LICENSE +0 -27
@@ -0,0 +1,66 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { Hono } from "hono";
3
+ import { createDb, upsertUser, createSession } from "@urateam/core";
4
+ import { createSsoMiddleware } from "../middleware/sso.js";
5
+ let db;
6
+ let userId;
7
+ const ssoConfig = {
8
+ enabled: true,
9
+ workosApiKey: "sk_test",
10
+ workosClientId: "client_test",
11
+ redirectUri: "https://x/auth/callback",
12
+ sessionDurationHours: 24,
13
+ cookieName: "urateam_session",
14
+ cookieSecure: false,
15
+ stateSigningSecret: "0123456789abcdef0123456789abcdef",
16
+ };
17
+ beforeEach(async () => {
18
+ db = await createDb({ connectionString: ":memory:" });
19
+ userId = await upsertUser(db, {
20
+ email: "a@b.com",
21
+ name: "A",
22
+ workosUserId: null,
23
+ });
24
+ });
25
+ function appWithSso() {
26
+ const app = new Hono();
27
+ app.use("*", createSsoMiddleware({ db, sso: ssoConfig }));
28
+ app.get("/runs", (c) => c.text(`hello ${c.get("user").email}`));
29
+ app.post("/webhooks/linear", (c) => c.text("ok"));
30
+ app.get("/auth/login", (c) => c.text("login page"));
31
+ return app;
32
+ }
33
+ describe("ssoMiddleware", () => {
34
+ it("redirects to /auth/login when no cookie present", async () => {
35
+ const res = await appWithSso().request("/runs");
36
+ expect(res.status).toBe(302);
37
+ expect(res.headers.get("location")).toContain("/auth/login");
38
+ expect(res.headers.get("location")).toContain("next=%2Fruns");
39
+ });
40
+ it("allows /auth/* paths through without a cookie", async () => {
41
+ const res = await appWithSso().request("/auth/login");
42
+ expect(res.status).toBe(200);
43
+ });
44
+ it("allows /webhooks/* paths through without a cookie", async () => {
45
+ const res = await appWithSso().request("/webhooks/linear", {
46
+ method: "POST",
47
+ });
48
+ expect(res.status).toBe(200);
49
+ });
50
+ it("returns 200 with c.get('user') populated when valid session cookie present", async () => {
51
+ const sid = await createSession(db, { userId, durationHours: 24 });
52
+ const res = await appWithSso().request("/runs", {
53
+ headers: { cookie: `urateam_session=${sid}` },
54
+ });
55
+ expect(res.status).toBe(200);
56
+ expect(await res.text()).toBe("hello a@b.com");
57
+ });
58
+ it("clears cookie and redirects when session id is unknown", async () => {
59
+ const res = await appWithSso().request("/runs", {
60
+ headers: { cookie: `urateam_session=unknown` },
61
+ });
62
+ expect(res.status).toBe(302);
63
+ expect(res.headers.get("set-cookie")).toContain("urateam_session=;");
64
+ });
65
+ });
66
+ //# sourceMappingURL=sso-middleware.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso-middleware.test.js","sourceRoot":"","sources":["../../src/__tests__/sso-middleware.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=users-routes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users-routes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/users-routes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,127 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { Hono } from "hono";
3
+ import { createDb } from "@urateam/core";
4
+ import { dashboardUsers, auditEvents, } from "@urateam/core/dist/db/schema.js";
5
+ import { createUsersRouter } from "../routes/users.js";
6
+ import { installTestProLicense, restoreLicense } from "./helpers/license.js";
7
+ let db;
8
+ beforeEach(async () => {
9
+ db = await createDb({ connectionString: ":memory:" });
10
+ await db.insert(dashboardUsers).values([
11
+ {
12
+ id: "u_admin",
13
+ email: "admin@b.com",
14
+ name: "Admin",
15
+ workosUserId: null,
16
+ role: "admin",
17
+ },
18
+ {
19
+ id: "u_op",
20
+ email: "op@b.com",
21
+ name: "Op",
22
+ workosUserId: null,
23
+ role: "operator",
24
+ },
25
+ {
26
+ id: "u_view",
27
+ email: "view@b.com",
28
+ name: "View",
29
+ workosUserId: null,
30
+ role: "viewer",
31
+ },
32
+ ]);
33
+ });
34
+ afterEach(async () => {
35
+ await restoreLicense();
36
+ });
37
+ function appWith(role, userId = "u_admin") {
38
+ const app = new Hono();
39
+ app.use("*", async (c, next) => {
40
+ if (role) {
41
+ c.set("user", {
42
+ id: userId,
43
+ email: `${role}@b.com`,
44
+ role,
45
+ });
46
+ }
47
+ await next();
48
+ });
49
+ app.route("/", createUsersRouter({ db, basePath: "" }));
50
+ return app;
51
+ }
52
+ describe("/users routes (licensed)", () => {
53
+ beforeEach(async () => {
54
+ await installTestProLicense("enterprise");
55
+ });
56
+ it("GET /users as admin → 200 with user list", async () => {
57
+ const res = await appWith("admin").request("/users");
58
+ expect(res.status).toBe(200);
59
+ const body = await res.text();
60
+ expect(body).toContain("admin@b.com");
61
+ expect(body).toContain("op@b.com");
62
+ expect(body).toContain("view@b.com");
63
+ });
64
+ it("GET /users as operator → 403", async () => {
65
+ const res = await appWith("operator", "u_op").request("/users");
66
+ expect(res.status).toBe(403);
67
+ });
68
+ it("GET /users as viewer → 403", async () => {
69
+ const res = await appWith("viewer", "u_view").request("/users");
70
+ expect(res.status).toBe(403);
71
+ });
72
+ it("POST /users/:id/role as admin → 302, role updated, audit event written", async () => {
73
+ const res = await appWith("admin").request("/users/u_view/role", {
74
+ method: "POST",
75
+ headers: {
76
+ "HX-Request": "true",
77
+ "content-type": "application/x-www-form-urlencoded",
78
+ },
79
+ body: "role=operator",
80
+ });
81
+ expect(res.status).toBe(302);
82
+ const rows = await db.select().from(dashboardUsers);
83
+ expect(rows.find((u) => u.id === "u_view").role).toBe("operator");
84
+ const events = await db.select().from(auditEvents);
85
+ expect(events.some((e) => e.eventType === "dashboard.manual_action")).toBe(true);
86
+ });
87
+ it("POST /users/:id/role as operator → 403", async () => {
88
+ const res = await appWith("operator", "u_op").request("/users/u_view/role", {
89
+ method: "POST",
90
+ headers: {
91
+ "HX-Request": "true",
92
+ "content-type": "application/x-www-form-urlencoded",
93
+ },
94
+ body: "role=admin",
95
+ });
96
+ expect(res.status).toBe(403);
97
+ });
98
+ it("POST with invalid role → 400", async () => {
99
+ const res = await appWith("admin").request("/users/u_view/role", {
100
+ method: "POST",
101
+ headers: {
102
+ "HX-Request": "true",
103
+ "content-type": "application/x-www-form-urlencoded",
104
+ },
105
+ body: "role=god",
106
+ });
107
+ expect(res.status).toBe(400);
108
+ });
109
+ it("POST to demote self → 400", async () => {
110
+ const res = await appWith("admin", "u_admin").request("/users/u_admin/role", {
111
+ method: "POST",
112
+ headers: {
113
+ "HX-Request": "true",
114
+ "content-type": "application/x-www-form-urlencoded",
115
+ },
116
+ body: "role=viewer",
117
+ });
118
+ expect(res.status).toBe(400);
119
+ });
120
+ });
121
+ describe("/users routes (unlicensed)", () => {
122
+ it("GET /users → 404", async () => {
123
+ const res = await appWith("admin").request("/users");
124
+ expect(res.status).toBe(404);
125
+ });
126
+ });
127
+ //# sourceMappingURL=users-routes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users-routes.test.js","sourceRoot":"","sources":["../../src/__tests__/users-routes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import { type PermissionKey } from "@urateam/core";
3
+ export declare function requirePermission(action: PermissionKey): MiddlewareHandler;
4
+ //# sourceMappingURL=rbac.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../../src/middleware/rbac.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AAGvB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,iBAAiB,CAsB1E"}
@@ -0,0 +1,21 @@
1
+ import { isFeatureLicensed, canAccess, } from "@urateam/core";
2
+ import { renderForbidden } from "../views/forbidden.js";
3
+ export function requirePermission(action) {
4
+ return async (c, next) => {
5
+ if (!isFeatureLicensed("rbac"))
6
+ return next();
7
+ const user = c.get("user");
8
+ if (!user)
9
+ return c.text("Unauthorized", 401);
10
+ if (!canAccess(user.role, action)) {
11
+ return c.html(renderForbidden({
12
+ email: user.email,
13
+ role: user.role,
14
+ action,
15
+ basePath: "",
16
+ }), 403);
17
+ }
18
+ return next();
19
+ };
20
+ }
21
+ //# sourceMappingURL=rbac.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbac.js","sourceRoot":"","sources":["../../src/middleware/rbac.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,SAAS,GAGV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAE9C,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAe,CAErB,CAAC;QACd,IAAI,CAAC,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QAE9C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,CAAC,IAAI,CACX,eAAe,CAAC;gBACd,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM;gBACN,QAAQ,EAAE,EAAE;aACb,CAAC,EACF,GAAG,CACJ,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import type { SsoConfig } from "@urateam/core";
3
+ interface SsoMiddlewareDeps {
4
+ db: any;
5
+ sso: SsoConfig;
6
+ }
7
+ export declare function createSsoMiddleware(deps: SsoMiddlewareDeps): MiddlewareHandler;
8
+ export {};
9
+ //# sourceMappingURL=sso.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.d.ts","sourceRoot":"","sources":["../../src/middleware/sso.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAO9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,UAAU,iBAAiB;IACzB,EAAE,EAAE,GAAG,CAAC;IACR,GAAG,EAAE,SAAS,CAAC;CAChB;AAMD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,iBAAiB,GACtB,iBAAiB,CA+CnB"}
@@ -0,0 +1,47 @@
1
+ import { getCookie, setCookie } from "hono/cookie";
2
+ import { getSession, getUserById, deleteSession, touchSessionLastSeen, } from "@urateam/core";
3
+ // CSRF note: the session cookie uses SameSite=Lax. This prevents
4
+ // cross-origin POST from third-party sites (CSRF), but allows top-level
5
+ // navigations (GET). The dashboard CSRF middleware separately rejects
6
+ // state-changing requests without the HX-Request header.
7
+ export function createSsoMiddleware(deps) {
8
+ return async (c, next) => {
9
+ const path = c.req.path;
10
+ if (path.startsWith("/auth/"))
11
+ return next();
12
+ if (path.startsWith("/webhooks/"))
13
+ return next();
14
+ const cookie = getCookie(c, deps.sso.cookieName);
15
+ if (!cookie) {
16
+ return c.redirect(`/auth/login?next=${encodeURIComponent(path)}`, 302);
17
+ }
18
+ const session = await getSession(deps.db, cookie);
19
+ if (!session) {
20
+ setCookie(c, deps.sso.cookieName, "", {
21
+ maxAge: 0,
22
+ path: "/",
23
+ httpOnly: true,
24
+ sameSite: "Lax",
25
+ secure: deps.sso.cookieSecure,
26
+ });
27
+ return c.redirect(`/auth/login?next=${encodeURIComponent(path)}`, 302);
28
+ }
29
+ const user = await getUserById(deps.db, session.userId);
30
+ if (!user) {
31
+ await deleteSession(deps.db, cookie);
32
+ setCookie(c, deps.sso.cookieName, "", {
33
+ maxAge: 0,
34
+ path: "/",
35
+ httpOnly: true,
36
+ sameSite: "Lax",
37
+ secure: deps.sso.cookieSecure,
38
+ });
39
+ return c.redirect(`/auth/login`, 302);
40
+ }
41
+ c.set("user", user);
42
+ c.set("session", session);
43
+ void touchSessionLastSeen(deps.db, cookie);
44
+ return next();
45
+ };
46
+ }
47
+ //# sourceMappingURL=sso.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.js","sourceRoot":"","sources":["../../src/middleware/sso.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EACL,UAAU,EACV,WAAW,EACX,aAAa,EACb,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAQvB,iEAAiE;AACjE,wEAAwE;AACxE,sEAAsE;AACtE,yDAAyD;AACzD,MAAM,UAAU,mBAAmB,CACjC,IAAuB;IAEvB,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;QACxB,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAC7C,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAEjD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,CAAC,QAAQ,CACf,oBAAoB,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAC9C,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAClD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE;gBACpC,MAAM,EAAE,CAAC;gBACT,IAAI,EAAE,GAAG;gBACT,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,KAAK;gBACf,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,YAAY;aAC9B,CAAC,CAAC;YACH,OAAO,CAAC,CAAC,QAAQ,CACf,oBAAoB,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAC9C,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YACrC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE;gBACpC,MAAM,EAAE,CAAC;gBACT,IAAI,EAAE,GAAG;gBACT,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,KAAK;gBACf,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,YAAY;aAC9B,CAAC,CAAC;YACH,OAAO,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;QAED,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpB,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC1B,KAAK,oBAAoB,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Hono } from "hono";
2
+ import type { Db } from "@urateam/core";
3
+ export declare function createAuditRouter(db: Db, basePath?: string): Hono;
4
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/routes/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,EAAE,EAAkB,MAAM,eAAe,CAAC;AA6DxD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,SAAK,GAAG,IAAI,CA+E7D"}
@@ -0,0 +1,123 @@
1
+ import { Hono } from "hono";
2
+ import { isFeatureLicensed, listAuditEvents, streamAuditCsv, findAuditEventById, } from "@urateam/core";
3
+ import { layout } from "../views/layout.js";
4
+ import { renderAuditPage, renderEventDetailRow } from "../views/audit.js";
5
+ import { requirePermission } from "../middleware/rbac.js";
6
+ function parseFilters(query) {
7
+ return {
8
+ from: query.from || undefined,
9
+ to: query.to || undefined,
10
+ scope: query.scope || undefined,
11
+ eventType: query.eventType || undefined,
12
+ actor: query.actor || undefined,
13
+ runId: query.runId || undefined,
14
+ issueId: query.issueId || undefined,
15
+ q: query.q || undefined,
16
+ cursor: query.cursor || undefined,
17
+ };
18
+ }
19
+ function buildReaderFilters(q) {
20
+ const f = {};
21
+ if (q.from) {
22
+ const d = new Date(q.from);
23
+ if (!isNaN(d.getTime()))
24
+ f.from = d;
25
+ }
26
+ if (q.to) {
27
+ const d = new Date(q.to);
28
+ if (!isNaN(d.getTime()))
29
+ f.to = d;
30
+ }
31
+ if (q.scope)
32
+ f.scope = q.scope;
33
+ if (q.eventType)
34
+ f.eventTypes = [q.eventType];
35
+ if (q.actor)
36
+ f.actor = q.actor;
37
+ if (q.runId)
38
+ f.runId = q.runId;
39
+ if (q.issueId)
40
+ f.issueId = q.issueId;
41
+ if (q.q)
42
+ f.q = q.q;
43
+ if (q.cursor)
44
+ f.cursor = q.cursor;
45
+ f.limit = 50;
46
+ return f;
47
+ }
48
+ export function createAuditRouter(db, basePath = "") {
49
+ const router = new Hono();
50
+ // Gate every audit route behind the license feature flag.
51
+ router.use("/audit", async (c, next) => {
52
+ if (!isFeatureLicensed("audit-log"))
53
+ return c.notFound();
54
+ await next();
55
+ });
56
+ router.use("/audit/*", async (c, next) => {
57
+ if (!isFeatureLicensed("audit-log"))
58
+ return c.notFound();
59
+ await next();
60
+ });
61
+ router.get("/audit", requirePermission("audit.view"), async (c) => {
62
+ const query = c.req.query();
63
+ const filters = parseFilters(query);
64
+ const readerFilters = buildReaderFilters(query);
65
+ const { events, nextCursor } = await listAuditEvents(db, readerFilters);
66
+ const content = renderAuditPage({ events, nextCursor, filters });
67
+ if (c.req.header("HX-Request"))
68
+ return c.html(content);
69
+ const user = c.get("user");
70
+ return c.html(layout("Audit Log", content, basePath, { userEmail: user?.email }));
71
+ });
72
+ // HTMX partial used by the "Load more" link to append rows.
73
+ router.get("/audit/page", requirePermission("audit.view"), async (c) => {
74
+ const query = c.req.query();
75
+ const filters = parseFilters(query);
76
+ const readerFilters = buildReaderFilters(query);
77
+ const { events, nextCursor } = await listAuditEvents(db, readerFilters);
78
+ return c.html(renderAuditPage({ events, nextCursor, filters, partial: true }));
79
+ });
80
+ // HTMX detail expansion for a single event (expands the full payload).
81
+ router.get("/audit/event/:id", requirePermission("audit.view"), async (c) => {
82
+ const id = c.req.param("id");
83
+ const event = await findAuditEventById(db, id);
84
+ // Return 200 for not-found: HTMX 2.x drops 4xx responses by default so a 404
85
+ // would make the error message invisible to the user.
86
+ if (!event)
87
+ return c.html('<tr class="audit-detail-row"><td></td><td colspan="8" style="color:#c33;padding:0.5rem 1rem;background:#fff4f4;">Event not found</td></tr>');
88
+ return c.html(renderEventDetailRow(event));
89
+ });
90
+ router.get("/audit/export.csv", requirePermission("audit.export"), async (c) => {
91
+ const query = c.req.query();
92
+ const readerFilters = buildReaderFilters(query);
93
+ // Export ignores the paginated limit; streamAuditCsv walks its own cursor.
94
+ delete readerFilters.limit;
95
+ delete readerFilters.cursor;
96
+ const iter = streamAuditCsv(db, readerFilters);
97
+ const encoder = new TextEncoder();
98
+ const stream = new ReadableStream({
99
+ async pull(controller) {
100
+ const { value, done } = await iter.next();
101
+ if (done) {
102
+ controller.close();
103
+ return;
104
+ }
105
+ controller.enqueue(encoder.encode(value + "\n"));
106
+ },
107
+ async cancel() {
108
+ if (typeof iter.return === "function")
109
+ await iter.return();
110
+ },
111
+ });
112
+ return new Response(stream, {
113
+ status: 200,
114
+ headers: {
115
+ "Content-Type": "text/csv; charset=utf-8",
116
+ "Content-Disposition": 'attachment; filename="audit-export.csv"',
117
+ "Cache-Control": "no-store",
118
+ },
119
+ });
120
+ });
121
+ return router;
122
+ }
123
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/routes/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Hono } from "hono";
2
+ import type { SsoConfig, WorkosClient } from "@urateam/core";
3
+ interface AuthRouterDeps {
4
+ db: any;
5
+ sso: SsoConfig;
6
+ workos: WorkosClient;
7
+ }
8
+ export declare function createAuthRouter(deps: AuthRouterDeps): Hono;
9
+ export {};
10
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAmB5B,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAK7D,UAAU,cAAc;IACtB,EAAE,EAAE,GAAG,CAAC;IACR,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAiI3D"}
@@ -0,0 +1,105 @@
1
+ import { Hono } from "hono";
2
+ import { setCookie, getCookie } from "hono/cookie";
3
+ import { randomUUID } from "node:crypto";
4
+ import { signState, verifyState, validateNextPath, upsertUser, applyBootstrapAdmins, isFeatureLicensed, createSession, deleteSession, getSession, getUserById, logAuditEvent, dashboardLoginEvent, dashboardLogoutEvent, dashboardLoginDeniedEvent, } from "@urateam/core";
5
+ import { createLogger } from "@urateam/core";
6
+ const log = createLogger({ component: "dashboard.auth" });
7
+ export function createAuthRouter(deps) {
8
+ const app = new Hono();
9
+ app.get("/auth/login", async (c) => {
10
+ const next = validateNextPath(c.req.query("next"));
11
+ const state = signState({ next, nonce: randomUUID() }, deps.sso.stateSigningSecret);
12
+ const url = await deps.workos.getAuthorizationUrl({
13
+ clientId: deps.sso.workosClientId,
14
+ redirectUri: deps.sso.redirectUri,
15
+ state,
16
+ });
17
+ return c.redirect(url, 302);
18
+ });
19
+ app.get("/auth/callback", async (c) => {
20
+ const code = c.req.query("code");
21
+ const stateRaw = c.req.query("state");
22
+ if (!code || !stateRaw)
23
+ return c.text("Missing code or state", 400);
24
+ const state = verifyState(stateRaw, deps.sso.stateSigningSecret);
25
+ if (!state)
26
+ return c.text("Invalid login state. Please try again.", 400);
27
+ let result;
28
+ try {
29
+ result = await deps.workos.authenticateWithCode({
30
+ clientId: deps.sso.workosClientId,
31
+ code,
32
+ });
33
+ }
34
+ catch (err) {
35
+ log.warn({ err }, "WorkOS authenticateWithCode failed");
36
+ return c.text("SSO provider error. Try again or contact your administrator.", 503);
37
+ }
38
+ const email = result.user.email.toLowerCase();
39
+ if (deps.sso.allowedDomain) {
40
+ const expected = "@" + deps.sso.allowedDomain.toLowerCase();
41
+ if (!email.endsWith(expected)) {
42
+ void logAuditEvent(deps.db, dashboardLoginDeniedEvent({ email, reason: "domain-mismatch" }));
43
+ return c.text(`Access denied. ${email} is not in the allowed domain. Contact your administrator.`, 403);
44
+ }
45
+ }
46
+ const fullName = [result.user.firstName, result.user.lastName]
47
+ .filter(Boolean)
48
+ .join(" ")
49
+ .trim() || null;
50
+ const userId = await upsertUser(deps.db, {
51
+ email,
52
+ name: fullName,
53
+ workosUserId: result.user.id,
54
+ });
55
+ if (isFeatureLicensed("rbac")) {
56
+ try {
57
+ await applyBootstrapAdmins(deps.db, email, userId, process.env.URATEAM_ADMIN_EMAILS);
58
+ }
59
+ catch (err) {
60
+ // bootstrap must never block login
61
+ log.warn({ err }, "bootstrap admin failed");
62
+ }
63
+ }
64
+ const sessionId = await createSession(deps.db, {
65
+ userId,
66
+ durationHours: deps.sso.sessionDurationHours,
67
+ });
68
+ setCookie(c, deps.sso.cookieName, sessionId, {
69
+ httpOnly: true,
70
+ sameSite: "Lax",
71
+ secure: deps.sso.cookieSecure,
72
+ path: "/",
73
+ maxAge: deps.sso.sessionDurationHours * 3600,
74
+ });
75
+ void logAuditEvent(deps.db, dashboardLoginEvent({
76
+ userId,
77
+ email,
78
+ workosUserId: result.user.id,
79
+ }));
80
+ return c.redirect(validateNextPath(state.next), 302);
81
+ });
82
+ app.post("/auth/logout", async (c) => {
83
+ const sid = getCookie(c, deps.sso.cookieName);
84
+ if (sid) {
85
+ const session = await getSession(deps.db, sid);
86
+ if (session) {
87
+ const user = await getUserById(deps.db, session.userId);
88
+ await deleteSession(deps.db, sid);
89
+ if (user) {
90
+ void logAuditEvent(deps.db, dashboardLogoutEvent({ userId: user.id, email: user.email }));
91
+ }
92
+ }
93
+ }
94
+ setCookie(c, deps.sso.cookieName, "", {
95
+ maxAge: 0,
96
+ path: "/",
97
+ httpOnly: true,
98
+ sameSite: "Lax",
99
+ secure: deps.sso.cookieSecure,
100
+ });
101
+ return c.redirect("/auth/login", 302);
102
+ });
103
+ return app;
104
+ }
105
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAIhE,wBAAgB,kBAAkB,CAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC/C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACvC,QAAQ,SAAK,GACZ,IAAI,CASN"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhE,wBAAgB,kBAAkB,CAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC/C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACvC,QAAQ,SAAK,GACZ,IAAI,CAUN"}
@@ -1,11 +1,13 @@
1
1
  import { Hono } from "hono";
2
2
  import { layout } from "../views/layout.js";
3
3
  import { configView } from "../views/config.js";
4
+ import { requirePermission } from "../middleware/rbac.js";
4
5
  export function createConfigRouter(pipelineConfigs, repoConfigs, basePath = "") {
5
6
  const router = new Hono();
6
- router.get("/config", (c) => {
7
+ router.get("/config", requirePermission("config.view"), (c) => {
7
8
  const content = configView(pipelineConfigs, repoConfigs);
8
- return c.html(layout("Configuration", content, basePath));
9
+ const user = c.get("user");
10
+ return c.html(layout("Configuration", content, basePath, { userEmail: user?.email }));
9
11
  });
10
12
  return router;
11
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,UAAU,kBAAkB,CAChC,eAA+C,EAC/C,WAAuC,EACvC,QAAQ,GAAG,EAAE;IAEb,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;IAE1B,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;QAC1B,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;QACzD,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,UAAU,kBAAkB,CAChC,eAA+C,EAC/C,WAAuC,EACvC,QAAQ,GAAG,EAAE;IAEb,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;IAE1B,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;QAC5D,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAe,CAAmC,CAAC;QACtE,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"coordination.d.ts","sourceRoot":"","sources":["../../src/routes/coordination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAKxC,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,SAAK,GAAG,IAAI,CAqBpE"}
1
+ {"version":3,"file":"coordination.d.ts","sourceRoot":"","sources":["../../src/routes/coordination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAMxC,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,SAAK,GAAG,IAAI,CAsBpE"}