@simple-auth-kit/cli 1.5.0 → 1.6.1

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 (176) hide show
  1. package/lib/copy.ts +1 -1
  2. package/package.json +1 -1
  3. package/registry/combos/express-drizzle/shared/src/{ability.ts → ability/ability.ts} +8 -2
  4. package/registry/combos/express-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
  5. package/registry/combos/express-drizzle/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
  6. package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/config}/auth.config.ts +1 -1
  7. package/registry/combos/express-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
  8. package/registry/combos/express-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  9. package/registry/combos/express-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
  10. package/registry/combos/{nestjs-drizzle/shared/src → express-drizzle/shared/src/helpers}/pagination.ts +17 -3
  11. package/registry/combos/express-drizzle/shared/src/middleware/auth-core-error.middleware.ts +6 -8
  12. package/registry/combos/express-drizzle/shared/src/middleware/auth.middleware.ts +6 -8
  13. package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/openapi}/openapi-fragment.ts +13 -4
  14. package/registry/combos/express-drizzle/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +251 -52
  15. package/registry/combos/express-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
  16. package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +2 -4
  17. package/registry/combos/express-drizzle/shared/src/repositories/session.repository.ts +3 -3
  18. package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
  19. package/registry/combos/express-drizzle/shared/src/route-tiers.ts +52 -11
  20. package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +2 -2
  21. package/registry/combos/express-drizzle/test/bootstrap.ts +4 -2
  22. package/registry/combos/express-drizzle/test/prove-cycle.ts +1095 -308
  23. package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +5 -5
  24. package/registry/combos/express-drizzle/variants/base/src/index.ts +8 -0
  25. package/registry/combos/express-drizzle/variants/base/src/middleware/authz.middleware.ts +2 -2
  26. package/registry/combos/express-drizzle/variants/base/src/openapi/openapi-admin.ts +1082 -0
  27. package/registry/combos/express-drizzle/variants/base/src/rbac.defaults.ts +60 -17
  28. package/registry/combos/express-drizzle/variants/base/src/repositories/audit-log.repository.ts +2 -2
  29. package/registry/combos/express-drizzle/variants/base/src/repositories/rbac.repository.ts +5 -5
  30. package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +18 -22
  31. package/registry/combos/express-drizzle/variants/base/src/seed.ts +76 -17
  32. package/registry/combos/express-drizzle/variants/base/src/services/auth.service.ts +7 -9
  33. package/registry/combos/express-drizzle/variants/base/test/variant-hooks.ts +167 -45
  34. package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +5 -5
  35. package/registry/combos/express-drizzle/variants/workspaces/src/index.ts +8 -0
  36. package/registry/combos/express-drizzle/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
  37. package/registry/combos/express-drizzle/variants/workspaces/src/openapi/openapi-admin.ts +1176 -0
  38. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +1 -1
  39. package/registry/combos/express-drizzle/variants/workspaces/src/rbac.defaults.ts +66 -19
  40. package/registry/combos/express-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
  41. package/registry/combos/express-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +5 -5
  42. package/registry/combos/express-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +9 -11
  43. package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +18 -22
  44. package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +1 -1
  45. package/registry/combos/express-drizzle/variants/workspaces/src/seed.ts +168 -35
  46. package/registry/combos/express-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
  47. package/registry/combos/express-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
  48. package/registry/combos/express-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
  49. package/registry/combos/express-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
  50. package/registry/combos/express-prisma/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
  51. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/config}/auth.config.ts +1 -1
  52. package/registry/combos/express-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  53. package/registry/combos/{nestjs-prisma/shared/src → express-prisma/shared/src/helpers}/id.helper.ts +6 -2
  54. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/helpers}/pagination.ts +17 -3
  55. package/registry/combos/express-prisma/shared/src/middleware/auth-core-error.middleware.ts +6 -8
  56. package/registry/combos/express-prisma/shared/src/middleware/auth.middleware.ts +6 -8
  57. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/openapi}/openapi-fragment.ts +13 -4
  58. package/registry/combos/express-prisma/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +313 -55
  59. package/registry/combos/express-prisma/shared/src/repositories/oauth.repository.ts +2 -2
  60. package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
  61. package/registry/combos/express-prisma/shared/src/repositories/session.repository.ts +2 -2
  62. package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
  63. package/registry/combos/express-prisma/shared/src/route-tiers.ts +52 -11
  64. package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +2 -2
  65. package/registry/combos/express-prisma/test/bootstrap.ts +7 -3
  66. package/registry/combos/express-prisma/test/prove-cycle.ts +1095 -308
  67. package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +5 -5
  68. package/registry/combos/express-prisma/variants/base/src/index.ts +8 -0
  69. package/registry/combos/express-prisma/variants/base/src/middleware/authz.middleware.ts +2 -2
  70. package/registry/combos/express-prisma/variants/base/src/openapi/openapi-admin.ts +1102 -0
  71. package/registry/combos/express-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
  72. package/registry/combos/express-prisma/variants/base/src/repositories/rbac.repository.ts +4 -4
  73. package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +18 -22
  74. package/registry/combos/express-prisma/variants/base/src/services/auth.service.ts +6 -8
  75. package/registry/combos/express-prisma/variants/base/test/variant-hooks.ts +176 -47
  76. package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +5 -5
  77. package/registry/combos/express-prisma/variants/workspaces/src/index.ts +8 -0
  78. package/registry/combos/express-prisma/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
  79. package/registry/combos/express-prisma/variants/workspaces/src/openapi/openapi-admin.ts +1218 -0
  80. package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +1 -1
  81. package/registry/combos/express-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
  82. package/registry/combos/express-prisma/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
  83. package/registry/combos/express-prisma/variants/workspaces/src/repositories/workspace.repository.ts +2 -2
  84. package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +18 -22
  85. package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +1 -1
  86. package/registry/combos/express-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
  87. package/registry/combos/express-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
  88. package/registry/combos/nestjs-drizzle/shared/src/{guards → ability}/ability.guard.ts +1 -1
  89. package/registry/combos/nestjs-drizzle/shared/src/{ability.ts → ability/ability.ts} +8 -2
  90. package/registry/combos/nestjs-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
  91. package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/cache}/rate-limit.store.ts +4 -1
  92. package/registry/combos/nestjs-drizzle/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
  93. package/registry/combos/nestjs-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
  94. package/registry/combos/nestjs-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  95. package/registry/combos/nestjs-drizzle/shared/src/controllers/auth.controller.ts +1 -1
  96. package/registry/combos/nestjs-drizzle/shared/src/guards/auth.guard.ts +1 -1
  97. package/registry/combos/nestjs-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
  98. package/registry/combos/{express-prisma/shared/src → nestjs-drizzle/shared/src/helpers}/pagination.ts +17 -3
  99. package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
  100. package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +8 -12
  101. package/registry/combos/nestjs-drizzle/shared/src/repositories/session.repository.ts +3 -3
  102. package/registry/combos/nestjs-drizzle/shared/src/repositories/two-factor.repository.ts +1 -1
  103. package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
  104. package/registry/combos/nestjs-drizzle/test/bootstrap.ts +1 -1
  105. package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1 -1
  106. package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +10 -6
  107. package/registry/combos/nestjs-drizzle/variants/base/src/controllers/admin.controller.ts +1 -1
  108. package/registry/combos/nestjs-drizzle/variants/base/src/guards/authz.guard.ts +2 -2
  109. package/registry/combos/nestjs-drizzle/variants/base/src/index.ts +9 -0
  110. package/registry/combos/nestjs-drizzle/variants/base/src/rbac.defaults.ts +58 -16
  111. package/registry/combos/nestjs-drizzle/variants/base/src/repositories/audit-log.repository.ts +3 -3
  112. package/registry/combos/nestjs-drizzle/variants/base/src/repositories/rbac.repository.ts +4 -4
  113. package/registry/combos/nestjs-drizzle/variants/base/src/seed.ts +76 -17
  114. package/registry/combos/nestjs-drizzle/variants/base/src/services/auth.service.ts +7 -9
  115. package/registry/combos/nestjs-drizzle/variants/base/test/variant-hooks.ts +167 -45
  116. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +10 -6
  117. package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/admin.controller.ts +1 -1
  118. package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
  119. package/registry/combos/nestjs-drizzle/variants/workspaces/src/guards/authz.guard.ts +2 -2
  120. package/registry/combos/nestjs-drizzle/variants/workspaces/src/index.ts +9 -0
  121. package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.defaults.ts +64 -18
  122. package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +3 -3
  123. package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
  124. package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +8 -10
  125. package/registry/combos/nestjs-drizzle/variants/workspaces/src/seed.ts +171 -36
  126. package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
  127. package/registry/combos/nestjs-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
  128. package/registry/combos/nestjs-prisma/shared/src/{guards → ability}/ability.guard.ts +1 -1
  129. package/registry/combos/nestjs-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
  130. package/registry/combos/nestjs-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
  131. package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/cache}/rate-limit.store.ts +4 -1
  132. package/registry/combos/nestjs-prisma/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
  133. package/registry/combos/nestjs-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  134. package/registry/combos/nestjs-prisma/shared/src/controllers/auth.controller.ts +1 -1
  135. package/registry/combos/nestjs-prisma/shared/src/guards/auth.guard.ts +1 -1
  136. package/registry/combos/{express-prisma/shared/src → nestjs-prisma/shared/src/helpers}/id.helper.ts +6 -2
  137. package/registry/combos/nestjs-prisma/shared/src/{pagination.ts → helpers/pagination.ts} +17 -3
  138. package/registry/combos/nestjs-prisma/shared/src/repositories/oauth.repository.ts +1 -1
  139. package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
  140. package/registry/combos/nestjs-prisma/shared/src/repositories/session.repository.ts +2 -2
  141. package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
  142. package/registry/combos/nestjs-prisma/test/bootstrap.ts +1 -1
  143. package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1 -1
  144. package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +9 -5
  145. package/registry/combos/nestjs-prisma/variants/base/src/controllers/admin.controller.ts +1 -1
  146. package/registry/combos/nestjs-prisma/variants/base/src/gateways/audit-log.gateway.ts +1 -1
  147. package/registry/combos/nestjs-prisma/variants/base/src/guards/authz.guard.ts +2 -2
  148. package/registry/combos/nestjs-prisma/variants/base/src/index.ts +9 -0
  149. package/registry/combos/nestjs-prisma/variants/base/src/{docs.ts → openapi/docs.ts} +23 -6
  150. package/registry/combos/nestjs-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
  151. package/registry/combos/nestjs-prisma/variants/base/src/repositories/country.repository.ts +2 -2
  152. package/registry/combos/nestjs-prisma/variants/base/src/repositories/customer.repository.ts +6 -5
  153. package/registry/combos/nestjs-prisma/variants/base/src/repositories/language.repository.ts +2 -2
  154. package/registry/combos/nestjs-prisma/variants/base/src/repositories/rbac.repository.ts +3 -3
  155. package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +6 -8
  156. package/registry/combos/nestjs-prisma/variants/base/test/variant-hooks.ts +176 -47
  157. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +9 -5
  158. package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/admin.controller.ts +1 -1
  159. package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
  160. package/registry/combos/nestjs-prisma/variants/workspaces/src/guards/authz.guard.ts +2 -2
  161. package/registry/combos/nestjs-prisma/variants/workspaces/src/index.ts +9 -0
  162. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
  163. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/country.repository.ts +2 -2
  164. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/customer.repository.ts +6 -5
  165. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +2 -2
  166. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/rbac.repository.ts +3 -3
  167. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/workspace.repository.ts +1 -1
  168. package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
  169. package/registry/combos/nestjs-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
  170. package/simple-auth-kit.ts +13 -9
  171. package/registry/combos/express-drizzle/variants/base/src/openapi-admin.ts +0 -565
  172. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-admin.ts +0 -596
  173. package/registry/combos/express-prisma/variants/base/src/openapi-admin.ts +0 -569
  174. package/registry/combos/express-prisma/variants/workspaces/src/openapi-admin.ts +0 -601
  175. /package/registry/combos/express-drizzle/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
  176. /package/registry/combos/express-prisma/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
@@ -22,7 +22,7 @@
22
22
  // Callers: `src/seed.ts` (`npm run seed`, the only bootstrap in this variant) and
23
23
  // `variants/base/test/variant-hooks.ts`.
24
24
  import { inArray } from "drizzle-orm";
25
- import type { Database } from "./db.js";
25
+ import type { Database } from "./config/db.js";
26
26
  import { permissionRole, permissions, roles } from "./schema.js";
27
27
 
28
28
  /** The display metadata a seeded permission carries. `group`/`order` exist so an admin console can render a stable matrix. */
@@ -44,40 +44,65 @@ export interface PermissionSeed {
44
44
  * evaluate.
45
45
  */
46
46
  export const PERMISSION_CATALOG = {
47
- "users:read": { displayName: "List users", description: "GET /auth/admin/users", group: "Users", order: 1 },
48
- "users:block": { displayName: "Block and unblock users", description: "POST /auth/admin/users/:userId/block, .../unblock", group: "Users", order: 2 },
47
+ "users:read": {
48
+ displayName: "List users",
49
+ description: "GET /auth/admin/users",
50
+ group: "Users",
51
+ order: 1,
52
+ },
53
+ "users:block": {
54
+ displayName: "Block and unblock users",
55
+ description: "POST /auth/admin/users/:userId/block, .../unblock",
56
+ group: "Users",
57
+ order: 2,
58
+ },
49
59
  "users:manage": {
50
60
  displayName: "Edit and delete users",
51
- description: "Edit a user's profile or delete their account — PATCH /auth/admin/users/:userId, DELETE /auth/admin/users/:userId",
61
+ description:
62
+ "Edit a user's profile or delete their account — PATCH /auth/admin/users/:userId, DELETE /auth/admin/users/:userId",
52
63
  group: "Users",
53
64
  order: 3,
54
65
  },
55
66
  "roles:manage": {
56
67
  displayName: "Define roles",
57
- description: "Create, edit, or delete roles and say what they carry — POST/PATCH/DELETE /auth/admin/roles(/:roleId), POST /auth/admin/roles/:roleId/permissions",
68
+ description:
69
+ "Create, edit, or delete roles and say what they carry — POST/PATCH/DELETE /auth/admin/roles(/:roleId), POST /auth/admin/roles/:roleId/permissions",
58
70
  group: "Roles",
59
71
  order: 1,
60
72
  },
61
73
  "roles:assign": {
62
74
  displayName: "Assign roles",
63
- description: "Assign and revoke a user's roles — POST /auth/admin/users/:userId/roles, .../roles/:roleSlug/revoke",
75
+ description:
76
+ "Assign and revoke a user's roles — POST /auth/admin/users/:userId/roles, .../roles/:roleSlug/revoke",
64
77
  group: "Roles",
65
78
  order: 2,
66
79
  },
67
- "permissions:read": { displayName: "Read the permission catalog", description: "GET /auth/admin/permissions", group: "Permissions", order: 1 },
80
+ "permissions:read": {
81
+ displayName: "Read the permission catalog",
82
+ description: "GET /auth/admin/permissions",
83
+ group: "Permissions",
84
+ order: 1,
85
+ },
68
86
  "permissions:define": {
69
87
  displayName: "Define permissions",
70
- description: "Create a permission, rename it, or deactivate it — POST /auth/admin/permissions. The authority that defines all the others.",
88
+ description:
89
+ "Create a permission, rename it, or deactivate it — POST /auth/admin/permissions. The authority that defines all the others.",
71
90
  group: "Permissions",
72
91
  order: 2,
73
92
  },
74
93
  "permissions:grant": {
75
94
  displayName: "Grant permissions directly",
76
- description: "Grant and revoke a permission straight to a user, bypassing roles — POST /auth/admin/users/:userId/permissions, .../permissions/:slug/revoke",
95
+ description:
96
+ "Grant and revoke a permission straight to a user, bypassing roles — POST /auth/admin/users/:userId/permissions, .../permissions/:slug/revoke",
77
97
  group: "Permissions",
78
98
  order: 3,
79
99
  },
80
- "audit-log:read": { displayName: "Read the audit log", description: "GET /auth/admin/audit-log", group: "Audit", order: 1 },
100
+ "audit-log:read": {
101
+ displayName: "Read the audit log",
102
+ description: "GET /auth/admin/audit-log",
103
+ group: "Audit",
104
+ order: 1,
105
+ },
81
106
  } as const satisfies Record<string, PermissionSeed>;
82
107
 
83
108
  /**
@@ -89,10 +114,14 @@ export const PERMISSION_CATALOG = {
89
114
  */
90
115
  export type PermissionSlug = keyof typeof PERMISSION_CATALOG;
91
116
 
92
- export const PERMISSION_SLUGS = Object.keys(PERMISSION_CATALOG) as PermissionSlug[];
117
+ export const PERMISSION_SLUGS = Object.keys(
118
+ PERMISSION_CATALOG,
119
+ ) as PermissionSlug[];
93
120
 
94
121
  /** Groups, in the order an admin console should render them. Derived from the catalog so it cannot drift from it. */
95
- export const PERMISSION_GROUP_ORDER: string[] = [...new Set(PERMISSION_SLUGS.map((slug) => PERMISSION_CATALOG[slug].group))];
122
+ export const PERMISSION_GROUP_ORDER: string[] = [
123
+ ...new Set(PERMISSION_SLUGS.map((slug) => PERMISSION_CATALOG[slug].group)),
124
+ ];
96
125
 
97
126
  /** The display metadata a seeded role carries, plus what it starts out granting. */
98
127
  export interface RoleSeed {
@@ -127,7 +156,8 @@ export const DEFAULT_ROLES: readonly RoleSeed[] = [
127
156
  {
128
157
  slug: "superadmin",
129
158
  displayName: "Super Admin",
130
- description: "Carries every permission in the catalog — same authority as admin, held by the seeded super_admin account (see SEED_SUPERADMIN_* in seed.ts).",
159
+ description:
160
+ "Carries every permission in the catalog — same authority as admin, held by the seeded super_admin account (see SEED_SUPERADMIN_* in seed.ts).",
131
161
  isDefault: false,
132
162
  order: 1,
133
163
  permissions: PERMISSION_SLUGS,
@@ -177,12 +207,17 @@ export async function provisionDefaultRoles(db: RbacWriter): Promise<void> {
177
207
  displayName: PERMISSION_CATALOG[slug].displayName,
178
208
  description: PERMISSION_CATALOG[slug].description,
179
209
  group: PERMISSION_CATALOG[slug].group,
180
- groupOrder: PERMISSION_GROUP_ORDER.indexOf(PERMISSION_CATALOG[slug].group),
210
+ groupOrder: PERMISSION_GROUP_ORDER.indexOf(
211
+ PERMISSION_CATALOG[slug].group,
212
+ ),
181
213
  order: PERMISSION_CATALOG[slug].order,
182
214
  })),
183
215
  )
184
216
  .onConflictDoNothing({ target: permissions.slug });
185
- const permissionRows = await db.select({ id: permissions.id, slug: permissions.slug }).from(permissions).where(inArray(permissions.slug, PERMISSION_SLUGS));
217
+ const permissionRows = await db
218
+ .select({ id: permissions.id, slug: permissions.slug })
219
+ .from(permissions)
220
+ .where(inArray(permissions.slug, PERMISSION_SLUGS));
186
221
  const permissionId = new Map(permissionRows.map((p) => [p.slug, p.id]));
187
222
 
188
223
  await db
@@ -210,9 +245,17 @@ export async function provisionDefaultRoles(db: RbacWriter): Promise<void> {
210
245
  const seedBySlug = new Map(DEFAULT_ROLES.map((role) => [role.slug, role]));
211
246
 
212
247
  const pairs = roleRows.flatMap((role) =>
213
- (seedBySlug.get(role.slug)?.permissions ?? []).map((slug) => ({ roleId: role.id, permissionId: permissionId.get(slug)! })),
248
+ (seedBySlug.get(role.slug)?.permissions ?? []).map((slug) => ({
249
+ roleId: role.id,
250
+ permissionId: permissionId.get(slug)!,
251
+ })),
214
252
  );
215
253
  if (pairs.length) {
216
- await db.insert(permissionRole).values(pairs).onConflictDoNothing({ target: [permissionRole.permissionId, permissionRole.roleId] });
254
+ await db
255
+ .insert(permissionRole)
256
+ .values(pairs)
257
+ .onConflictDoNothing({
258
+ target: [permissionRole.permissionId, permissionRole.roleId],
259
+ });
217
260
  }
218
261
  }
@@ -6,9 +6,9 @@ import {
6
6
  normalizeLimit,
7
7
  normalizePage,
8
8
  type Paginated,
9
- } from "../pagination.js";
9
+ } from "../helpers/pagination.js";
10
10
  import * as schema from "../schema.js";
11
- import { toIdOrNull, toIdOrUndefined } from "../id.helper.js";
11
+ import { toIdOrNull, toIdOrUndefined } from "../helpers/id.helper.js";
12
12
 
13
13
  export interface AuditLogEntry {
14
14
  id: string;
@@ -9,14 +9,14 @@ import {
9
9
  type SQL,
10
10
  } from "drizzle-orm";
11
11
  import { resolvePermissions } from "@/lib/auth/core/rbac.js";
12
- import type { Database } from "../db.js";
12
+ import type { Database } from "../config/db.js";
13
13
  import {
14
14
  buildPageMeta,
15
15
  normalizeLimit,
16
16
  normalizePage,
17
17
  type Paginated,
18
- } from "../pagination.js";
19
- import { PermissionCache } from "../permission-cache.js";
18
+ } from "../helpers/pagination.js";
19
+ import { PermissionCache } from "../cache/permission-cache.js";
20
20
  import {
21
21
  permissionRole,
22
22
  permissionUser,
@@ -25,8 +25,8 @@ import {
25
25
  roles,
26
26
  users,
27
27
  } from "../schema.js";
28
- import { HttpError } from "../http-error.js";
29
- import { toId, toIdOrNull } from "../id.helper.js";
28
+ import { HttpError } from "../errors/http-error.js";
29
+ import { toId, toIdOrNull } from "../helpers/id.helper.js";
30
30
 
31
31
  /**
32
32
  * Every column the `users` table has, except the two secrets (`passwordHash`, `twoFactorSecret`
@@ -1,6 +1,6 @@
1
1
  import type { NextFunction, Request, RequestHandler, Response } from "express";
2
2
  import { AuthService } from "../services/auth.service.js";
3
- import { HttpError } from "../http-error.js";
3
+ import { HttpError } from "../errors/http-error.js";
4
4
  import {
5
5
  ability,
6
6
  createTieredRouter,
@@ -54,15 +54,13 @@ export function createAdminRouter(deps: AdminRouterDeps): RequestHandler {
54
54
  string,
55
55
  string | undefined
56
56
  >;
57
- res
58
- .status(200)
59
- .json(
60
- await auth.listUsers({
61
- search,
62
- page: page ? Number(page) : undefined,
63
- limit: limit ? Number(limit) : undefined,
64
- }),
65
- );
57
+ res.status(200).json(
58
+ await auth.listUsers({
59
+ search,
60
+ page: page ? Number(page) : undefined,
61
+ limit: limit ? Number(limit) : undefined,
62
+ }),
63
+ );
66
64
  } catch (err) {
67
65
  next(err);
68
66
  }
@@ -179,18 +177,16 @@ export function createAdminRouter(deps: AdminRouterDeps): RequestHandler {
179
177
  try {
180
178
  const { userId, action, since, until, page, limit } =
181
179
  req.query as Record<string, string | undefined>;
182
- res
183
- .status(200)
184
- .json(
185
- await auth.listAuditLog({
186
- userId,
187
- action,
188
- since,
189
- until,
190
- page: page ? Number(page) : undefined,
191
- limit: limit ? Number(limit) : undefined,
192
- }),
193
- );
180
+ res.status(200).json(
181
+ await auth.listAuditLog({
182
+ userId,
183
+ action,
184
+ since,
185
+ until,
186
+ page: page ? Number(page) : undefined,
187
+ limit: limit ? Number(limit) : undefined,
188
+ }),
189
+ );
194
190
  } catch (err) {
195
191
  next(err);
196
192
  }
@@ -24,14 +24,23 @@ import { eq, inArray, notInArray } from "drizzle-orm";
24
24
  import { drizzle } from "drizzle-orm/node-postgres";
25
25
  import { Pool } from "pg";
26
26
  import { hashPassword } from "@/lib/auth/core/crypto.js";
27
- import type { Database } from "./db.js";
28
- import { DEFAULT_ROLES, PERMISSION_SLUGS, SEED_ADMIN_ROLES, SEED_SUPERADMIN_ROLES, provisionDefaultRoles } from "./rbac.defaults.js";
27
+ import type { Database } from "./config/db.js";
28
+ import {
29
+ DEFAULT_ROLES,
30
+ PERMISSION_SLUGS,
31
+ SEED_ADMIN_ROLES,
32
+ SEED_SUPERADMIN_ROLES,
33
+ provisionDefaultRoles,
34
+ } from "./rbac.defaults.js";
29
35
  import * as schema from "./schema.js";
30
36
  import { permissions, roleUser, roles, users } from "./schema.js";
31
37
 
32
38
  function requireEnv(name: string): string {
33
39
  const value = process.env[name];
34
- if (!value) throw new Error(`${name} is not set — the seeder cannot reach the database without it`);
40
+ if (!value)
41
+ throw new Error(
42
+ `${name} is not set — the seeder cannot reach the database without it`,
43
+ );
35
44
  return value;
36
45
  }
37
46
 
@@ -39,7 +48,10 @@ function requireEnv(name: string): string {
39
48
  async function seedRbacDefaults(db: Database): Promise<void> {
40
49
  await provisionDefaultRoles(db);
41
50
  console.log(`permissions: ${PERMISSION_SLUGS.length} slug(s) in the catalog`);
42
- for (const role of DEFAULT_ROLES) console.log(`role "${role.slug}": ${role.permissions.length} permission(s)${role.isDefault ? " (signup default)" : ""}`);
51
+ for (const role of DEFAULT_ROLES)
52
+ console.log(
53
+ `role "${role.slug}": ${role.permissions.length} permission(s)${role.isDefault ? " (signup default)" : ""}`,
54
+ );
43
55
 
44
56
  // Slugs that exist in the database but not in this build's catalog. They are not an error —
45
57
  // `POST /auth/admin/permissions` exists so a deployment can define its own, and a grant can
@@ -49,9 +61,18 @@ async function seedRbacDefaults(db: Database): Promise<void> {
49
61
  .select({ slug: permissions.slug })
50
62
  .from(permissions)
51
63
  .where(notInArray(permissions.slug, PERMISSION_SLUGS));
52
- if (unknown.length) console.log(`permissions: ${unknown.length} slug(s) outside this build's catalog (no route names them): ${unknown.map((r) => r.slug).join(", ")}`);
53
- const inactive = await db.select({ slug: permissions.slug }).from(permissions).where(eq(permissions.isActive, false));
54
- if (inactive.length) console.log(`permissions: ${inactive.length} deactivated, granting nothing: ${inactive.map((r) => r.slug).join(", ")}`);
64
+ if (unknown.length)
65
+ console.log(
66
+ `permissions: ${unknown.length} slug(s) outside this build's catalog (no route names them): ${unknown.map((r) => r.slug).join(", ")}`,
67
+ );
68
+ const inactive = await db
69
+ .select({ slug: permissions.slug })
70
+ .from(permissions)
71
+ .where(eq(permissions.isActive, false));
72
+ if (inactive.length)
73
+ console.log(
74
+ `permissions: ${inactive.length} deactivated, granting nothing: ${inactive.map((r) => r.slug).join(", ")}`,
75
+ );
55
76
  }
56
77
 
57
78
  /** Roles are global here, so administrative authority is a property of the user row itself. */
@@ -59,15 +80,32 @@ async function seedAdminUser(db: Database): Promise<void> {
59
80
  const email = process.env["SEED_ADMIN_EMAIL"];
60
81
  const password = process.env["SEED_ADMIN_PASSWORD"];
61
82
  if (!email || !password) {
62
- console.log("admin: skipped — set SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD to create one (there is no default password)");
83
+ console.log(
84
+ "admin: skipped — set SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD to create one (there is no default password)",
85
+ );
63
86
  return;
64
87
  }
65
88
 
66
89
  // Their password may have been changed since; rewriting it here would silently reset it.
67
- const [existing] = await db.select({ id: users.id }).from(users).where(eq(users.email, email)).limit(1);
90
+ const [existing] = await db
91
+ .select({ id: users.id })
92
+ .from(users)
93
+ .where(eq(users.email, email))
94
+ .limit(1);
68
95
  // Hashed by the same function signup uses, so the seeded admin can actually log in.
69
- const user = existing ?? (await db.insert(users).values({ email, passwordHash: await hashPassword(password) }).returning({ id: users.id }))[0];
70
- console.log(existing ? `admin: ${email} already exists — password left unchanged` : `admin: created ${email}`);
96
+ const user =
97
+ existing ??
98
+ (
99
+ await db
100
+ .insert(users)
101
+ .values({ email, passwordHash: await hashPassword(password) })
102
+ .returning({ id: users.id })
103
+ )[0];
104
+ console.log(
105
+ existing
106
+ ? `admin: ${email} already exists — password left unchanged`
107
+ : `admin: created ${email}`,
108
+ );
71
109
 
72
110
  const adminRoles = await db
73
111
  .select({ id: roles.id, slug: roles.slug })
@@ -87,16 +125,33 @@ async function seedSuperAdminUser(db: Database): Promise<void> {
87
125
  const email = process.env["SEED_SUPERADMIN_EMAIL"];
88
126
  const password = process.env["SEED_SUPERADMIN_PASSWORD"];
89
127
  if (!email || !password) {
90
- console.log("super admin: skipped — set SEED_SUPERADMIN_EMAIL and SEED_SUPERADMIN_PASSWORD to create one (there is no default password)");
128
+ console.log(
129
+ "super admin: skipped — set SEED_SUPERADMIN_EMAIL and SEED_SUPERADMIN_PASSWORD to create one (there is no default password)",
130
+ );
91
131
  return;
92
132
  }
93
133
  const username = process.env["SEED_SUPERADMIN_USERNAME"] || undefined;
94
134
 
95
135
  // Their password may have been changed since; rewriting it here would silently reset it.
96
- const [existing] = await db.select({ id: users.id }).from(users).where(eq(users.email, email)).limit(1);
136
+ const [existing] = await db
137
+ .select({ id: users.id })
138
+ .from(users)
139
+ .where(eq(users.email, email))
140
+ .limit(1);
97
141
  // Hashed by the same function signup uses, so the seeded super admin can actually log in.
98
- const user = existing ?? (await db.insert(users).values({ email, username, passwordHash: await hashPassword(password) }).returning({ id: users.id }))[0];
99
- console.log(existing ? `super admin: ${email} already exists — password left unchanged` : `super admin: created ${email}${username ? ` (username ${username})` : ""}`);
142
+ const user =
143
+ existing ??
144
+ (
145
+ await db
146
+ .insert(users)
147
+ .values({ email, username, passwordHash: await hashPassword(password) })
148
+ .returning({ id: users.id })
149
+ )[0];
150
+ console.log(
151
+ existing
152
+ ? `super admin: ${email} already exists — password left unchanged`
153
+ : `super admin: created ${email}${username ? ` (username ${username})` : ""}`,
154
+ );
100
155
 
101
156
  const superAdminRoles = await db
102
157
  .select({ id: roles.id, slug: roles.slug })
@@ -105,10 +160,14 @@ async function seedSuperAdminUser(db: Database): Promise<void> {
105
160
  if (superAdminRoles.length) {
106
161
  await db
107
162
  .insert(roleUser)
108
- .values(superAdminRoles.map((role) => ({ userId: user.id, roleId: role.id })))
163
+ .values(
164
+ superAdminRoles.map((role) => ({ userId: user.id, roleId: role.id })),
165
+ )
109
166
  .onConflictDoNothing({ target: [roleUser.userId, roleUser.roleId] });
110
167
  }
111
- console.log(`super admin: holds roles ${superAdminRoles.map((r) => r.slug).join(", ")}`);
168
+ console.log(
169
+ `super admin: holds roles ${superAdminRoles.map((r) => r.slug).join(", ")}`,
170
+ );
112
171
  }
113
172
 
114
173
  async function main(): Promise<void> {
@@ -42,17 +42,17 @@ import {
42
42
  generateTotpSecret,
43
43
  verifyTotpCode,
44
44
  } from "@/lib/auth/core/two-factor.js";
45
- import type { AuthConfig } from "../auth.config.js";
46
- import { HttpError } from "../http-error.js";
45
+ import type { AuthConfig } from "../config/auth.config.js";
46
+ import { HttpError } from "../errors/http-error.js";
47
47
  import {
48
48
  AuditLogEntry,
49
49
  AuditLogListFilter,
50
50
  AuditLogRepository,
51
51
  toAuditLogEntry,
52
52
  } from "../repositories/audit-log.repository.js";
53
- import type { Paginated } from "../pagination.js";
54
- import type { Database } from "../db.js";
55
- import { KeyProviderService } from "../key-provider.js";
53
+ import type { Paginated } from "../helpers/pagination.js";
54
+ import type { Database } from "../config/db.js";
55
+ import { KeyProviderService } from "../config/key-provider.js";
56
56
  import { OAuthRepository } from "../repositories/oauth.repository.js";
57
57
  import { PasswordResetRepository } from "../repositories/password-reset.repository.js";
58
58
  import type { Revoker } from "@/lib/auth/core/types.js";
@@ -69,7 +69,7 @@ import {
69
69
  import { sessions, users } from "../schema.js";
70
70
  import { SessionRepository } from "../repositories/session.repository.js";
71
71
  import { TwoFactorRepository } from "../repositories/two-factor.repository.js";
72
- import { toId, toIdOrNull } from "../id.helper.js";
72
+ import { toId, toIdOrNull } from "../helpers/id.helper.js";
73
73
 
74
74
  export interface AuthTokens {
75
75
  accessToken: string;
@@ -472,9 +472,7 @@ export class AuthService {
472
472
  return { twoFactorEnabled: user.twoFactorEnabled };
473
473
  }
474
474
 
475
- async listActiveSessions(
476
- userId: string,
477
- ): Promise<
475
+ async listActiveSessions(userId: string): Promise<
478
476
  Array<{
479
477
  id: string;
480
478
  createdAt: string;