@tulip-systems/core 0.5.0 → 0.5.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 (247) hide show
  1. package/dist/components/client.d.mts +2 -2
  2. package/dist/components/client.mjs +2 -2
  3. package/dist/components/common/icons.d.mts +52 -52
  4. package/dist/components/common/icons.d.mts.map +1 -1
  5. package/dist/components/common/status.d.mts +3 -3
  6. package/dist/components/common/status.d.mts.map +1 -1
  7. package/dist/components/editor/components/content.client.d.mts +2 -2
  8. package/dist/components/editor/components/editor.client.d.mts +5 -5
  9. package/dist/components/editor/components/editor.client.d.mts.map +1 -1
  10. package/dist/components/editor/components/{block-dropdown.mjs → menu-nodes.client.mjs} +7 -11
  11. package/dist/components/editor/components/menu-nodes.client.mjs.map +1 -0
  12. package/dist/components/editor/components/menu.client.d.mts +10 -0
  13. package/dist/components/editor/components/menu.client.d.mts.map +1 -0
  14. package/dist/components/editor/components/menu.client.mjs +166 -0
  15. package/dist/components/editor/components/menu.client.mjs.map +1 -0
  16. package/dist/components/editor/lib/extensions.d.mts +3 -0
  17. package/dist/components/editor/lib/extensions.d.mts.map +1 -1
  18. package/dist/components/editor/lib/extensions.mjs.map +1 -1
  19. package/dist/components/header/back-button.client.d.mts +2 -2
  20. package/dist/components/header/bottom-bar.client.d.mts +3 -3
  21. package/dist/components/header/breadcrumbs.client.d.mts +7 -7
  22. package/dist/components/header/header.client.d.mts +2 -2
  23. package/dist/components/header/mobile-nav-switcher.client.d.mts +2 -2
  24. package/dist/components/header/top-bar.client.d.mts +4 -4
  25. package/dist/components/layouts/admin-content.client.d.mts +2 -2
  26. package/dist/components/layouts/admin-layout.d.mts +2 -2
  27. package/dist/components/layouts/admin-layout.d.mts.map +1 -1
  28. package/dist/components/layouts/admin-loading.d.mts +2 -2
  29. package/dist/components/layouts/admin-loading.d.mts.map +1 -1
  30. package/dist/components/layouts/empty-page.d.mts +4 -4
  31. package/dist/components/layouts/empty-page.d.mts.map +1 -1
  32. package/dist/components/layouts/list-layout.d.mts +2 -2
  33. package/dist/components/layouts/list-layout.d.mts.map +1 -1
  34. package/dist/components/layouts/not-found-page.d.mts +3 -3
  35. package/dist/components/layouts/not-found-page.d.mts.map +1 -1
  36. package/dist/components/layouts/providers.client.d.mts +2 -2
  37. package/dist/components/layouts/root-layout.server.d.mts +2 -2
  38. package/dist/components/layouts/root-layout.server.d.mts.map +1 -1
  39. package/dist/components/layouts/root-loading.d.mts +2 -2
  40. package/dist/components/layouts/root-loading.d.mts.map +1 -1
  41. package/dist/components/layouts/tab-layout.d.mts +2 -2
  42. package/dist/components/layouts/tab-layout.d.mts.map +1 -1
  43. package/dist/components/lists/data-list.d.mts +5 -5
  44. package/dist/components/lists/data-list.d.mts.map +1 -1
  45. package/dist/components/lists/data-stack.d.mts +8 -8
  46. package/dist/components/lists/data-stack.d.mts.map +1 -1
  47. package/dist/components/navigation/admin-sidebar-paths.client.d.mts +10 -10
  48. package/dist/components/ui/accordion.d.mts +5 -5
  49. package/dist/components/ui/alert-dialog.d.mts +12 -12
  50. package/dist/components/ui/alert.d.mts +6 -6
  51. package/dist/components/ui/aspect-ratio.d.mts +2 -2
  52. package/dist/components/ui/avatar.client.d.mts +4 -4
  53. package/dist/components/ui/badge.d.mts +4 -4
  54. package/dist/components/ui/breadcrumb.d.mts +8 -8
  55. package/dist/components/ui/breadcrumb.d.mts.map +1 -1
  56. package/dist/components/ui/button.d.mts +5 -5
  57. package/dist/components/ui/button.d.mts.map +1 -1
  58. package/dist/components/ui/calendar.d.mts +3 -3
  59. package/dist/components/ui/calendar.d.mts.map +1 -1
  60. package/dist/components/ui/card.d.mts +7 -7
  61. package/dist/components/ui/card.d.mts.map +1 -1
  62. package/dist/components/ui/carousel.d.mts +6 -6
  63. package/dist/components/ui/carousel.d.mts.map +1 -1
  64. package/dist/components/ui/chart.client.d.mts +5 -5
  65. package/dist/components/ui/checkbox.d.mts +2 -2
  66. package/dist/components/ui/checkbox.d.mts.map +1 -1
  67. package/dist/components/ui/collapsible.client.d.mts +4 -4
  68. package/dist/components/ui/color-picker.client.d.mts +2 -2
  69. package/dist/components/ui/combobox-dropdown.client.d.mts +2 -2
  70. package/dist/components/ui/combobox.client.d.mts +2 -2
  71. package/dist/components/ui/command.d.mts +10 -10
  72. package/dist/components/ui/command.d.mts.map +1 -1
  73. package/dist/components/ui/context-menu.d.mts +16 -16
  74. package/dist/components/ui/date-picker.client.d.mts +2 -2
  75. package/dist/components/ui/dialog.client.d.mts +11 -11
  76. package/dist/components/ui/drawer.client.d.mts +11 -11
  77. package/dist/components/ui/dropdown-menu.d.mts +16 -16
  78. package/dist/components/ui/form.client.d.mts +7 -7
  79. package/dist/components/ui/hover-card.client.d.mts +4 -4
  80. package/dist/components/ui/input-recipient.d.mts +2 -2
  81. package/dist/components/ui/input.d.mts +2 -2
  82. package/dist/components/ui/label.d.mts +2 -2
  83. package/dist/components/ui/navigation-menu.d.mts +11 -11
  84. package/dist/components/ui/pagination.d.mts +8 -8
  85. package/dist/components/ui/popover.d.mts +5 -5
  86. package/dist/components/ui/progress.client.d.mts +2 -2
  87. package/dist/components/ui/radio-group.d.mts +3 -3
  88. package/dist/components/ui/resizable.client.d.mts +4 -4
  89. package/dist/components/ui/scroll-area.d.mts +3 -3
  90. package/dist/components/ui/select.client.d.mts +11 -11
  91. package/dist/components/ui/separator.d.mts +2 -2
  92. package/dist/components/ui/sheet.client.d.mts +9 -9
  93. package/dist/components/ui/sidebar.client.d.mts +24 -24
  94. package/dist/components/ui/skeleton.d.mts +2 -2
  95. package/dist/components/ui/slider.d.mts +2 -2
  96. package/dist/components/ui/sonner.client.d.mts +2 -2
  97. package/dist/components/ui/switch.d.mts +2 -2
  98. package/dist/components/ui/tabs.d.mts +5 -5
  99. package/dist/components/ui/textarea.d.mts +2 -2
  100. package/dist/components/ui/time-input.client.d.mts +2 -2
  101. package/dist/components/ui/toggle-group.client.d.mts +3 -3
  102. package/dist/components/ui/toggle.d.mts +2 -2
  103. package/dist/components/ui/tooltip.client.d.mts +5 -5
  104. package/dist/lib/hooks/use-action.d.mts +2 -2
  105. package/dist/lib/hooks/use-indicator.d.mts +2 -2
  106. package/dist/lib/hooks/use-indicator.d.mts.map +1 -1
  107. package/dist/modules/auth/components/allowed.client.d.mts +2 -2
  108. package/dist/modules/auth/components/auth-layout.server.d.mts +2 -2
  109. package/dist/modules/auth/components/auth-layout.server.d.mts.map +1 -1
  110. package/dist/modules/auth/components/auth-loading.d.mts +2 -2
  111. package/dist/modules/auth/components/create-first-user-page.client.d.mts +2 -2
  112. package/dist/modules/auth/components/forget-password-page.client.d.mts +2 -2
  113. package/dist/modules/auth/components/guard-first-user.server.d.mts +2 -2
  114. package/dist/modules/auth/components/guard-first-user.server.d.mts.map +1 -1
  115. package/dist/modules/auth/components/guard.server.d.mts +2 -2
  116. package/dist/modules/auth/components/guard.server.d.mts.map +1 -1
  117. package/dist/modules/auth/components/login-page.client.d.mts +2 -2
  118. package/dist/modules/auth/components/reset-password-page.client.d.mts +2 -2
  119. package/dist/modules/auth/components/update-password-command.d.mts +2 -2
  120. package/dist/modules/auth/db/schema.d.mts +73 -73
  121. package/dist/modules/auth/handler/client.client.d.mts +2 -2
  122. package/dist/modules/auth/hooks/use-permission.d.mts +2 -2
  123. package/dist/modules/auth/lib/validators.d.mts +2 -2
  124. package/dist/modules/auth/lib/validators.d.mts.map +1 -1
  125. package/dist/modules/commands/components/alert-dialog-command.client.d.mts +10 -10
  126. package/dist/modules/commands/components/alert-dialog-command.client.d.mts.map +1 -1
  127. package/dist/modules/commands/components/click-command.client.d.mts +2 -2
  128. package/dist/modules/commands/components/click-command.client.d.mts.map +1 -1
  129. package/dist/modules/commands/components/command-trigger.client.d.mts +6 -6
  130. package/dist/modules/commands/components/command-trigger.client.d.mts.map +1 -1
  131. package/dist/modules/commands/components/dialog-command.client.d.mts +8 -8
  132. package/dist/modules/commands/components/dialog-command.client.d.mts.map +1 -1
  133. package/dist/modules/commands/components/dropdown-command.client.d.mts +5 -5
  134. package/dist/modules/commands/components/dropdown-command.client.d.mts.map +1 -1
  135. package/dist/modules/commands/components/empty-command.client.d.mts +2 -2
  136. package/dist/modules/commands/components/empty-command.client.d.mts.map +1 -1
  137. package/dist/modules/commands/components/form-dialog-command.client.d.mts +11 -11
  138. package/dist/modules/commands/components/form-dialog-command.client.d.mts.map +1 -1
  139. package/dist/modules/commands/hooks/use-command-mutation.client.d.mts +2 -2
  140. package/dist/modules/commands/menus/context-menu.client.d.mts +2 -2
  141. package/dist/modules/commands/menus/context-menu.client.d.mts.map +1 -1
  142. package/dist/modules/commands/menus/dropdown-menu.client.d.mts +3 -3
  143. package/dist/modules/commands/menus/dropdown-menu.client.d.mts.map +1 -1
  144. package/dist/modules/commands/menus/inline-menu.client.d.mts +3 -3
  145. package/dist/modules/commands/menus/inline-menu.client.d.mts.map +1 -1
  146. package/dist/modules/commands/menus/responsive-menu.client.d.mts +3 -3
  147. package/dist/modules/commands/menus/responsive-menu.client.d.mts.map +1 -1
  148. package/dist/modules/commands/utils/archive-command.client.d.mts +3 -3
  149. package/dist/modules/commands/utils/archive-command.client.d.mts.map +1 -1
  150. package/dist/modules/commands/utils/delete-command.client.d.mts +3 -3
  151. package/dist/modules/commands/utils/delete-command.client.d.mts.map +1 -1
  152. package/dist/modules/config/db/helpers.d.mts +5 -5
  153. package/dist/modules/data-tables/components/cell/common.client.d.mts +5 -5
  154. package/dist/modules/data-tables/components/cell/common.client.d.mts.map +1 -1
  155. package/dist/modules/data-tables/components/column-header.d.mts +2 -2
  156. package/dist/modules/data-tables/components/filters/combobox.client.d.mts +2 -2
  157. package/dist/modules/data-tables/components/filters/combobox.client.d.mts.map +1 -1
  158. package/dist/modules/data-tables/components/filters/slider.client.d.mts +2 -2
  159. package/dist/modules/data-tables/components/filters/slider.client.d.mts.map +1 -1
  160. package/dist/modules/data-tables/components/header.d.mts +4 -4
  161. package/dist/modules/data-tables/components/layout.d.mts +2 -2
  162. package/dist/modules/data-tables/components/search-input.client.d.mts +2 -2
  163. package/dist/modules/data-tables/components/search-input.client.d.mts.map +1 -1
  164. package/dist/modules/data-tables/components/skeleton.d.mts +2 -2
  165. package/dist/modules/data-tables/components/table.d.mts +7 -7
  166. package/dist/modules/data-tables/components/toolbar.d.mts +3 -3
  167. package/dist/modules/data-tables/hooks/use-context.client.d.mts +2 -2
  168. package/dist/modules/data-tables/hooks/use-context.client.d.mts.map +1 -1
  169. package/dist/modules/data-tables/tables/data-table/components/table.d.mts +2 -2
  170. package/dist/modules/data-tables/tables/data-table/components/table.d.mts.map +1 -1
  171. package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts +2 -2
  172. package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts.map +1 -1
  173. package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts +2 -2
  174. package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts.map +1 -1
  175. package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts +2 -2
  176. package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts.map +1 -1
  177. package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts +2 -2
  178. package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts.map +1 -1
  179. package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts +3 -3
  180. package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts.map +1 -1
  181. package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts +2 -2
  182. package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts.map +1 -1
  183. package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts +2 -2
  184. package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts.map +1 -1
  185. package/dist/modules/data-tables/tables/inline-table/components/table.d.mts +2 -2
  186. package/dist/modules/data-tables/tables/inline-table/components/table.d.mts.map +1 -1
  187. package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts +2 -2
  188. package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts.map +1 -1
  189. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts +2 -2
  190. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts.map +1 -1
  191. package/dist/modules/inline-edit/components/combobox.client.d.mts +2 -2
  192. package/dist/modules/inline-edit/components/combobox.client.d.mts.map +1 -1
  193. package/dist/modules/inline-edit/components/date-input.client.d.mts +2 -2
  194. package/dist/modules/inline-edit/components/date-input.client.d.mts.map +1 -1
  195. package/dist/modules/inline-edit/components/date-picker.client.d.mts +2 -2
  196. package/dist/modules/inline-edit/components/date-picker.client.d.mts.map +1 -1
  197. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts +2 -2
  198. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts.map +1 -1
  199. package/dist/modules/inline-edit/components/editor.client.d.mts +2 -2
  200. package/dist/modules/inline-edit/components/editor.client.d.mts.map +1 -1
  201. package/dist/modules/inline-edit/components/input-recipient.client.d.mts +2 -2
  202. package/dist/modules/inline-edit/components/input-recipient.client.d.mts.map +1 -1
  203. package/dist/modules/inline-edit/components/input-toggle.client.d.mts +2 -2
  204. package/dist/modules/inline-edit/components/input-toggle.client.d.mts.map +1 -1
  205. package/dist/modules/inline-edit/components/input.client.d.mts +4 -4
  206. package/dist/modules/inline-edit/components/input.client.d.mts.map +1 -1
  207. package/dist/modules/inline-edit/components/select.client.d.mts +6 -6
  208. package/dist/modules/inline-edit/components/select.client.d.mts.map +1 -1
  209. package/dist/modules/inline-edit/components/switch.client.d.mts +2 -2
  210. package/dist/modules/inline-edit/components/switch.client.d.mts.map +1 -1
  211. package/dist/modules/inline-edit/components/toggle.client.d.mts +2 -2
  212. package/dist/modules/inline-edit/components/toggle.client.d.mts.map +1 -1
  213. package/dist/modules/inline-edit/hooks/context.client.d.mts +2 -2
  214. package/dist/modules/inline-edit/hooks/context.client.d.mts.map +1 -1
  215. package/dist/modules/inline-edit/lib/variants.d.mts +1 -1
  216. package/dist/modules/router/lib/query-client.server.d.mts +2 -2
  217. package/dist/modules/router/lib/query-client.server.d.mts.map +1 -1
  218. package/dist/modules/storage/components/dropzone.client.d.mts +5 -5
  219. package/dist/modules/storage/components/dropzone.client.d.mts.map +1 -1
  220. package/dist/modules/storage/components/image-grid.client.d.mts +3 -3
  221. package/dist/modules/storage/components/image-grid.client.d.mts.map +1 -1
  222. package/dist/modules/storage/components/upload-zone.client.d.mts +2 -2
  223. package/dist/modules/storage/components/upload-zone.client.d.mts.map +1 -1
  224. package/dist/modules/storage/lib/router.server.d.mts +1768 -1768
  225. package/dist/modules/storage/lib/router.server.d.mts.map +1 -1
  226. package/dist/modules/storage/lib/router.server.mjs +1 -0
  227. package/dist/modules/storage/lib/router.server.mjs.map +1 -1
  228. package/dist/modules/storage/lib/schema.d.mts +88 -88
  229. package/dist/modules/storage/lib/service.server.d.mts +21 -21
  230. package/dist/modules/storage/lib/service.server.d.mts.map +1 -1
  231. package/dist/modules/storage/lib/service.server.mjs +6 -5
  232. package/dist/modules/storage/lib/service.server.mjs.map +1 -1
  233. package/dist/modules/storage/lib/validators.d.mts +78 -78
  234. package/dist/modules/storage/lib/validators.d.mts.map +1 -1
  235. package/package.json +1 -1
  236. package/src/components/editor/components/{block-dropdown.tsx → menu-nodes.client.tsx} +24 -29
  237. package/src/components/editor/components/menu.client.tsx +214 -0
  238. package/src/components/editor/lib/extensions.ts +3 -3
  239. package/src/components/entry.client.ts +1 -1
  240. package/src/modules/storage/lib/router.server.ts +6 -0
  241. package/src/modules/storage/lib/service.server.ts +15 -2
  242. package/dist/components/editor/components/block-dropdown.mjs.map +0 -1
  243. package/dist/components/editor/components/menu-fixed.client.d.mts +0 -7
  244. package/dist/components/editor/components/menu-fixed.client.d.mts.map +0 -1
  245. package/dist/components/editor/components/menu-fixed.client.mjs +0 -128
  246. package/dist/components/editor/components/menu-fixed.client.mjs.map +0 -1
  247. package/src/components/editor/components/menu-fixed.client.tsx +0 -165
@@ -67,19 +67,19 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
67
67
  * Upload file to S3 and add it to the database
68
68
  **/
69
69
  uploadFile(input: UploadFileSchema & Pick<PutObjectInput, "body">): Promise<{
70
- isPending: boolean;
71
70
  id: string;
72
- readonly: boolean | null;
73
- type: "file" | "folder" | null;
71
+ createdAt: Date;
72
+ updatedAt: Date;
74
73
  name: string;
75
- namespace: string;
74
+ type: "file" | "folder" | null;
75
+ readonly: boolean | null;
76
76
  mode: "private" | "public" | null;
77
- subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
78
77
  size: number | null;
78
+ namespace: string;
79
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
79
80
  contentType: string | null;
80
- createdAt: Date;
81
- updatedAt: Date;
82
81
  createdBy: string | null;
82
+ isPending: boolean;
83
83
  isDeleted: boolean;
84
84
  parentId: string | null;
85
85
  }>;
@@ -90,19 +90,19 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
90
90
  id: string;
91
91
  presignedUrl: string;
92
92
  node: {
93
- isPending: boolean;
94
93
  id: string;
95
- readonly: boolean | null;
96
- type: "file" | "folder" | null;
94
+ createdAt: Date;
95
+ updatedAt: Date;
97
96
  name: string;
98
- namespace: string;
97
+ type: "file" | "folder" | null;
98
+ readonly: boolean | null;
99
99
  mode: "private" | "public" | null;
100
- subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
101
100
  size: number | null;
101
+ namespace: string;
102
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
102
103
  contentType: string | null;
103
- createdAt: Date;
104
- updatedAt: Date;
105
104
  createdBy: string | null;
105
+ isPending: boolean;
106
106
  isDeleted: boolean;
107
107
  parentId: string | null;
108
108
  };
@@ -139,19 +139,19 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
139
139
  * Create a new folder
140
140
  */
141
141
  createFolder(input: CreateFolderNodeSchema): Promise<{
142
- isPending: boolean;
143
142
  id: string;
144
- readonly: boolean | null;
145
- type: "file" | "folder" | null;
143
+ createdAt: Date;
144
+ updatedAt: Date;
146
145
  name: string;
147
- namespace: string;
146
+ type: "file" | "folder" | null;
147
+ readonly: boolean | null;
148
148
  mode: "private" | "public" | null;
149
- subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
150
149
  size: number | null;
150
+ namespace: string;
151
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
151
152
  contentType: string | null;
152
- createdAt: Date;
153
- updatedAt: Date;
154
153
  createdBy: string | null;
154
+ isPending: boolean;
155
155
  isDeleted: boolean;
156
156
  parentId: string | null;
157
157
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"service.server.d.mts","names":[],"sources":["../../../../src/modules/storage/lib/service.server.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;KAyCY,qCAAqC;MAC3C,eAAe;UACX;AAFV,CAAA;;;;AAEU,cAMG,cANH,CAAA,gBAMkC,eANlC,CAAA,CAAA;EAAc,CAAA,OAAA;EAMX;;;EAUO,WAAA,CAAA;IAAA,EAAA;IAAA;EAAA,CAAA,EAAU,oBAAV,CAA+B,OAA/B,CAAA;EAA+B;;;EA4BZ,IAAA,CAAA,CAAA,EApBjC,QAoBiC;EAA2C;;;EAoChC,SAAA,CAAA,EAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EApCX,gBAoCW,CAAA,EApCgC,OAoChC,CApCgC,mBAAA,CAAA,sBAAA,CAoChC;;;;EAuBvB,kBAAA,CAAA;IAAA,OAAA;IAAA,GAAA;EAAA,CAAA,EAvBuB,uBAuBvB,CAAA,EAvB8C,OAuB9C,CAAA;IAAe,IAAA,EAAA,MAAA;IAA2C,SAAA,EAAA,MAAA;IAmE3D,IAAA,EAAA,MAAA,GAAA,QAAA,GAAA,IAAA;IAAwB,IAAA,EAAA,SAAA,GAAA,QAAA,GAAA,IAAA;IAAL,OAAA,EAAA,OAAA,GAAA,UAAA,GAAA,aAAA,GAAA,OAAA,GAAA,OAAA,GAAA,SAAA,GAAA,OAAA;;;IAA4B,QAAA,EAAA,OAAA,GAAA,IAAA;IAyC5C,SAAA,EAAA,MAAA,GAAA,IAAA;;;IAAiB,QAAA,EAAA,MAAA,GAAA,IAAA;;;IAqCH,SAAA,MAAA;EAuBG,CAAA,EAAA,CAAA;EAwDlB;;;EAAsB,YAAA,CAAA,IAAA,EAhOvB,IAgOuB,EAAA,OAAA,CAAA,EAhOR,gBAgOQ,CAAA,EAhOmC,OAgOnC,CAAA,MAAA,CAAA;EA0CJ;;;EAAkB,UAAA,CAAA,KAAA,EAvMtC,gBAuMsC,GAvMnB,IAuMmB,CAvMd,cAuMc,EAAA,MAAA,CAAA,CAAA,EAvMS,OAuMT,CAAA;IAmBrC,SAAA,EAAA,OAAA;IAAgB,EAAA,EAAA,MAAA;IAAA,QAAA,EAAA,OAAA,GAAA,IAAA;;;;;;;;;;;;;;;;;uBAjLd,oBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;MAqCH;;;;;;;;;;;;;;;;;;;;;;MAuBG;;;;sBAwDlB,yBAAsB;;;;;;;;;;;;;;;;;;;;;;UA0CJ;MAAkB;;;;;;;;;;;;;;;;;;;;qBAmBrC,mBAAgB"}
1
+ {"version":3,"file":"service.server.d.mts","names":[],"sources":["../../../../src/modules/storage/lib/service.server.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;KAyCY,qCAAqC;MAC3C,eAAe;UACX;AAFV,CAAA;;;;AAEU,cAMG,cANH,CAAA,gBAMkC,eANlC,CAAA,CAAA;EAAc,CAAA,OAAA;EAMX;;;EAUO,WAAA,CAAA;IAAA,EAAA;IAAA;EAAA,CAAA,EAAU,oBAAV,CAA+B,OAA/B,CAAA;EAA+B;;;EA4BZ,IAAA,CAAA,CAAA,EApBjC,QAoBiC;EAA2C;;;EAmChC,SAAA,CAAA,EAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAnCX,gBAmCW,CAAA,EAnCgC,OAmChC,CAnCgC,mBAAA,CAAA,sBAAA,CAmChC;;;;EAuBvB,kBAAA,CAAA;IAAA,OAAA;IAAA,GAAA;EAAA,CAAA,EAvBuB,uBAuBvB,CAAA,EAvB8C,OAuB9C,CAAA;IAAe,IAAA,EAAA,MAAA;IAA2C,SAAA,EAAA,MAAA;IAmE3D,IAAA,EAAA,MAAA,GAAA,QAAA,GAAA,IAAA;IAAwB,IAAA,EAAA,SAAA,GAAA,QAAA,GAAA,IAAA;IAAL,OAAA,EAAA,OAAA,GAAA,UAAA,GAAA,aAAA,GAAA,OAAA,GAAA,OAAA,GAAA,SAAA,GAAA,OAAA;;;IAA4B,QAAA,EAAA,OAAA,GAAA,IAAA;IAyC5C,SAAA,EAAA,MAAA,GAAA,IAAA;;;IAAiB,QAAA,EAAA,MAAA,GAAA,IAAA;;;IAqCH,SAAA,MAAA;EAuBG,CAAA,EAAA,CAAA;EAwDlB;;;EAAsB,YAAA,CAAA,IAAA,EAhOvB,IAgOuB,EAAA,OAAA,CAAA,EAhOR,gBAgOQ,CAAA,EAhOmC,OAgOnC,CAAA,MAAA,CAAA;EA0CJ;;;EAAkB,UAAA,CAAA,KAAA,EAvMtC,gBAuMsC,GAvMnB,IAuMmB,CAvMd,cAuMc,EAAA,MAAA,CAAA,CAAA,EAvMS,OAuMT,CAAA;IA4BrC,EAAA,EAAA,MAAA;IAAgB,SAAA,MAAA;IAAA,SAAA,MAAA;;;;;;;;;;;;;;;;;uBA1Ld,oBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;MAqCH;;;;;;;;;;;;;;;;;;;;;;MAuBG;;;;sBAwDlB,yBAAsB;;;;;;;;;;;;;;;;;;;;;;UA0CJ;MAAkB;;;;;;;;;;;;;;;;;;;;qBA4BrC,mBAAgB"}
@@ -68,10 +68,7 @@ var StorageService = class {
68
68
  Body: input.body,
69
69
  ContentType: input.contentType,
70
70
  ContentLength: input.size,
71
- Metadata: {
72
- nodeId: input.id,
73
- filename: input.name
74
- }
71
+ Metadata: { nodeId: input.id }
75
72
  });
76
73
  }
77
74
  /**
@@ -263,6 +260,8 @@ var StorageService = class {
263
260
  * Update a node
264
261
  */
265
262
  async updateNode(input) {
263
+ const [node] = await this.#db.select({ readonly: nodes.readonly }).from(nodes).where(eq(nodes.id, input.id));
264
+ if (node?.readonly) throw new ServerError("BAD_REQUEST", { message: "Node is readonly" });
266
265
  const [result] = await this.#db.update(nodes).set(input.data).where(eq(nodes.id, input.id)).returning();
267
266
  if (!result) throw new ServerError("INTERNAL_SERVER_ERROR", { message: "Node kon niet worden gewijzigd" });
268
267
  return result;
@@ -273,8 +272,10 @@ var StorageService = class {
273
272
  async deleteNodes(input) {
274
273
  const items = await this.#db.select({
275
274
  id: nodes.id,
276
- type: nodes.type
275
+ type: nodes.type,
276
+ readonly: nodes.readonly
277
277
  }).from(nodes).where(inArray(nodes.id, input.ids));
278
+ if (items.some((item) => item.readonly)) throw new ServerError("BAD_REQUEST", { message: "Nodes are readonly" });
278
279
  const folders = items.filter(isFolder).map((folder) => folder.id);
279
280
  const files = items.filter(isFile).map((file) => file.id);
280
281
  const deleteCommand = files.length > 0 ? new DeleteObjectsCommand({
@@ -1 +1 @@
1
- {"version":3,"file":"service.server.mjs","names":["#db","#blob","#createGetCommand","#putObject","#createPutCommand"],"sources":["../../../../src/modules/storage/lib/service.server.ts"],"sourcesContent":["import { BUCKET_NAME } from \"@/lib/config/constants\";\nimport { generateDefaultUUID, TDatabaseSchema } from \"@/modules/config/entry\";\nimport { DatabaseClient } from \"@/modules/config/entry.server\";\nimport {\n convertOrderByToQueryParams,\n convertSearchToQueryParams,\n} from \"@/modules/data-tables/entry.server\";\nimport { BulkActionSchema } from \"@/modules/router/entry\";\nimport { ServerError } from \"@/modules/router/lib/error.server\";\nimport {\n DeleteObjectsCommand,\n GetObjectCommand,\n PutObjectCommand,\n S3Client,\n S3ClientConfig,\n} from \"@aws-sdk/client-s3\";\nimport { getSignedUrl } from \"@aws-sdk/s3-request-presigner\";\nimport { addSeconds } from \"date-fns\";\nimport { and, asc, eq, inArray, isNull, SQL } from \"drizzle-orm\";\nimport { after } from \"next/server\";\nimport { deviceSizes } from \"./constants\";\nimport { getDriveBucketKey, inferNodeSubtype, isFile, isFolder } from \"./helpers\";\nimport { nodePresignedUrls, nodes, nodeVariants } from \"./schema\";\nimport {\n CreateFolderNodeSchema,\n GetFileURLSchema,\n getFileURLSchemaDefaults,\n GetNodesByParentIdInput,\n GetObjectInput,\n getObjectSchema,\n Node,\n PresignFileSchema,\n PutObjectInput,\n putObjectSchema,\n UpdateNodeSchema,\n UploadFileSchema,\n} from \"./validators\";\n\n/**\n * Storage Service Config\n */\nexport type StorageServiceConfig<TSchema extends TDatabaseSchema> = {\n db: DatabaseClient<TSchema>;\n config: S3ClientConfig;\n};\n\n/**\n * Storage Service\n */\nexport class StorageService<TSchema extends TDatabaseSchema> {\n /**\n * S3 Client\n */\n #blob: S3Client;\n #db: DatabaseClient<TSchema>;\n\n /**\n * Constructor\n */\n constructor({ db, config }: StorageServiceConfig<TSchema>) {\n this.#db = db;\n this.#blob = new S3Client(config);\n }\n\n /**\n * Get Blob\n */\n blob() {\n return this.#blob;\n }\n\n /**\n * Create get command\n */\n #createGetCommand(props: GetObjectInput) {\n const input = getObjectSchema.parse(props);\n\n return new GetObjectCommand({\n Bucket: BUCKET_NAME,\n Key: getDriveBucketKey(input.id, input.variant),\n ResponseContentDisposition: input.disposition,\n });\n }\n\n /**\n * Get object\n */\n async getObject(id: string, options: GetFileURLSchema = getFileURLSchemaDefaults) {\n const getCommand = this.#createGetCommand({ ...options, id });\n\n return await this.#blob.send(getCommand);\n }\n\n /**\n * Create put command\n */\n #createPutCommand(props: PutObjectInput) {\n const input = putObjectSchema.parse(props);\n\n return new PutObjectCommand({\n Bucket: BUCKET_NAME,\n Key: getDriveBucketKey(input.id, input.variant),\n Body: input.body,\n ContentType: input.contentType,\n ContentLength: input.size,\n Metadata: {\n nodeId: input.id,\n filename: input.name,\n },\n });\n }\n\n /**\n * Put object\n */\n async #putObject(props: PutObjectInput) {\n const putCommand = this.#createPutCommand(props);\n return await this.#blob.send(putCommand);\n }\n\n /**\n * Get nodes by parent id\n */\n async getNodesByParentId({ filters, ...query }: GetNodesByParentIdInput) {\n const orderBy = convertOrderByToQueryParams(query, nodes, asc(nodes.createdAt));\n const search = convertSearchToQueryParams(query, [nodes.name]);\n\n return this.#db\n .select()\n .from(nodes)\n .where(\n and(\n filters.nodeIds != null ? inArray(nodes.id, filters.nodeIds) : undefined,\n filters.types != null ? inArray(nodes.type, filters.types) : undefined,\n filters.isDeleted != null ? eq(nodes.isDeleted, filters.isDeleted) : undefined,\n filters.parentId ? eq(nodes.parentId, filters.parentId) : isNull(nodes.parentId),\n eq(nodes.namespace, filters.namespace),\n search,\n ),\n )\n .orderBy(orderBy as SQL);\n }\n\n /**\n * Get file url\n */\n async getSignedURL(node: Node, options: GetFileURLSchema = getFileURLSchemaDefaults) {\n const [presignedUrl] = await this.#db\n .select({ url: nodePresignedUrls.url, expiresAt: nodePresignedUrls.expiresAt })\n .from(nodePresignedUrls)\n .where(\n and(\n eq(nodePresignedUrls.nodeId, node.id),\n eq(nodePresignedUrls.variant, options.variant),\n eq(nodePresignedUrls.disposition, options.disposition),\n ),\n );\n\n if (presignedUrl && presignedUrl.expiresAt > new Date()) return presignedUrl.url;\n\n const expiresIn = 3600 * 24;\n\n // Get the variants\n const variants = await this.#db\n .select()\n .from(nodeVariants)\n .where(eq(nodeVariants.nodeId, node.id));\n\n // If the requested variant does not exist, fallback to main\n const variantExists = variants.find((v) => v.variant === options.variant);\n const variant = variantExists ? options.variant : \"main\";\n\n console.info(\n `Generating new signed url for file: ${node.id} with variant: ${variant} and disposition: ${options.disposition}`,\n );\n\n // Generate the get command\n const getCommand = this.#createGetCommand({\n id: node.id,\n variant,\n disposition: `${options.disposition}; filename=\"${node.name}\"`,\n });\n\n // Generate the presigned url that expires in 24 hours\n const url = await getSignedUrl(this.#blob, getCommand, { expiresIn });\n\n // Add the presigned url to the database\n after(async () => {\n await this.#db\n .insert(nodePresignedUrls)\n .values({\n nodeId: node.id,\n url,\n variant,\n disposition: options.disposition,\n expiresAt: addSeconds(new Date(), expiresIn),\n })\n .onConflictDoUpdate({\n target: [\n nodePresignedUrls.nodeId,\n nodePresignedUrls.variant,\n nodePresignedUrls.disposition,\n ],\n set: { url, expiresAt: addSeconds(new Date(), expiresIn) },\n });\n });\n\n return url;\n }\n\n /**\n * Upload file to S3 and add it to the database\n **/\n async uploadFile(input: UploadFileSchema & Pick<PutObjectInput, \"body\">) {\n const id = generateDefaultUUID();\n\n return await this.#db.transaction(async (tx) => {\n const [result] = await tx\n .insert(nodes)\n .values({\n id,\n type: \"file\",\n name: input.name,\n namespace: input.namespace,\n parentId: input.parentId,\n size: input.size,\n contentType: input.contentType,\n mode: input.mode,\n subtype: inferNodeSubtype(input),\n })\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n await this.#putObject({\n id,\n body: input.body,\n variant: \"main\",\n name: input.name,\n contentType: input.contentType,\n size: input.size,\n });\n\n return result;\n });\n }\n\n /**\n * Presign a new upload\n */\n async presignUpload(input: PresignFileSchema) {\n // Generate the put command\n const putCommand = this.#createPutCommand({\n id: input.id,\n name: input.name,\n variant: \"main\",\n contentType: input.contentType,\n size: input.size,\n });\n\n // Generate the presigned url\n const presignedUrl = await getSignedUrl(this.#blob, putCommand, { expiresIn: 3600 });\n\n const [node] = await this.#db\n .insert(nodes)\n .values({\n ...input,\n subtype: inferNodeSubtype(input),\n isPending: true,\n type: \"file\",\n id: input.id,\n })\n .returning();\n\n if (!node) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n // Return the result\n return { id: input.id, presignedUrl, node };\n }\n\n /**\n * Confirm a new upload\n */\n async confirmUpload(input: { id: string }) {\n const [result] = await this.#db\n .update(nodes)\n .set({ isPending: false })\n .where(eq(nodes.id, input.id))\n .returning();\n\n if (!result) {\n throw new ServerError(\"NOT_FOUND\", { message: \"File not found\" });\n }\n\n /**\n * Generate the preview version of the file\n */\n // after(async () => {});\n await this.generatePreviews(input);\n\n return result;\n }\n\n /**\n * Generate preview version of the file\n */\n async generatePreviews(input: { id: string }) {\n /**\n * Get the main version of the file\n */\n const getCommand = this.#createGetCommand({ id: input.id, variant: \"main\" });\n\n const response = await this.#blob.send(getCommand);\n const contentType = response.ContentType;\n if (!response.Body) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n /**\n * Transform the main version of the file to a buffer\n */\n const byteArray = await response.Body.transformToByteArray();\n const buffer = Buffer.from(byteArray);\n\n /**\n * Generate the preview versions for images\n */\n if (contentType?.startsWith(\"image/\")) {\n const sharp = await import(\"sharp\");\n\n // Generate the preview versions\n await Promise.allSettled(\n deviceSizes.flatMap(async (width) => {\n // Generate the preview\n const preview = await sharp.default(buffer).resize({ width }).webp().toBuffer();\n\n // Upload the preview and add the variant to the database\n return this.#db.transaction(async (tx) => {\n await this.#putObject({\n id: input.id,\n body: preview,\n variant: `preview-${width}`,\n contentType: \"image/webp\",\n size: preview.byteLength,\n });\n\n await tx.insert(nodeVariants).values({\n nodeId: input.id,\n variant: `preview-${width}`,\n width,\n });\n });\n }),\n );\n }\n }\n\n /**\n * Create a new folder\n */\n async createFolder(input: CreateFolderNodeSchema) {\n const [parent] = input.parentId\n ? await this.#db.select().from(nodes).where(eq(nodes.id, input.parentId))\n : [];\n\n /**\n * Validate\n */\n if (input.parentId && !parent) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Parent not found\" });\n }\n\n if (parent && !isFolder(parent)) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Parent is not a folder\" });\n }\n\n if (parent && parent.namespace !== input.namespace) {\n throw new ServerError(\"BAD_REQUEST\", {\n message: \"Parent is not in the same namespace\",\n });\n }\n\n /**\n * Create the folder\n */\n const [result] = await this.#db\n .insert(nodes)\n .values({ ...input, type: \"folder\" })\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Folder kon niet worden aangemaakt\",\n });\n }\n\n return result;\n }\n\n /**\n * Update a node\n */\n async updateNode(input: { id: string; data: UpdateNodeSchema }) {\n const [result] = await this.#db\n .update(nodes)\n .set(input.data)\n .where(eq(nodes.id, input.id))\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Node kon niet worden gewijzigd\",\n });\n }\n\n return result;\n }\n\n /**\n * Delete nodes\n */\n async deleteNodes(input: BulkActionSchema) {\n const items = await this.#db\n .select({ id: nodes.id, type: nodes.type })\n .from(nodes)\n .where(inArray(nodes.id, input.ids));\n\n // Split the nodes into folders and files\n const folders = items.filter(isFolder).map((folder) => folder.id);\n const files = items.filter(isFile).map((file) => file.id);\n\n // Delete command for S3\n const deleteCommand =\n files.length > 0\n ? new DeleteObjectsCommand({\n Bucket: BUCKET_NAME,\n Delete: {\n Objects: files.map((id) => ({ Key: id })),\n },\n })\n : undefined;\n\n /**\n * Delete files and folders in a transaction\n */\n await this.#db.transaction(async (tx) => {\n await tx.delete(nodes).where(inArray(nodes.id, folders));\n await tx.delete(nodes).where(inArray(nodes.id, files));\n\n if (deleteCommand) await this.#blob.send(deleteCommand);\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.server.mjs","names":["#db","#blob","#createGetCommand","#putObject","#createPutCommand"],"sources":["../../../../src/modules/storage/lib/service.server.ts"],"sourcesContent":["import { BUCKET_NAME } from \"@/lib/config/constants\";\nimport { generateDefaultUUID, TDatabaseSchema } from \"@/modules/config/entry\";\nimport { DatabaseClient } from \"@/modules/config/entry.server\";\nimport {\n convertOrderByToQueryParams,\n convertSearchToQueryParams,\n} from \"@/modules/data-tables/entry.server\";\nimport { BulkActionSchema } from \"@/modules/router/entry\";\nimport { ServerError } from \"@/modules/router/lib/error.server\";\nimport {\n DeleteObjectsCommand,\n GetObjectCommand,\n PutObjectCommand,\n S3Client,\n S3ClientConfig,\n} from \"@aws-sdk/client-s3\";\nimport { getSignedUrl } from \"@aws-sdk/s3-request-presigner\";\nimport { addSeconds } from \"date-fns\";\nimport { and, asc, eq, inArray, isNull, SQL } from \"drizzle-orm\";\nimport { after } from \"next/server\";\nimport { deviceSizes } from \"./constants\";\nimport { getDriveBucketKey, inferNodeSubtype, isFile, isFolder } from \"./helpers\";\nimport { nodePresignedUrls, nodes, nodeVariants } from \"./schema\";\nimport {\n CreateFolderNodeSchema,\n GetFileURLSchema,\n getFileURLSchemaDefaults,\n GetNodesByParentIdInput,\n GetObjectInput,\n getObjectSchema,\n Node,\n PresignFileSchema,\n PutObjectInput,\n putObjectSchema,\n UpdateNodeSchema,\n UploadFileSchema,\n} from \"./validators\";\n\n/**\n * Storage Service Config\n */\nexport type StorageServiceConfig<TSchema extends TDatabaseSchema> = {\n db: DatabaseClient<TSchema>;\n config: S3ClientConfig;\n};\n\n/**\n * Storage Service\n */\nexport class StorageService<TSchema extends TDatabaseSchema> {\n /**\n * S3 Client\n */\n #blob: S3Client;\n #db: DatabaseClient<TSchema>;\n\n /**\n * Constructor\n */\n constructor({ db, config }: StorageServiceConfig<TSchema>) {\n this.#db = db;\n this.#blob = new S3Client(config);\n }\n\n /**\n * Get Blob\n */\n blob() {\n return this.#blob;\n }\n\n /**\n * Create get command\n */\n #createGetCommand(props: GetObjectInput) {\n const input = getObjectSchema.parse(props);\n\n return new GetObjectCommand({\n Bucket: BUCKET_NAME,\n Key: getDriveBucketKey(input.id, input.variant),\n ResponseContentDisposition: input.disposition,\n });\n }\n\n /**\n * Get object\n */\n async getObject(id: string, options: GetFileURLSchema = getFileURLSchemaDefaults) {\n const getCommand = this.#createGetCommand({ ...options, id });\n\n return await this.#blob.send(getCommand);\n }\n\n /**\n * Create put command\n */\n #createPutCommand(props: PutObjectInput) {\n const input = putObjectSchema.parse(props);\n\n return new PutObjectCommand({\n Bucket: BUCKET_NAME,\n Key: getDriveBucketKey(input.id, input.variant),\n Body: input.body,\n ContentType: input.contentType,\n ContentLength: input.size,\n Metadata: {\n nodeId: input.id,\n },\n });\n }\n\n /**\n * Put object\n */\n async #putObject(props: PutObjectInput) {\n const putCommand = this.#createPutCommand(props);\n return await this.#blob.send(putCommand);\n }\n\n /**\n * Get nodes by parent id\n */\n async getNodesByParentId({ filters, ...query }: GetNodesByParentIdInput) {\n const orderBy = convertOrderByToQueryParams(query, nodes, asc(nodes.createdAt));\n const search = convertSearchToQueryParams(query, [nodes.name]);\n\n return this.#db\n .select()\n .from(nodes)\n .where(\n and(\n filters.nodeIds != null ? inArray(nodes.id, filters.nodeIds) : undefined,\n filters.types != null ? inArray(nodes.type, filters.types) : undefined,\n filters.isDeleted != null ? eq(nodes.isDeleted, filters.isDeleted) : undefined,\n filters.parentId ? eq(nodes.parentId, filters.parentId) : isNull(nodes.parentId),\n eq(nodes.namespace, filters.namespace),\n search,\n ),\n )\n .orderBy(orderBy as SQL);\n }\n\n /**\n * Get file url\n */\n async getSignedURL(node: Node, options: GetFileURLSchema = getFileURLSchemaDefaults) {\n const [presignedUrl] = await this.#db\n .select({ url: nodePresignedUrls.url, expiresAt: nodePresignedUrls.expiresAt })\n .from(nodePresignedUrls)\n .where(\n and(\n eq(nodePresignedUrls.nodeId, node.id),\n eq(nodePresignedUrls.variant, options.variant),\n eq(nodePresignedUrls.disposition, options.disposition),\n ),\n );\n\n if (presignedUrl && presignedUrl.expiresAt > new Date()) return presignedUrl.url;\n\n const expiresIn = 3600 * 24;\n\n // Get the variants\n const variants = await this.#db\n .select()\n .from(nodeVariants)\n .where(eq(nodeVariants.nodeId, node.id));\n\n // If the requested variant does not exist, fallback to main\n const variantExists = variants.find((v) => v.variant === options.variant);\n const variant = variantExists ? options.variant : \"main\";\n\n console.info(\n `Generating new signed url for file: ${node.id} with variant: ${variant} and disposition: ${options.disposition}`,\n );\n\n // Generate the get command\n const getCommand = this.#createGetCommand({\n id: node.id,\n variant,\n disposition: `${options.disposition}; filename=\"${node.name}\"`,\n });\n\n // Generate the presigned url that expires in 24 hours\n const url = await getSignedUrl(this.#blob, getCommand, { expiresIn });\n\n // Add the presigned url to the database\n after(async () => {\n await this.#db\n .insert(nodePresignedUrls)\n .values({\n nodeId: node.id,\n url,\n variant,\n disposition: options.disposition,\n expiresAt: addSeconds(new Date(), expiresIn),\n })\n .onConflictDoUpdate({\n target: [\n nodePresignedUrls.nodeId,\n nodePresignedUrls.variant,\n nodePresignedUrls.disposition,\n ],\n set: { url, expiresAt: addSeconds(new Date(), expiresIn) },\n });\n });\n\n return url;\n }\n\n /**\n * Upload file to S3 and add it to the database\n **/\n async uploadFile(input: UploadFileSchema & Pick<PutObjectInput, \"body\">) {\n const id = generateDefaultUUID();\n\n return await this.#db.transaction(async (tx) => {\n const [result] = await tx\n .insert(nodes)\n .values({\n id,\n type: \"file\",\n name: input.name,\n namespace: input.namespace,\n parentId: input.parentId,\n size: input.size,\n contentType: input.contentType,\n mode: input.mode,\n subtype: inferNodeSubtype(input),\n })\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n await this.#putObject({\n id,\n body: input.body,\n variant: \"main\",\n name: input.name,\n contentType: input.contentType,\n size: input.size,\n });\n\n return result;\n });\n }\n\n /**\n * Presign a new upload\n */\n async presignUpload(input: PresignFileSchema) {\n // Generate the put command\n const putCommand = this.#createPutCommand({\n id: input.id,\n name: input.name,\n variant: \"main\",\n contentType: input.contentType,\n size: input.size,\n });\n\n // Generate the presigned url\n const presignedUrl = await getSignedUrl(this.#blob, putCommand, { expiresIn: 3600 });\n\n const [node] = await this.#db\n .insert(nodes)\n .values({\n ...input,\n subtype: inferNodeSubtype(input),\n isPending: true,\n type: \"file\",\n id: input.id,\n })\n .returning();\n\n if (!node) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n // Return the result\n return { id: input.id, presignedUrl, node };\n }\n\n /**\n * Confirm a new upload\n */\n async confirmUpload(input: { id: string }) {\n const [result] = await this.#db\n .update(nodes)\n .set({ isPending: false })\n .where(eq(nodes.id, input.id))\n .returning();\n\n if (!result) {\n throw new ServerError(\"NOT_FOUND\", { message: \"File not found\" });\n }\n\n /**\n * Generate the preview version of the file\n */\n // after(async () => {});\n await this.generatePreviews(input);\n\n return result;\n }\n\n /**\n * Generate preview version of the file\n */\n async generatePreviews(input: { id: string }) {\n /**\n * Get the main version of the file\n */\n const getCommand = this.#createGetCommand({ id: input.id, variant: \"main\" });\n\n const response = await this.#blob.send(getCommand);\n const contentType = response.ContentType;\n if (!response.Body) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n /**\n * Transform the main version of the file to a buffer\n */\n const byteArray = await response.Body.transformToByteArray();\n const buffer = Buffer.from(byteArray);\n\n /**\n * Generate the preview versions for images\n */\n if (contentType?.startsWith(\"image/\")) {\n const sharp = await import(\"sharp\");\n\n // Generate the preview versions\n await Promise.allSettled(\n deviceSizes.flatMap(async (width) => {\n // Generate the preview\n const preview = await sharp.default(buffer).resize({ width }).webp().toBuffer();\n\n // Upload the preview and add the variant to the database\n return this.#db.transaction(async (tx) => {\n await this.#putObject({\n id: input.id,\n body: preview,\n variant: `preview-${width}`,\n contentType: \"image/webp\",\n size: preview.byteLength,\n });\n\n await tx.insert(nodeVariants).values({\n nodeId: input.id,\n variant: `preview-${width}`,\n width,\n });\n });\n }),\n );\n }\n }\n\n /**\n * Create a new folder\n */\n async createFolder(input: CreateFolderNodeSchema) {\n const [parent] = input.parentId\n ? await this.#db.select().from(nodes).where(eq(nodes.id, input.parentId))\n : [];\n\n /**\n * Validate\n */\n if (input.parentId && !parent) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Parent not found\" });\n }\n\n if (parent && !isFolder(parent)) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Parent is not a folder\" });\n }\n\n if (parent && parent.namespace !== input.namespace) {\n throw new ServerError(\"BAD_REQUEST\", {\n message: \"Parent is not in the same namespace\",\n });\n }\n\n /**\n * Create the folder\n */\n const [result] = await this.#db\n .insert(nodes)\n .values({ ...input, type: \"folder\" })\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Folder kon niet worden aangemaakt\",\n });\n }\n\n return result;\n }\n\n /**\n * Update a node\n */\n async updateNode(input: { id: string; data: UpdateNodeSchema }) {\n const [node] = await this.#db\n .select({ readonly: nodes.readonly })\n .from(nodes)\n .where(eq(nodes.id, input.id));\n\n if (node?.readonly) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Node is readonly\" });\n }\n\n const [result] = await this.#db\n .update(nodes)\n .set(input.data)\n .where(eq(nodes.id, input.id))\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Node kon niet worden gewijzigd\",\n });\n }\n\n return result;\n }\n\n /**\n * Delete nodes\n */\n async deleteNodes(input: BulkActionSchema) {\n const items = await this.#db\n .select({ id: nodes.id, type: nodes.type, readonly: nodes.readonly })\n .from(nodes)\n .where(inArray(nodes.id, input.ids));\n\n // Check if the nodes are readonly\n if (items.some((item) => item.readonly)) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Nodes are readonly\" });\n }\n\n // Split the nodes into folders and files\n const folders = items.filter(isFolder).map((folder) => folder.id);\n const files = items.filter(isFile).map((file) => file.id);\n\n // Delete command for S3\n const deleteCommand =\n files.length > 0\n ? new DeleteObjectsCommand({\n Bucket: BUCKET_NAME,\n Delete: {\n Objects: files.map((id) => ({ Key: id })),\n },\n })\n : undefined;\n\n /**\n * Delete files and folders in a transaction\n */\n await this.#db.transaction(async (tx) => {\n await tx.delete(nodes).where(inArray(nodes.id, folders));\n await tx.delete(nodes).where(inArray(nodes.id, files));\n\n if (deleteCommand) await this.#blob.send(deleteCommand);\n });\n }\n}\n"],"mappings":"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"}