@questpie/admin 3.0.3 → 3.0.4

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 (249) hide show
  1. package/README.md +34 -5
  2. package/dist/client/blocks/block-renderer.d.mts +2 -2
  3. package/dist/client/blocks/block-renderer.mjs +4 -1
  4. package/dist/client/builder/types/action-types.d.mts +31 -3
  5. package/dist/client/builder/types/collection-types.d.mts +140 -0
  6. package/dist/client/builder/types/ui-config.d.mts +16 -2
  7. package/dist/client/builder/types/views.d.mts +57 -0
  8. package/dist/client/builder/types/widget-types.d.mts +5 -0
  9. package/dist/client/components/actions/action-button.mjs +137 -199
  10. package/dist/client/components/actions/action-dialog.mjs +198 -156
  11. package/dist/client/components/actions/confirmation-dialog.mjs +2 -2
  12. package/dist/client/components/actions/header-actions.mjs +52 -53
  13. package/dist/client/components/admin-link.d.mts +2 -2
  14. package/dist/client/components/auth/auth-loading.mjs +41 -18
  15. package/dist/client/components/blocks/block-fields-renderer.mjs +64 -28
  16. package/dist/client/components/blocks/block-insert-button.mjs +4 -4
  17. package/dist/client/components/blocks/block-item.mjs +2 -2
  18. package/dist/client/components/blocks/block-library-sidebar.mjs +2 -2
  19. package/dist/client/components/component-renderer.mjs +1 -1
  20. package/dist/client/components/fields/array-field.mjs +14 -14
  21. package/dist/client/components/fields/asset-preview-field.mjs +1 -1
  22. package/dist/client/components/fields/blocks-field/blocks-field.mjs +84 -104
  23. package/dist/client/components/fields/json-field.mjs +2 -2
  24. package/dist/client/components/fields/object-array-field.mjs +22 -22
  25. package/dist/client/components/fields/object-field.mjs +5 -5
  26. package/dist/client/components/fields/relation/displays/cards-display.mjs +16 -9
  27. package/dist/client/components/fields/relation/displays/chips-display.mjs +15 -12
  28. package/dist/client/components/fields/relation/displays/grid-display.mjs +15 -11
  29. package/dist/client/components/fields/relation/displays/list-display.mjs +33 -20
  30. package/dist/client/components/fields/relation/displays/table-display.mjs +62 -93
  31. package/dist/client/components/fields/relation/relation-items-display.mjs +1 -1
  32. package/dist/client/components/fields/relation-picker.mjs +7 -6
  33. package/dist/client/components/fields/relation-select.mjs +71 -47
  34. package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +392 -82
  35. package/dist/client/components/fields/rich-text-editor/extensions.mjs +54 -23
  36. package/dist/client/components/fields/rich-text-editor/image-popover.mjs +24 -50
  37. package/dist/client/components/fields/rich-text-editor/image-upload.mjs +66 -0
  38. package/dist/client/components/fields/rich-text-editor/index.d.mts +38 -0
  39. package/dist/client/components/fields/rich-text-editor/index.mjs +637 -376
  40. package/dist/client/components/fields/rich-text-editor/link-utils.mjs +26 -0
  41. package/dist/client/components/fields/rich-text-editor/presets.d.mts +10 -0
  42. package/dist/client/components/fields/rich-text-editor/slash-commands.mjs +27 -6
  43. package/dist/client/components/fields/rich-text-editor/toolbar.mjs +464 -346
  44. package/dist/client/components/fields/rich-text-editor/types.d.mts +77 -0
  45. package/dist/client/components/fields/upload-field.mjs +45 -49
  46. package/dist/client/components/filter-builder/columns-tab.mjs +69 -62
  47. package/dist/client/components/filter-builder/filter-builder-sheet.mjs +473 -308
  48. package/dist/client/components/filter-builder/filters-tab.mjs +109 -82
  49. package/dist/client/components/filter-builder/saved-views-tab.mjs +300 -198
  50. package/dist/client/components/history-sidebar.mjs +850 -340
  51. package/dist/client/components/layout/field-layout-renderer.mjs +6 -5
  52. package/dist/client/components/locale-switcher.mjs +8 -8
  53. package/dist/client/components/media/media-grid.mjs +12 -9
  54. package/dist/client/components/media/media-picker-dialog.mjs +242 -230
  55. package/dist/client/components/preview/live-preview-mode.mjs +1 -1
  56. package/dist/client/components/primitives/asset-preview.mjs +37 -22
  57. package/dist/client/components/primitives/date-input.mjs +212 -249
  58. package/dist/client/components/primitives/dropzone.mjs +192 -159
  59. package/dist/client/components/primitives/field-select-control.mjs +93 -0
  60. package/dist/client/components/primitives/select-multi.mjs +251 -230
  61. package/dist/client/components/primitives/select-single.mjs +345 -290
  62. package/dist/client/components/primitives/time-input.mjs +2 -2
  63. package/dist/client/components/sheets/resource-sheet.mjs +2 -0
  64. package/dist/client/components/ui/accordion.mjs +4 -4
  65. package/dist/client/components/ui/alert.mjs +3 -3
  66. package/dist/client/components/ui/badge.mjs +4 -4
  67. package/dist/client/components/ui/button.mjs +47 -37
  68. package/dist/client/components/ui/card.mjs +2 -2
  69. package/dist/client/components/ui/checkbox.mjs +1 -1
  70. package/dist/client/components/ui/command.mjs +5 -5
  71. package/dist/client/components/ui/dialog.mjs +3 -3
  72. package/dist/client/components/ui/drawer.mjs +1 -1
  73. package/dist/client/components/ui/dropdown-menu.mjs +157 -15
  74. package/dist/client/components/ui/empty-state.mjs +88 -59
  75. package/dist/client/components/ui/field.mjs +2 -2
  76. package/dist/client/components/ui/input-group.mjs +3 -3
  77. package/dist/client/components/ui/input.mjs +1 -1
  78. package/dist/client/components/ui/kbd.mjs +1 -1
  79. package/dist/client/components/ui/label.mjs +1 -1
  80. package/dist/client/components/ui/popover.mjs +19 -11
  81. package/dist/client/components/ui/scroll-fade.mjs +170 -0
  82. package/dist/client/components/ui/search-input.mjs +1 -1
  83. package/dist/client/components/ui/select.mjs +129 -27
  84. package/dist/client/components/ui/sheet.mjs +54 -34
  85. package/dist/client/components/ui/sidebar.mjs +15 -14
  86. package/dist/client/components/ui/skeleton.mjs +28 -12
  87. package/dist/client/components/ui/switch.mjs +2 -2
  88. package/dist/client/components/ui/table.mjs +82 -74
  89. package/dist/client/components/ui/tabs.mjs +26 -31
  90. package/dist/client/components/ui/textarea.mjs +1 -1
  91. package/dist/client/components/ui/tooltip.mjs +1 -1
  92. package/dist/client/components/widgets/chart-widget.mjs +134 -96
  93. package/dist/client/components/widgets/progress-widget.mjs +59 -34
  94. package/dist/client/components/widgets/quick-actions-widget.mjs +184 -113
  95. package/dist/client/components/widgets/recent-items-widget.mjs +144 -102
  96. package/dist/client/components/widgets/stats-widget.mjs +91 -72
  97. package/dist/client/components/widgets/table-widget.mjs +159 -246
  98. package/dist/client/components/widgets/timeline-widget.mjs +66 -43
  99. package/dist/client/components/widgets/value-widget.mjs +261 -152
  100. package/dist/client/components/widgets/widget-empty-state.mjs +88 -0
  101. package/dist/client/components/widgets/widget-skeletons.mjs +53 -20
  102. package/dist/client/contexts/focus-context.d.mts +2 -2
  103. package/dist/client/hooks/use-action.mjs +63 -55
  104. package/dist/client/hooks/use-audit-history.mjs +1 -65
  105. package/dist/client/hooks/use-collection-validation.mjs +36 -23
  106. package/dist/client/hooks/use-collection.mjs +96 -1
  107. package/dist/client/hooks/use-saved-views.mjs +70 -49
  108. package/dist/client/hooks/use-server-actions.mjs +59 -40
  109. package/dist/client/hooks/use-server-validation.mjs +156 -41
  110. package/dist/client/hooks/use-server-widget-data.mjs +1 -1
  111. package/dist/client/hooks/use-setup-status.d.mts +3 -3
  112. package/dist/client/hooks/use-setup-status.mjs +2 -2
  113. package/dist/client/hooks/use-transition-stage.mjs +2 -10
  114. package/dist/client/hooks/use-validation-error-map.mjs +31 -13
  115. package/dist/client/hooks/use-view-state.mjs +238 -174
  116. package/dist/client/i18n/date-locale.mjs +33 -0
  117. package/dist/client/i18n/hooks.mjs +17 -1
  118. package/dist/client/lib/utils.mjs +3 -2
  119. package/dist/client/preview/block-scope-context.d.mts +2 -2
  120. package/dist/client/preview/preview-banner.d.mts +2 -2
  121. package/dist/client/preview/preview-field.d.mts +4 -4
  122. package/dist/client/preview/preview-field.mjs +2 -2
  123. package/dist/client/runtime/provider.mjs +8 -1
  124. package/dist/client/runtime/translations-provider.mjs +1 -1
  125. package/dist/client/scope/picker.d.mts +2 -2
  126. package/dist/client/scope/provider.d.mts +2 -2
  127. package/dist/client/styles/base.css +1022 -0
  128. package/dist/client/styles/index.css +3 -589
  129. package/dist/client/utils/auto-expand-fields.mjs +4 -2
  130. package/dist/client/utils/keyboard-shortcuts.mjs +26 -0
  131. package/dist/client/utils/use-lazy-component.mjs +80 -0
  132. package/dist/client/views/auth/auth-layout.d.mts +18 -11
  133. package/dist/client/views/auth/auth-layout.mjs +291 -80
  134. package/dist/client/views/auth/forgot-password-form.d.mts +2 -2
  135. package/dist/client/views/auth/forgot-password-form.mjs +2 -2
  136. package/dist/client/views/auth/login-form.d.mts +2 -2
  137. package/dist/client/views/auth/login-form.mjs +1 -1
  138. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  139. package/dist/client/views/auth/reset-password-form.mjs +2 -2
  140. package/dist/client/views/auth/setup-form.d.mts +2 -2
  141. package/dist/client/views/collection/auto-form-fields.mjs +11 -9
  142. package/dist/client/views/collection/bulk-action-toolbar.mjs +173 -138
  143. package/dist/client/views/collection/cells/complex-cells.mjs +22 -22
  144. package/dist/client/views/collection/cells/primitive-cells.mjs +1 -1
  145. package/dist/client/views/collection/cells/relation-cells.mjs +147 -129
  146. package/dist/client/views/collection/cells/shared/asset-thumbnail.mjs +224 -278
  147. package/dist/client/views/collection/cells/shared/relation-chip.mjs +64 -36
  148. package/dist/client/views/collection/cells/upload-cells.mjs +199 -9
  149. package/dist/client/views/collection/columns/build-columns.mjs +29 -9
  150. package/dist/client/views/collection/columns/column-defaults.mjs +2 -2
  151. package/dist/client/views/collection/field-renderer.mjs +50 -89
  152. package/dist/client/views/collection/form-view.mjs +237 -227
  153. package/dist/client/views/collection/table-view.mjs +1162 -229
  154. package/dist/client/views/collection/view-skeletons.mjs +222 -79
  155. package/dist/client/views/common/global-search.mjs +29 -18
  156. package/dist/client/views/dashboard/dashboard-grid.mjs +678 -501
  157. package/dist/client/views/dashboard/dashboard-widget.mjs +6 -3
  158. package/dist/client/views/dashboard/widget-card.mjs +23 -14
  159. package/dist/client/views/globals/global-form-view.mjs +634 -589
  160. package/dist/client/views/layout/admin-layout-provider.mjs +67 -70
  161. package/dist/client/views/layout/admin-layout.d.mts +3 -6
  162. package/dist/client/views/layout/admin-layout.mjs +149 -172
  163. package/dist/client/views/layout/admin-router.mjs +747 -544
  164. package/dist/client/views/layout/admin-sidebar.d.mts +38 -1
  165. package/dist/client/views/layout/admin-sidebar.mjs +751 -591
  166. package/dist/client/views/layout/admin-theme.d.mts +10 -0
  167. package/dist/client/views/layout/admin-theme.mjs +84 -0
  168. package/dist/client/views/layout/admin-view-layout.mjs +161 -0
  169. package/dist/client/views/pages/accept-invite-page.d.mts +2 -2
  170. package/dist/client/views/pages/accept-invite-page.mjs +49 -26
  171. package/dist/client/views/pages/dashboard-page.d.mts +2 -2
  172. package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
  173. package/dist/client/views/pages/forgot-password-page.mjs +2 -19
  174. package/dist/client/views/pages/invite-page.d.mts +2 -2
  175. package/dist/client/views/pages/invite-page.mjs +2 -19
  176. package/dist/client/views/pages/login-page.d.mts +1 -1
  177. package/dist/client/views/pages/login-page.mjs +4 -21
  178. package/dist/client/views/pages/reset-password-page.d.mts +2 -2
  179. package/dist/client/views/pages/reset-password-page.mjs +3 -20
  180. package/dist/client/views/pages/setup-page.d.mts +2 -2
  181. package/dist/client/views/pages/setup-page.mjs +3 -20
  182. package/dist/client.d.mts +6 -2
  183. package/dist/client.mjs +2 -1
  184. package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
  185. package/dist/index.d.mts +6 -2
  186. package/dist/index.mjs +2 -1
  187. package/dist/server/augmentation/dashboard.d.mts +67 -3
  188. package/dist/server/augmentation/form-layout.d.mts +21 -0
  189. package/dist/server/augmentation/index.d.mts +1 -1
  190. package/dist/server/codegen/admin-client-template.mjs +4 -0
  191. package/dist/server/fields/blocks.d.mts +1 -1
  192. package/dist/server/fields/blocks.mjs +12 -0
  193. package/dist/server/fields/rich-text.d.mts +1 -1
  194. package/dist/server/fields/rich-text.mjs +8 -0
  195. package/dist/server/i18n/index.mjs +17 -1
  196. package/dist/server/i18n/messages/cs.mjs +23 -0
  197. package/dist/server/i18n/messages/de.mjs +23 -0
  198. package/dist/server/i18n/messages/en.mjs +64 -1
  199. package/dist/server/i18n/messages/es.mjs +23 -0
  200. package/dist/server/i18n/messages/fr.mjs +23 -0
  201. package/dist/server/i18n/messages/pl.mjs +23 -0
  202. package/dist/server/i18n/messages/pt.mjs +23 -0
  203. package/dist/server/i18n/messages/sk.mjs +83 -1
  204. package/dist/server/modules/admin/block/introspection.mjs +4 -1
  205. package/dist/server/modules/admin/block/prefetch.mjs +12 -2
  206. package/dist/server/modules/admin/collections/account.d.mts +50 -50
  207. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  208. package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
  209. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
  210. package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
  211. package/dist/server/modules/admin/collections/assets.d.mts +20 -20
  212. package/dist/server/modules/admin/collections/assets.mjs +0 -1
  213. package/dist/server/modules/admin/collections/session.d.mts +42 -42
  214. package/dist/server/modules/admin/collections/user.d.mts +12 -0
  215. package/dist/server/modules/admin/collections/user.mjs +40 -9
  216. package/dist/server/modules/admin/collections/verification.d.mts +2 -2
  217. package/dist/server/modules/admin/dto/admin-config.dto.mjs +2 -0
  218. package/dist/server/modules/admin/factories.mjs +7 -18
  219. package/dist/server/modules/admin/index.d.mts +1 -1
  220. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  221. package/dist/server/modules/admin/routes/admin-config.mjs +34 -16
  222. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  223. package/dist/server/modules/admin/routes/execute-action.mjs +33 -0
  224. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  225. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  226. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  227. package/dist/server/modules/admin/routes/setup.d.mts +10 -10
  228. package/dist/server/modules/admin/routes/setup.mjs +7 -7
  229. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  230. package/dist/server/modules/admin/routes/translations.mjs +5 -1
  231. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  232. package/dist/server/modules/admin-preferences/collections/admin-preferences.mjs +1 -1
  233. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -27
  234. package/dist/server/modules/audit/.generated/module.d.mts +1 -1
  235. package/dist/server/modules/audit/.generated/module.mjs +1 -1
  236. package/dist/server/modules/audit/collections/audit-log.d.mts +2 -2
  237. package/dist/server/modules/audit/collections/audit-log.mjs +1 -1
  238. package/dist/server/modules/audit/config/app.mjs +99 -42
  239. package/dist/server/modules/audit/jobs/audit-cleanup.mjs +1 -1
  240. package/dist/server/plugin.mjs +4 -2
  241. package/dist/server/proxy-factories.d.mts +4 -3
  242. package/dist/server/proxy-factories.mjs +34 -8
  243. package/dist/shared/types/saved-views.types.d.mts +2 -0
  244. package/package.json +6 -4
  245. package/dist/client/components/fields/rich-text-editor/link-popover.mjs +0 -85
  246. package/dist/client/components/ui/spinner.mjs +0 -52
  247. package/dist/client/components/ui/toolbar.mjs +0 -136
  248. package/dist/client/contexts/breadcrumb-context.mjs +0 -60
  249. package/dist/client/views/layout/admin-topbar.mjs +0 -236
@@ -1,4 +1,4 @@
1
- import * as questpie143 from "questpie";
1
+ import * as questpie135 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/reactive.d.ts
4
4
 
@@ -13,7 +13,7 @@ import * as questpie143 from "questpie";
13
13
  * Batch reactive endpoint.
14
14
  * Executes multiple reactive handlers in a single request.
15
15
  */
16
- declare const batchReactive: questpie143.JsonRouteDefinition<{
16
+ declare const batchReactive: questpie135.JsonRouteDefinition<{
17
17
  collection: string;
18
18
  type: "collection" | "global";
19
19
  requests: {
@@ -26,12 +26,12 @@ declare const batchReactive: questpie143.JsonRouteDefinition<{
26
26
  }[];
27
27
  formData?: Record<string, unknown> | undefined;
28
28
  prevData?: Record<string, unknown> | null | undefined;
29
- }, any, questpie143.JsonRouteParams>;
29
+ }, any, questpie135.JsonRouteParams>;
30
30
  /**
31
31
  * Dynamic options endpoint.
32
32
  * Fetches options for select/relation fields with search and pagination.
33
33
  */
34
- declare const fieldOptions: questpie143.JsonRouteDefinition<{
34
+ declare const fieldOptions: questpie135.JsonRouteDefinition<{
35
35
  collection: string;
36
36
  type: "collection" | "global";
37
37
  field: string;
@@ -40,12 +40,12 @@ declare const fieldOptions: questpie143.JsonRouteDefinition<{
40
40
  page: number;
41
41
  limit: number;
42
42
  siblingData?: Record<string, unknown> | null | undefined;
43
- }, any, questpie143.JsonRouteParams>;
43
+ }, any, questpie135.JsonRouteParams>;
44
44
  /**
45
45
  * Reactive functions bundle.
46
46
  */
47
47
  declare const reactiveFunctions: {
48
- readonly batchReactive: questpie143.JsonRouteDefinition<{
48
+ readonly batchReactive: questpie135.JsonRouteDefinition<{
49
49
  collection: string;
50
50
  type: "collection" | "global";
51
51
  requests: {
@@ -58,8 +58,8 @@ declare const reactiveFunctions: {
58
58
  }[];
59
59
  formData?: Record<string, unknown> | undefined;
60
60
  prevData?: Record<string, unknown> | null | undefined;
61
- }, any, questpie143.JsonRouteParams>;
62
- readonly fieldOptions: questpie143.JsonRouteDefinition<{
61
+ }, any, questpie135.JsonRouteParams>;
62
+ readonly fieldOptions: questpie135.JsonRouteDefinition<{
63
63
  collection: string;
64
64
  type: "collection" | "global";
65
65
  field: string;
@@ -68,7 +68,7 @@ declare const reactiveFunctions: {
68
68
  page: number;
69
69
  limit: number;
70
70
  siblingData?: Record<string, unknown> | null | undefined;
71
- }, any, questpie143.JsonRouteParams>;
71
+ }, any, questpie135.JsonRouteParams>;
72
72
  };
73
73
  //#endregion
74
74
  export { batchReactive, fieldOptions, reactiveFunctions };
@@ -1,4 +1,4 @@
1
- import * as questpie135 from "questpie";
1
+ import * as questpie143 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/setup.d.ts
4
4
 
@@ -10,7 +10,7 @@ import * as questpie135 from "questpie";
10
10
  * need an existing admin to create the first invitation.
11
11
  */
12
12
  /**
13
- * Check if setup is required (no users exist in the system).
13
+ * Check if setup is required (no admin users exist in the system).
14
14
  *
15
15
  * @example
16
16
  * ```ts
@@ -20,12 +20,12 @@ import * as questpie135 from "questpie";
20
20
  * }
21
21
  * ```
22
22
  */
23
- declare const isSetupRequired: questpie135.JsonRouteDefinition<Record<string, never>, any, questpie135.JsonRouteParams>;
23
+ declare const isSetupRequired: questpie143.JsonRouteDefinition<Record<string, never>, any, questpie143.JsonRouteParams>;
24
24
  /**
25
25
  * Create the first admin user in the system.
26
- * This function only works when no users exist (setup mode).
26
+ * This function only works when no admin users exist (setup mode).
27
27
  *
28
- * Security: Once any user exists, this function will refuse to create more users.
28
+ * Security: Once any admin user exists, this function will refuse to create more users.
29
29
  * This prevents unauthorized admin creation after initial setup.
30
30
  *
31
31
  * @example
@@ -43,21 +43,21 @@ declare const isSetupRequired: questpie135.JsonRouteDefinition<Record<string, ne
43
43
  * }
44
44
  * ```
45
45
  */
46
- declare const createFirstAdmin: questpie135.JsonRouteDefinition<{
46
+ declare const createFirstAdmin: questpie143.JsonRouteDefinition<{
47
47
  email: string;
48
48
  password: string;
49
49
  name: string;
50
- }, any, questpie135.JsonRouteParams>;
50
+ }, any, questpie143.JsonRouteParams>;
51
51
  /**
52
52
  * Bundle of setup-related functions.
53
53
  */
54
54
  declare const setupFunctions: {
55
- readonly isSetupRequired: questpie135.JsonRouteDefinition<Record<string, never>, any, questpie135.JsonRouteParams>;
56
- readonly createFirstAdmin: questpie135.JsonRouteDefinition<{
55
+ readonly isSetupRequired: questpie143.JsonRouteDefinition<Record<string, never>, any, questpie143.JsonRouteParams>;
56
+ readonly createFirstAdmin: questpie143.JsonRouteDefinition<{
57
57
  email: string;
58
58
  password: string;
59
59
  name: string;
60
- }, any, questpie135.JsonRouteParams>;
60
+ }, any, questpie143.JsonRouteParams>;
61
61
  };
62
62
  //#endregion
63
63
  export { createFirstAdmin, isSetupRequired, setupFunctions };
@@ -1,7 +1,7 @@
1
1
  import { getApp } from "./route-helpers.mjs";
2
2
  import { z } from "zod";
3
3
  import { route } from "questpie";
4
- import { eq, sql as sql$1 } from "drizzle-orm";
4
+ import { eq, sql as sql$1 } from "questpie/drizzle";
5
5
 
6
6
  //#region src/server/modules/admin/routes/setup.ts
7
7
  /**
@@ -28,7 +28,7 @@ const createFirstAdminOutputSchema = z.object({
28
28
  error: z.string().optional()
29
29
  });
30
30
  /**
31
- * Check if setup is required (no users exist in the system).
31
+ * Check if setup is required (no admin users exist in the system).
32
32
  *
33
33
  * @example
34
34
  * ```ts
@@ -41,13 +41,13 @@ const createFirstAdminOutputSchema = z.object({
41
41
  const isSetupRequired = route().post().schema(isSetupRequiredSchema).outputSchema(isSetupRequiredOutputSchema).handler(async (ctx) => {
42
42
  const app = getApp(ctx);
43
43
  const userCollection = app.getCollectionConfig("user");
44
- return { required: (await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table))[0].count === 0 };
44
+ return { required: (await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userCollection.table.role, "admin")))[0].count === 0 };
45
45
  });
46
46
  /**
47
47
  * Create the first admin user in the system.
48
- * This function only works when no users exist (setup mode).
48
+ * This function only works when no admin users exist (setup mode).
49
49
  *
50
- * Security: Once any user exists, this function will refuse to create more users.
50
+ * Security: Once any admin user exists, this function will refuse to create more users.
51
51
  * This prevents unauthorized admin creation after initial setup.
52
52
  *
53
53
  * @example
@@ -69,9 +69,9 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
69
69
  const app = getApp(ctx);
70
70
  const input = ctx.input;
71
71
  const userCollection = app.getCollectionConfig("user");
72
- if ((await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table))[0].count > 0) return {
72
+ if ((await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userCollection.table.role, "admin")))[0].count > 0) return {
73
73
  success: false,
74
- error: "Setup already completed - users exist in the system"
74
+ error: "Setup already completed - admin users exist in the system"
75
75
  };
76
76
  try {
77
77
  const signUpResult = await app.auth.api.signUpEmail({ body: {
@@ -1,4 +1,4 @@
1
- import * as questpie462 from "questpie";
1
+ import * as questpie464 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/translations.d.ts
4
4
 
@@ -17,10 +17,10 @@ import * as questpie462 from "questpie";
17
17
  * Bundle of translation-related functions.
18
18
  */
19
19
  declare const translationFunctions: {
20
- readonly getAdminTranslations: questpie462.JsonRouteDefinition<{
20
+ readonly getAdminTranslations: questpie464.JsonRouteDefinition<{
21
21
  locale: string;
22
- }, any, questpie462.JsonRouteParams>;
23
- readonly getAdminLocales: questpie462.JsonRouteDefinition<Record<string, never> | undefined, any, questpie462.JsonRouteParams>;
22
+ }, any, questpie464.JsonRouteParams>;
23
+ readonly getAdminLocales: questpie464.JsonRouteDefinition<Record<string, never> | undefined, any, questpie464.JsonRouteParams>;
24
24
  };
25
25
  //#endregion
26
26
  export { translationFunctions };
@@ -29,7 +29,11 @@ function getAdminLocaleConfig(app) {
29
29
  const getAdminTranslationsSchema = z.object({ locale: z.string() });
30
30
  const messageValueSchema = z.union([z.string(), z.object({
31
31
  one: z.string(),
32
- other: z.string()
32
+ other: z.string(),
33
+ zero: z.string().optional(),
34
+ two: z.string().optional(),
35
+ few: z.string().optional(),
36
+ many: z.string().optional()
33
37
  })]);
34
38
  const getAdminTranslationsOutputSchema = z.object({
35
39
  locale: z.string(),
@@ -1,4 +1,4 @@
1
- import * as questpie69 from "questpie";
1
+ import * as questpie93 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/widget-data.d.ts
4
4
 
@@ -20,13 +20,13 @@ import * as questpie69 from "questpie";
20
20
  * const data = await client.routes.fetchWidgetData({ widgetId: "my-widget" });
21
21
  * ```
22
22
  */
23
- declare const fetchWidgetData: questpie69.JsonRouteDefinition<{
23
+ declare const fetchWidgetData: questpie93.JsonRouteDefinition<{
24
24
  widgetId: string;
25
- }, any, questpie69.JsonRouteParams>;
25
+ }, any, questpie93.JsonRouteParams>;
26
26
  declare const widgetDataFunctions: {
27
- readonly fetchWidgetData: questpie69.JsonRouteDefinition<{
27
+ readonly fetchWidgetData: questpie93.JsonRouteDefinition<{
28
28
  widgetId: string;
29
- }, any, questpie69.JsonRouteParams>;
29
+ }, any, questpie93.JsonRouteParams>;
30
30
  };
31
31
  //#endregion
32
32
  export { fetchWidgetData, widgetDataFunctions };
@@ -1,5 +1,5 @@
1
1
  import { collection } from "questpie";
2
- import { uniqueIndex } from "drizzle-orm/pg-core";
2
+ import { uniqueIndex } from "questpie/drizzle-pg-core";
3
3
 
4
4
  //#region src/server/modules/admin-preferences/collections/admin-preferences.ts
5
5
  /**
@@ -1,10 +1,10 @@
1
1
  import { FilterOperator, FilterRule, SortConfig, ViewConfiguration } from "../../../../shared/types/saved-views.types.mjs";
2
2
  import * as questpie_shared15 from "questpie/shared";
3
- import * as questpie73 from "questpie";
3
+ import * as questpie107 from "questpie";
4
+ import * as questpie_src_server_modules_core_fields_email_js2 from "questpie/src/server/modules/core/fields/email.js";
5
+ import * as questpie_src_server_modules_core_fields_json_js2 from "questpie/src/server/modules/core/fields/json.js";
4
6
  import * as drizzle_orm_pg_core22 from "drizzle-orm/pg-core";
5
7
  import * as drizzle_orm5 from "drizzle-orm";
6
- import * as questpie_src_server_modules_core_fields_email_js1 from "questpie/src/server/modules/core/fields/email.js";
7
- import * as questpie_src_server_modules_core_fields_json_js1 from "questpie/src/server/modules/core/fields/json.js";
8
8
 
9
9
  //#region src/server/modules/admin-preferences/collections/saved-views.d.ts
10
10
 
@@ -33,22 +33,22 @@ import * as questpie_src_server_modules_core_fields_json_js1 from "questpie/src/
33
33
  * });
34
34
  * ```
35
35
  */
36
- declare const savedViewsCollection: questpie73.CollectionBuilder<questpie_shared15.Override<questpie_shared15.Override<questpie73.EmptyCollectionState<"admin_saved_views", undefined, {
37
- readonly text: typeof questpie73.text;
38
- readonly textarea: typeof questpie73.textarea;
39
- readonly email: typeof questpie_src_server_modules_core_fields_email_js1.email;
40
- readonly url: typeof questpie73.url;
41
- readonly number: typeof questpie73.number;
42
- readonly boolean: typeof questpie73.boolean;
43
- readonly date: typeof questpie73.date;
44
- readonly datetime: typeof questpie73.datetime;
45
- readonly time: typeof questpie73.time;
46
- readonly select: typeof questpie73.select;
47
- readonly upload: typeof questpie73.upload;
48
- readonly relation: typeof questpie73.relation;
49
- readonly object: typeof questpie73.object;
50
- readonly json: typeof questpie_src_server_modules_core_fields_json_js1.json;
51
- readonly from: typeof questpie73.from;
36
+ declare const savedViewsCollection: questpie107.CollectionBuilder<questpie_shared15.Override<questpie_shared15.Override<questpie107.EmptyCollectionState<"admin_saved_views", undefined, {
37
+ readonly text: typeof questpie107.text;
38
+ readonly textarea: typeof questpie107.textarea;
39
+ readonly email: typeof questpie_src_server_modules_core_fields_email_js2.email;
40
+ readonly url: typeof questpie107.url;
41
+ readonly number: typeof questpie107.number;
42
+ readonly boolean: typeof questpie107.boolean;
43
+ readonly date: typeof questpie107.date;
44
+ readonly datetime: typeof questpie107.datetime;
45
+ readonly time: typeof questpie107.time;
46
+ readonly select: typeof questpie107.select;
47
+ readonly upload: typeof questpie107.upload;
48
+ readonly relation: typeof questpie107.relation;
49
+ readonly object: typeof questpie107.object;
50
+ readonly json: typeof questpie_src_server_modules_core_fields_json_js2.json;
51
+ readonly from: typeof questpie107.from;
52
52
  }>, {
53
53
  fields: {
54
54
  readonly userId: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
@@ -59,31 +59,31 @@ declare const savedViewsCollection: questpie73.CollectionBuilder<questpie_shared
59
59
  };
60
60
  localized: readonly string[];
61
61
  fieldDefinitions: {
62
- readonly userId: questpie73.FieldWithMethods<Omit<questpie73.TextFieldState, "notNull" | "column"> & {
62
+ readonly userId: questpie107.FieldWithMethods<Omit<questpie107.TextFieldState, "notNull" | "column"> & {
63
63
  notNull: true;
64
64
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
65
65
  } & {
66
66
  label: questpie_shared15.I18nText;
67
- }, questpie73.TextFieldMethods>;
68
- readonly collectionName: questpie73.FieldWithMethods<Omit<questpie73.TextFieldState, "notNull" | "column"> & {
67
+ }, questpie107.TextFieldMethods>;
68
+ readonly collectionName: questpie107.FieldWithMethods<Omit<questpie107.TextFieldState, "notNull" | "column"> & {
69
69
  notNull: true;
70
70
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
71
71
  } & {
72
72
  label: questpie_shared15.I18nText;
73
- }, questpie73.TextFieldMethods>;
74
- readonly name: questpie73.FieldWithMethods<Omit<questpie73.TextFieldState, "notNull" | "column"> & {
73
+ }, questpie107.TextFieldMethods>;
74
+ readonly name: questpie107.FieldWithMethods<Omit<questpie107.TextFieldState, "notNull" | "column"> & {
75
75
  notNull: true;
76
76
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
77
77
  } & {
78
78
  label: questpie_shared15.I18nText;
79
- }, questpie73.TextFieldMethods>;
80
- readonly configuration: questpie73.Field<Omit<questpie73.JsonFieldState, "notNull" | "column"> & {
79
+ }, questpie107.TextFieldMethods>;
80
+ readonly configuration: questpie107.Field<Omit<questpie107.JsonFieldState, "notNull" | "column"> & {
81
81
  notNull: true;
82
82
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgJsonbBuilder>;
83
83
  } & {
84
84
  label: questpie_shared15.I18nText;
85
85
  }>;
86
- readonly isDefault: questpie73.Field<Omit<questpie73.BooleanFieldState, "column" | "hasDefault"> & {
86
+ readonly isDefault: questpie107.Field<Omit<questpie107.BooleanFieldState, "column" | "hasDefault"> & {
87
87
  hasDefault: true;
88
88
  column: drizzle_orm5.HasDefault<drizzle_orm_pg_core22.PgBooleanBuilder>;
89
89
  } & {
@@ -4,7 +4,7 @@ import * as questpie64 from "questpie";
4
4
 
5
5
  //#region src/server/modules/audit/.generated/module.d.ts
6
6
  interface AuditCollections {
7
- auditLogCollection: typeof auditLogCollection;
7
+ admin_audit_log: typeof auditLogCollection;
8
8
  }
9
9
  interface AuditJobs {
10
10
  auditCleanup: typeof auditCleanupJob;
@@ -6,7 +6,7 @@ import app_default from "../config/app.mjs";
6
6
  //#region src/server/modules/audit/.generated/module.ts
7
7
  const _module = {
8
8
  name: "questpie-audit",
9
- collections: { auditLogCollection },
9
+ collections: { admin_audit_log: auditLogCollection },
10
10
  jobs: { auditCleanup: auditCleanupJob },
11
11
  globals: {},
12
12
  routes: {},
@@ -1,9 +1,9 @@
1
1
  import * as questpie_shared0 from "questpie/shared";
2
2
  import * as questpie16 from "questpie";
3
- import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
4
- import * as drizzle_orm0 from "drizzle-orm";
5
3
  import * as questpie_src_server_modules_core_fields_email_js0 from "questpie/src/server/modules/core/fields/email.js";
6
4
  import * as questpie_src_server_modules_core_fields_json_js0 from "questpie/src/server/modules/core/fields/json.js";
5
+ import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
6
+ import * as drizzle_orm0 from "drizzle-orm";
7
7
  import * as questpie_src_server_fields_operators_builtin_js0 from "questpie/src/server/fields/operators/builtin.js";
8
8
 
9
9
  //#region src/server/modules/audit/collections/audit-log.d.ts
@@ -1,5 +1,5 @@
1
1
  import { collection } from "questpie";
2
- import { index } from "drizzle-orm/pg-core";
2
+ import { index } from "questpie/drizzle-pg-core";
3
3
 
4
4
  //#region src/server/modules/audit/collections/audit-log.ts
5
5
  /**
@@ -19,16 +19,36 @@ function isAuditDisabled(type, name) {
19
19
  * Compute field-level changes between original and current data.
20
20
  * Returns an object of `{ field: { from, to } }` or null if no meaningful changes.
21
21
  */
22
+ const SKIP_CHANGE_FIELDS = new Set([
23
+ "updatedAt",
24
+ "createdAt",
25
+ "id"
26
+ ]);
27
+ function shouldSkipChangeField(key) {
28
+ return SKIP_CHANGE_FIELDS.has(key) || key.startsWith("_");
29
+ }
30
+ function makeFieldChangeMap(data, direction) {
31
+ if (!data) return null;
32
+ const changes = {};
33
+ for (const key of Object.keys(data)) {
34
+ if (shouldSkipChangeField(key)) continue;
35
+ const value = data[key];
36
+ if (value == null) continue;
37
+ changes[key] = direction === "create" ? {
38
+ from: null,
39
+ to: value
40
+ } : {
41
+ from: value,
42
+ to: null
43
+ };
44
+ }
45
+ return Object.keys(changes).length > 0 ? changes : null;
46
+ }
22
47
  function computeChanges(original, current) {
23
48
  if (!original) return null;
24
49
  const changes = {};
25
- const skipFields = new Set([
26
- "updatedAt",
27
- "createdAt",
28
- "id"
29
- ]);
30
50
  for (const key of Object.keys(current)) {
31
- if (skipFields.has(key) || key.startsWith("_")) continue;
51
+ if (shouldSkipChangeField(key)) continue;
32
52
  const fromVal = original[key];
33
53
  const toVal = current[key];
34
54
  if (fromVal == null && toVal == null) continue;
@@ -85,24 +105,61 @@ function getResourceTypeLabel(type, name) {
85
105
  * Generate a human-readable title for the audit log entry.
86
106
  */
87
107
  function generateTitle(action, _resourceType, resourceTypeLabel, resourceLabel, userName) {
88
- const user = userName || "Unknown";
89
108
  const resource = resourceLabel || "(unnamed)";
90
- return `${user} ${{
109
+ return `${userName} ${{
91
110
  create: "created",
92
111
  update: "updated",
93
112
  delete: "deleted",
94
113
  transition: "changed status of"
95
114
  }[action] || action} ${resourceTypeLabel} '${resource}'`;
96
115
  }
116
+ function firstNonEmptyString(...values) {
117
+ for (const value of values) {
118
+ if (typeof value !== "string") continue;
119
+ const trimmed = value.trim();
120
+ if (trimmed.length > 0) return trimmed;
121
+ }
122
+ return null;
123
+ }
124
+ function resolveAuditActor(ctx) {
125
+ const user = ctx.session?.user;
126
+ const userId = user?.id != null ? String(user.id) : null;
127
+ const userName = firstNonEmptyString(user?.name, user?.email, userId);
128
+ if (userName) return {
129
+ actorType: "user",
130
+ userId,
131
+ userName
132
+ };
133
+ if (ctx.accessMode === "system") return {
134
+ actorType: "system",
135
+ userId: "system",
136
+ userName: "System"
137
+ };
138
+ return {
139
+ actorType: "anonymous",
140
+ userId: null,
141
+ userName: "Anonymous"
142
+ };
143
+ }
144
+ function buildAuditMetadata(ctx, actor, extra) {
145
+ return {
146
+ actorType: actor.actorType,
147
+ accessMode: ctx.accessMode ?? null,
148
+ ...extra
149
+ };
150
+ }
151
+ function logAuditFailure(ctx, message, err) {
152
+ ctx.logger?.error?.(message, err);
153
+ }
97
154
  async function collectionAfterChange(ctx) {
98
155
  try {
99
156
  const collections = ctx.collections ?? ctx.app?.collections;
100
157
  if (isAuditDisabled("collection", ctx.collection)) return;
101
158
  const action = ctx.operation === "create" ? "create" : "update";
102
- const changes = ctx.operation === "update" ? computeChanges(ctx.original, ctx.data) : null;
159
+ const changes = ctx.operation === "update" ? computeChanges(ctx.original, ctx.data) : makeFieldChangeMap(ctx.data, "create");
103
160
  if (ctx.operation === "update" && !changes) return;
104
161
  const resourceLabel = extractLabel(ctx.data);
105
- const userName = ctx.session?.user?.name || ctx.session?.user?.email || null;
162
+ const actor = resolveAuditActor(ctx);
106
163
  const resourceTypeLabel = getResourceTypeLabel("collection", ctx.collection);
107
164
  await collections[AUDIT_LOG_COLLECTION].create({
108
165
  action,
@@ -110,18 +167,18 @@ async function collectionAfterChange(ctx) {
110
167
  resource: ctx.collection,
111
168
  resourceId: ctx.data?.id ? String(ctx.data.id) : null,
112
169
  resourceLabel,
113
- userId: ctx.session?.user?.id ? String(ctx.session.user.id) : null,
114
- userName,
170
+ userId: actor.userId,
171
+ userName: actor.userName,
115
172
  locale: ctx.locale || null,
116
173
  changes,
117
- metadata: null,
118
- title: generateTitle(action, "collection", resourceTypeLabel, resourceLabel, userName)
174
+ metadata: buildAuditMetadata(ctx, actor, { operation: ctx.operation }),
175
+ title: generateTitle(action, "collection", resourceTypeLabel, resourceLabel, actor.userName)
119
176
  }, {
120
177
  accessMode: "system",
121
178
  db: ctx.db
122
179
  });
123
180
  } catch (err) {
124
- console.error(`[Audit] Failed to log ${ctx.operation} for collection "${ctx.collection}":`, err);
181
+ logAuditFailure(ctx, `[Audit] Failed to log ${ctx.operation} for collection "${ctx.collection}":`, err);
125
182
  }
126
183
  }
127
184
  async function collectionAfterDelete(ctx) {
@@ -129,7 +186,7 @@ async function collectionAfterDelete(ctx) {
129
186
  const collections = ctx.collections ?? ctx.app?.collections;
130
187
  if (isAuditDisabled("collection", ctx.collection)) return;
131
188
  const resourceLabel = extractLabel(ctx.data);
132
- const userName = ctx.session?.user?.name || ctx.session?.user?.email || null;
189
+ const actor = resolveAuditActor(ctx);
133
190
  const resourceTypeLabel = getResourceTypeLabel("collection", ctx.collection);
134
191
  await collections[AUDIT_LOG_COLLECTION].create({
135
192
  action: "delete",
@@ -137,18 +194,18 @@ async function collectionAfterDelete(ctx) {
137
194
  resource: ctx.collection,
138
195
  resourceId: ctx.data?.id ? String(ctx.data.id) : null,
139
196
  resourceLabel,
140
- userId: ctx.session?.user?.id ? String(ctx.session.user.id) : null,
141
- userName,
197
+ userId: actor.userId,
198
+ userName: actor.userName,
142
199
  locale: ctx.locale || null,
143
- changes: null,
144
- metadata: null,
145
- title: generateTitle("delete", "collection", resourceTypeLabel, resourceLabel, userName)
200
+ changes: makeFieldChangeMap(ctx.data, "delete"),
201
+ metadata: buildAuditMetadata(ctx, actor, { operation: "delete" }),
202
+ title: generateTitle("delete", "collection", resourceTypeLabel, resourceLabel, actor.userName)
146
203
  }, {
147
204
  accessMode: "system",
148
205
  db: ctx.db
149
206
  });
150
207
  } catch (err) {
151
- console.error(`[Audit] Failed to log delete for collection "${ctx.collection}":`, err);
208
+ logAuditFailure(ctx, `[Audit] Failed to log delete for collection "${ctx.collection}":`, err);
152
209
  }
153
210
  }
154
211
  async function collectionAfterTransition(ctx) {
@@ -156,7 +213,7 @@ async function collectionAfterTransition(ctx) {
156
213
  const collections = ctx.collections ?? ctx.app?.collections;
157
214
  if (isAuditDisabled("collection", ctx.collection)) return;
158
215
  const resourceLabel = extractLabel(ctx.data);
159
- const userName = ctx.session?.user?.name || ctx.session?.user?.email || null;
216
+ const actor = resolveAuditActor(ctx);
160
217
  const resourceTypeLabel = getResourceTypeLabel("collection", ctx.collection);
161
218
  await collections[AUDIT_LOG_COLLECTION].create({
162
219
  action: "transition",
@@ -164,31 +221,31 @@ async function collectionAfterTransition(ctx) {
164
221
  resource: ctx.collection,
165
222
  resourceId: ctx.data?.id ? String(ctx.data.id) : null,
166
223
  resourceLabel,
167
- userId: ctx.session?.user?.id ? String(ctx.session.user.id) : null,
168
- userName,
224
+ userId: actor.userId,
225
+ userName: actor.userName,
169
226
  locale: ctx.locale || null,
170
227
  changes: { stage: {
171
228
  from: ctx.fromStage,
172
229
  to: ctx.toStage
173
230
  } },
174
- metadata: {
231
+ metadata: buildAuditMetadata(ctx, actor, {
175
232
  fromStage: ctx.fromStage,
176
233
  toStage: ctx.toStage
177
- },
178
- title: generateTitle("transition", "collection", resourceTypeLabel, resourceLabel, userName)
234
+ }),
235
+ title: generateTitle("transition", "collection", resourceTypeLabel, resourceLabel, actor.userName)
179
236
  }, {
180
237
  accessMode: "system",
181
238
  db: ctx.db
182
239
  });
183
240
  } catch (err) {
184
- console.error(`[Audit] Failed to log transition for collection "${ctx.collection}":`, err);
241
+ logAuditFailure(ctx, `[Audit] Failed to log transition for collection "${ctx.collection}":`, err);
185
242
  }
186
243
  }
187
244
  async function globalAfterChange(ctx) {
188
245
  try {
189
246
  const collections = ctx.collections ?? ctx.app?.collections;
190
247
  if (isAuditDisabled("global", ctx.global)) return;
191
- const userName = ctx.session?.user?.name || ctx.session?.user?.email || null;
248
+ const actor = resolveAuditActor(ctx);
192
249
  const resourceTypeLabel = getResourceTypeLabel("global", ctx.global);
193
250
  await collections[AUDIT_LOG_COLLECTION].create({
194
251
  action: "update",
@@ -196,25 +253,25 @@ async function globalAfterChange(ctx) {
196
253
  resource: ctx.global,
197
254
  resourceId: null,
198
255
  resourceLabel: ctx.global,
199
- userId: ctx.session?.user?.id ? String(ctx.session.user.id) : null,
200
- userName,
256
+ userId: actor.userId,
257
+ userName: actor.userName,
201
258
  locale: ctx.locale || null,
202
259
  changes: null,
203
- metadata: null,
204
- title: generateTitle("update", "global", resourceTypeLabel, ctx.global, userName)
260
+ metadata: buildAuditMetadata(ctx, actor, { operation: "update" }),
261
+ title: generateTitle("update", "global", resourceTypeLabel, ctx.global, actor.userName)
205
262
  }, {
206
263
  accessMode: "system",
207
264
  db: ctx.db
208
265
  });
209
266
  } catch (err) {
210
- console.error(`[Audit] Failed to log update for global "${ctx.global}":`, err);
267
+ logAuditFailure(ctx, `[Audit] Failed to log update for global "${ctx.global}":`, err);
211
268
  }
212
269
  }
213
270
  async function globalAfterTransition(ctx) {
214
271
  try {
215
272
  const collections = ctx.collections ?? ctx.app?.collections;
216
273
  if (isAuditDisabled("global", ctx.global)) return;
217
- const userName = ctx.session?.user?.name || ctx.session?.user?.email || null;
274
+ const actor = resolveAuditActor(ctx);
218
275
  const resourceTypeLabel = getResourceTypeLabel("global", ctx.global);
219
276
  await collections[AUDIT_LOG_COLLECTION].create({
220
277
  action: "transition",
@@ -222,24 +279,24 @@ async function globalAfterTransition(ctx) {
222
279
  resource: ctx.global,
223
280
  resourceId: null,
224
281
  resourceLabel: ctx.global,
225
- userId: ctx.session?.user?.id ? String(ctx.session.user.id) : null,
226
- userName,
282
+ userId: actor.userId,
283
+ userName: actor.userName,
227
284
  locale: ctx.locale || null,
228
285
  changes: { stage: {
229
286
  from: ctx.fromStage,
230
287
  to: ctx.toStage
231
288
  } },
232
- metadata: {
289
+ metadata: buildAuditMetadata(ctx, actor, {
233
290
  fromStage: ctx.fromStage,
234
291
  toStage: ctx.toStage
235
- },
236
- title: generateTitle("transition", "global", resourceTypeLabel, ctx.global, userName)
292
+ }),
293
+ title: generateTitle("transition", "global", resourceTypeLabel, ctx.global, actor.userName)
237
294
  }, {
238
295
  accessMode: "system",
239
296
  db: ctx.db
240
297
  });
241
298
  } catch (err) {
242
- console.error(`[Audit] Failed to log transition for global "${ctx.global}":`, err);
299
+ logAuditFailure(ctx, `[Audit] Failed to log transition for global "${ctx.global}":`, err);
243
300
  }
244
301
  }
245
302
  /**
@@ -1,7 +1,7 @@
1
1
  import { AUDIT_LOG_COLLECTION } from "../collections/audit-log.mjs";
2
2
  import { z } from "zod";
3
3
  import { job } from "questpie";
4
- import { sql as sql$1 } from "drizzle-orm";
4
+ import { sql as sql$1 } from "questpie/drizzle";
5
5
 
6
6
  //#region src/server/modules/audit/jobs/audit-cleanup.ts
7
7
  /**