@tulip-systems/core 0.5.1 → 0.5.2

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 (236) hide show
  1. package/dist/components/common/icons.d.mts +52 -52
  2. package/dist/components/common/icons.d.mts.map +1 -1
  3. package/dist/components/common/status.d.mts +3 -3
  4. package/dist/components/common/status.d.mts.map +1 -1
  5. package/dist/components/editor/components/content.client.d.mts +2 -2
  6. package/dist/components/editor/components/editor.client.d.mts +4 -4
  7. package/dist/components/editor/components/menu.client.d.mts +3 -3
  8. package/dist/components/header/back-button.client.d.mts +2 -2
  9. package/dist/components/header/bottom-bar.client.d.mts +3 -3
  10. package/dist/components/header/breadcrumbs.client.d.mts +7 -7
  11. package/dist/components/header/header.client.d.mts +2 -2
  12. package/dist/components/header/mobile-nav-switcher.client.d.mts +2 -2
  13. package/dist/components/header/top-bar.client.d.mts +4 -4
  14. package/dist/components/layouts/admin-content.client.d.mts +2 -2
  15. package/dist/components/layouts/admin-layout.d.mts +2 -2
  16. package/dist/components/layouts/admin-layout.d.mts.map +1 -1
  17. package/dist/components/layouts/admin-loading.d.mts +2 -2
  18. package/dist/components/layouts/admin-loading.d.mts.map +1 -1
  19. package/dist/components/layouts/empty-page.d.mts +4 -4
  20. package/dist/components/layouts/empty-page.d.mts.map +1 -1
  21. package/dist/components/layouts/list-layout.d.mts +2 -2
  22. package/dist/components/layouts/list-layout.d.mts.map +1 -1
  23. package/dist/components/layouts/not-found-page.d.mts +3 -3
  24. package/dist/components/layouts/not-found-page.d.mts.map +1 -1
  25. package/dist/components/layouts/providers.client.d.mts +2 -2
  26. package/dist/components/layouts/root-layout.server.d.mts +2 -2
  27. package/dist/components/layouts/root-layout.server.d.mts.map +1 -1
  28. package/dist/components/layouts/root-loading.d.mts +2 -2
  29. package/dist/components/layouts/root-loading.d.mts.map +1 -1
  30. package/dist/components/layouts/tab-layout.d.mts +2 -2
  31. package/dist/components/layouts/tab-layout.d.mts.map +1 -1
  32. package/dist/components/lists/data-list.d.mts +5 -5
  33. package/dist/components/lists/data-list.d.mts.map +1 -1
  34. package/dist/components/lists/data-stack.d.mts +8 -8
  35. package/dist/components/lists/data-stack.d.mts.map +1 -1
  36. package/dist/components/navigation/admin-sidebar-paths.client.d.mts +10 -10
  37. package/dist/components/ui/accordion.d.mts +5 -5
  38. package/dist/components/ui/accordion.d.mts.map +1 -1
  39. package/dist/components/ui/alert-dialog.d.mts +12 -12
  40. package/dist/components/ui/alert-dialog.d.mts.map +1 -1
  41. package/dist/components/ui/alert.d.mts +6 -6
  42. package/dist/components/ui/alert.d.mts.map +1 -1
  43. package/dist/components/ui/aspect-ratio.d.mts +2 -2
  44. package/dist/components/ui/aspect-ratio.d.mts.map +1 -1
  45. package/dist/components/ui/avatar.client.d.mts +4 -4
  46. package/dist/components/ui/badge.d.mts +4 -4
  47. package/dist/components/ui/badge.d.mts.map +1 -1
  48. package/dist/components/ui/breadcrumb.d.mts +8 -8
  49. package/dist/components/ui/button.d.mts +5 -5
  50. package/dist/components/ui/calendar.d.mts +3 -3
  51. package/dist/components/ui/card.d.mts +7 -7
  52. package/dist/components/ui/carousel.d.mts +6 -6
  53. package/dist/components/ui/chart.client.d.mts +5 -5
  54. package/dist/components/ui/checkbox.d.mts +2 -2
  55. package/dist/components/ui/collapsible.client.d.mts +4 -4
  56. package/dist/components/ui/color-picker.client.d.mts +2 -2
  57. package/dist/components/ui/combobox-dropdown.client.d.mts +2 -2
  58. package/dist/components/ui/combobox.client.d.mts +2 -2
  59. package/dist/components/ui/command.d.mts +10 -10
  60. package/dist/components/ui/context-menu.d.mts +16 -16
  61. package/dist/components/ui/context-menu.d.mts.map +1 -1
  62. package/dist/components/ui/date-picker.client.d.mts +2 -2
  63. package/dist/components/ui/dialog.client.d.mts +11 -11
  64. package/dist/components/ui/drawer.client.d.mts +11 -11
  65. package/dist/components/ui/dropdown-menu.d.mts +16 -16
  66. package/dist/components/ui/form.client.d.mts +7 -7
  67. package/dist/components/ui/hover-card.client.d.mts +4 -4
  68. package/dist/components/ui/input.d.mts +2 -2
  69. package/dist/components/ui/label.d.mts +2 -2
  70. package/dist/components/ui/navigation-menu.d.mts +11 -11
  71. package/dist/components/ui/pagination.d.mts +8 -8
  72. package/dist/components/ui/popover.d.mts +5 -5
  73. package/dist/components/ui/progress.client.d.mts +2 -2
  74. package/dist/components/ui/radio-group.d.mts +3 -3
  75. package/dist/components/ui/resizable.client.d.mts +4 -4
  76. package/dist/components/ui/scroll-area.d.mts +3 -3
  77. package/dist/components/ui/select.client.d.mts +11 -11
  78. package/dist/components/ui/separator.d.mts +2 -2
  79. package/dist/components/ui/sheet.client.d.mts +9 -9
  80. package/dist/components/ui/sidebar.client.d.mts +24 -24
  81. package/dist/components/ui/skeleton.d.mts +2 -2
  82. package/dist/components/ui/slider.d.mts +2 -2
  83. package/dist/components/ui/sonner.client.d.mts +2 -2
  84. package/dist/components/ui/switch.d.mts +2 -2
  85. package/dist/components/ui/tabs.d.mts +5 -5
  86. package/dist/components/ui/textarea.d.mts +2 -2
  87. package/dist/components/ui/time-input.client.d.mts +2 -2
  88. package/dist/components/ui/toggle-group.client.d.mts +3 -3
  89. package/dist/components/ui/toggle.d.mts +2 -2
  90. package/dist/components/ui/tooltip.client.d.mts +5 -5
  91. package/dist/lib/hooks/use-indicator.d.mts +2 -2
  92. package/dist/lib/hooks/use-indicator.d.mts.map +1 -1
  93. package/dist/modules/auth/components/allowed.client.d.mts +2 -2
  94. package/dist/modules/auth/components/allowed.client.d.mts.map +1 -1
  95. package/dist/modules/auth/components/auth-layout.server.d.mts +2 -2
  96. package/dist/modules/auth/components/auth-loading.d.mts +2 -2
  97. package/dist/modules/auth/components/create-first-user-page.client.d.mts +2 -2
  98. package/dist/modules/auth/components/create-first-user-page.client.d.mts.map +1 -1
  99. package/dist/modules/auth/components/forget-password-page.client.d.mts +2 -2
  100. package/dist/modules/auth/components/forget-password-page.client.d.mts.map +1 -1
  101. package/dist/modules/auth/components/guard-first-user.server.d.mts +2 -2
  102. package/dist/modules/auth/components/guard.server.d.mts +2 -2
  103. package/dist/modules/auth/components/login-page.client.d.mts +2 -2
  104. package/dist/modules/auth/components/login-page.client.d.mts.map +1 -1
  105. package/dist/modules/auth/components/reset-password-page.client.d.mts +2 -2
  106. package/dist/modules/auth/components/reset-password-page.client.d.mts.map +1 -1
  107. package/dist/modules/auth/components/update-password-command.d.mts +2 -2
  108. package/dist/modules/auth/components/update-password-command.d.mts.map +1 -1
  109. package/dist/modules/auth/db/schema.d.mts +73 -73
  110. package/dist/modules/auth/db/schema.d.mts.map +1 -1
  111. package/dist/modules/auth/handler/client.client.d.mts +2 -2
  112. package/dist/modules/commands/components/alert-dialog-command.client.d.mts +10 -10
  113. package/dist/modules/commands/components/click-command.client.d.mts +2 -2
  114. package/dist/modules/commands/components/command-trigger.client.d.mts +6 -6
  115. package/dist/modules/commands/components/dialog-command.client.d.mts +8 -8
  116. package/dist/modules/commands/components/dropdown-command.client.d.mts +5 -5
  117. package/dist/modules/commands/components/empty-command.client.d.mts +2 -2
  118. package/dist/modules/commands/components/form-dialog-command.client.d.mts +11 -11
  119. package/dist/modules/commands/menus/context-menu.client.d.mts +2 -2
  120. package/dist/modules/commands/menus/dropdown-menu.client.d.mts +3 -3
  121. package/dist/modules/commands/menus/inline-menu.client.d.mts +3 -3
  122. package/dist/modules/commands/menus/responsive-menu.client.d.mts +3 -3
  123. package/dist/modules/commands/utils/archive-command.client.d.mts +3 -3
  124. package/dist/modules/commands/utils/delete-command.client.d.mts +3 -3
  125. package/dist/modules/config/db/helpers.d.mts +5 -5
  126. package/dist/modules/config/db/helpers.d.mts.map +1 -1
  127. package/dist/modules/data-tables/components/cell/common.client.d.mts +5 -5
  128. package/dist/modules/data-tables/components/cell/common.client.d.mts.map +1 -1
  129. package/dist/modules/data-tables/components/column-header.d.mts +2 -2
  130. package/dist/modules/data-tables/components/column-header.d.mts.map +1 -1
  131. package/dist/modules/data-tables/components/filters/combobox.client.d.mts +2 -2
  132. package/dist/modules/data-tables/components/filters/combobox.client.d.mts.map +1 -1
  133. package/dist/modules/data-tables/components/filters/slider.client.d.mts +2 -2
  134. package/dist/modules/data-tables/components/filters/slider.client.d.mts.map +1 -1
  135. package/dist/modules/data-tables/components/header.d.mts +4 -4
  136. package/dist/modules/data-tables/components/header.d.mts.map +1 -1
  137. package/dist/modules/data-tables/components/layout.d.mts +2 -2
  138. package/dist/modules/data-tables/components/layout.d.mts.map +1 -1
  139. package/dist/modules/data-tables/components/search-input.client.d.mts +2 -2
  140. package/dist/modules/data-tables/components/search-input.client.d.mts.map +1 -1
  141. package/dist/modules/data-tables/components/skeleton.d.mts +2 -2
  142. package/dist/modules/data-tables/components/skeleton.d.mts.map +1 -1
  143. package/dist/modules/data-tables/components/table.d.mts +7 -7
  144. package/dist/modules/data-tables/components/table.d.mts.map +1 -1
  145. package/dist/modules/data-tables/components/toolbar.d.mts +3 -3
  146. package/dist/modules/data-tables/components/toolbar.d.mts.map +1 -1
  147. package/dist/modules/data-tables/hooks/use-context.client.d.mts +2 -2
  148. package/dist/modules/data-tables/hooks/use-context.client.d.mts.map +1 -1
  149. package/dist/modules/data-tables/tables/data-table/components/table.d.mts +2 -2
  150. package/dist/modules/data-tables/tables/data-table/components/table.d.mts.map +1 -1
  151. package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts +2 -2
  152. package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts.map +1 -1
  153. package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts +2 -2
  154. package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts.map +1 -1
  155. package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts +2 -2
  156. package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts.map +1 -1
  157. package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts +2 -2
  158. package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts.map +1 -1
  159. package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts +3 -3
  160. package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts.map +1 -1
  161. package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts +2 -2
  162. package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts.map +1 -1
  163. package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts +2 -2
  164. package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts.map +1 -1
  165. package/dist/modules/data-tables/tables/inline-table/components/table.d.mts +2 -2
  166. package/dist/modules/data-tables/tables/inline-table/components/table.d.mts.map +1 -1
  167. package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts +2 -2
  168. package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts.map +1 -1
  169. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts +2 -2
  170. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts.map +1 -1
  171. package/dist/modules/inline-edit/components/combobox.client.d.mts +2 -2
  172. package/dist/modules/inline-edit/components/combobox.client.d.mts.map +1 -1
  173. package/dist/modules/inline-edit/components/date-input.client.d.mts +2 -2
  174. package/dist/modules/inline-edit/components/date-input.client.d.mts.map +1 -1
  175. package/dist/modules/inline-edit/components/date-picker.client.d.mts +2 -2
  176. package/dist/modules/inline-edit/components/date-picker.client.d.mts.map +1 -1
  177. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts +2 -2
  178. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts.map +1 -1
  179. package/dist/modules/inline-edit/components/editor.client.d.mts +2 -2
  180. package/dist/modules/inline-edit/components/editor.client.d.mts.map +1 -1
  181. package/dist/modules/inline-edit/components/input-recipient.client.d.mts +2 -2
  182. package/dist/modules/inline-edit/components/input-recipient.client.d.mts.map +1 -1
  183. package/dist/modules/inline-edit/components/input-toggle.client.d.mts +2 -2
  184. package/dist/modules/inline-edit/components/input-toggle.client.d.mts.map +1 -1
  185. package/dist/modules/inline-edit/components/input.client.d.mts +4 -4
  186. package/dist/modules/inline-edit/components/input.client.d.mts.map +1 -1
  187. package/dist/modules/inline-edit/components/select.client.d.mts +6 -6
  188. package/dist/modules/inline-edit/components/select.client.d.mts.map +1 -1
  189. package/dist/modules/inline-edit/components/switch.client.d.mts +2 -2
  190. package/dist/modules/inline-edit/components/switch.client.d.mts.map +1 -1
  191. package/dist/modules/inline-edit/components/toggle.client.d.mts +2 -2
  192. package/dist/modules/inline-edit/components/toggle.client.d.mts.map +1 -1
  193. package/dist/modules/inline-edit/hooks/context.client.d.mts +2 -2
  194. package/dist/modules/inline-edit/hooks/context.client.d.mts.map +1 -1
  195. package/dist/modules/router/lib/query-client.server.d.mts +2 -2
  196. package/dist/modules/router/lib/query-client.server.d.mts.map +1 -1
  197. package/dist/modules/storage/components/dropzone-context.client.d.mts +2 -2
  198. package/dist/modules/storage/components/dropzone-context.client.d.mts.map +1 -1
  199. package/dist/modules/storage/components/dropzone.client.d.mts +5 -5
  200. package/dist/modules/storage/components/dropzone.client.d.mts.map +1 -1
  201. package/dist/modules/storage/components/image-grid.client.d.mts +3 -3
  202. package/dist/modules/storage/components/image-grid.client.d.mts.map +1 -1
  203. package/dist/modules/storage/components/upload-zone.client.d.mts +5 -3
  204. package/dist/modules/storage/components/upload-zone.client.d.mts.map +1 -1
  205. package/dist/modules/storage/components/upload-zone.client.mjs +6 -4
  206. package/dist/modules/storage/components/upload-zone.client.mjs.map +1 -1
  207. package/dist/modules/storage/config/filters.d.mts +1 -0
  208. package/dist/modules/storage/config/filters.d.mts.map +1 -1
  209. package/dist/modules/storage/config/filters.mjs +2 -1
  210. package/dist/modules/storage/config/filters.mjs.map +1 -1
  211. package/dist/modules/storage/lib/helpers.d.mts +2 -2
  212. package/dist/modules/storage/lib/helpers.d.mts.map +1 -1
  213. package/dist/modules/storage/lib/helpers.mjs +1 -0
  214. package/dist/modules/storage/lib/helpers.mjs.map +1 -1
  215. package/dist/modules/storage/lib/router.server.d.mts +3195 -2217
  216. package/dist/modules/storage/lib/schema.d.mts +122 -88
  217. package/dist/modules/storage/lib/schema.d.mts.map +1 -1
  218. package/dist/modules/storage/lib/schema.mjs +1 -0
  219. package/dist/modules/storage/lib/schema.mjs.map +1 -1
  220. package/dist/modules/storage/lib/service.server.d.mts +39 -12
  221. package/dist/modules/storage/lib/service.server.d.mts.map +1 -1
  222. package/dist/modules/storage/lib/service.server.mjs +9 -3
  223. package/dist/modules/storage/lib/service.server.mjs.map +1 -1
  224. package/dist/modules/storage/lib/validators.d.mts +219 -138
  225. package/dist/modules/storage/lib/validators.d.mts.map +1 -1
  226. package/dist/modules/storage/lib/validators.mjs +14 -24
  227. package/dist/modules/storage/lib/validators.mjs.map +1 -1
  228. package/dist/storage.d.mts +2 -2
  229. package/dist/storage.mjs +2 -2
  230. package/package.json +3 -3
  231. package/src/modules/storage/components/upload-zone.client.tsx +5 -2
  232. package/src/modules/storage/config/filters.ts +1 -0
  233. package/src/modules/storage/lib/helpers.ts +3 -2
  234. package/src/modules/storage/lib/schema.ts +1 -0
  235. package/src/modules/storage/lib/service.server.ts +10 -2
  236. package/src/modules/storage/lib/validators.ts +20 -31
@@ -1 +1 @@
1
- {"version":3,"file":"schema.mjs","names":[],"sources":["../../../../src/modules/storage/lib/schema.ts"],"sourcesContent":["import { users } from \"@/modules/auth/db/schema\";\nimport { baseColumns } from \"@/modules/config/db/helpers\";\nimport { relations } from \"drizzle-orm\";\nimport { AnyPgColumn, boolean, pgEnum, pgTable, unique } from \"drizzle-orm/pg-core\";\nimport { imageDispositions, imageVariants } from \"./constants\";\nimport { nodeSubtypes } from \"./helpers\";\n\nexport const nodeTypeEnum = pgEnum(\"node_types\", [\"file\", \"folder\"]);\nexport const nodeModeEnum = pgEnum(\"node_mode\", [\"private\", \"public\"]);\n\n/**\n * Node table\n */\nexport const nodes = pgTable(\"nodes\", (t) => ({\n ...baseColumns,\n name: t.text().notNull(),\n namespace: t.text().notNull().default(\"global\"),\n type: nodeTypeEnum(),\n mode: nodeModeEnum().default(\"private\"),\n subtype: t.text({ enum: nodeSubtypes }).notNull().default(\"other\"),\n size: t.integer().default(0),\n contentType: t.varchar({ length: 255 }),\n readonly: boolean().default(false),\n createdBy: t.uuid().references(() => users.id, { onDelete: \"set null\" }),\n isPending: t.boolean().default(false).notNull(),\n isDeleted: t.boolean().default(false).notNull(),\n parentId: t.uuid().references((): AnyPgColumn => nodes.id, { onDelete: \"cascade\" }),\n}));\n\nexport const nodesRelations = relations(nodes, ({ one, many }) => ({\n parent: one(nodes, {\n fields: [nodes.parentId],\n references: [nodes.id],\n relationName: \"parent\",\n }),\n children: many(nodes, {\n relationName: \"parent\",\n }),\n urls: many(nodePresignedUrls),\n variants: many(nodeVariants),\n}));\n\n/**\n * Node variants table\n */\nexport const nodeVariants = pgTable(\"node_variants\", (t) => ({\n ...baseColumns,\n nodeId: t\n .uuid()\n .notNull()\n .references(() => nodes.id, { onDelete: \"cascade\" }),\n variant: t.text({ enum: imageVariants }).notNull(),\n width: t.integer().notNull(),\n}));\n\nexport const nodeVariantsRelations = relations(nodeVariants, ({ one }) => ({\n node: one(nodes, {\n fields: [nodeVariants.nodeId],\n references: [nodes.id],\n relationName: \"node\",\n }),\n}));\n\n/**\n * Node presigned urls table\n */\nexport const nodePresignedUrls = pgTable(\n \"node_presigned_urls\",\n (t) => ({\n ...baseColumns,\n url: t.text().notNull().unique(),\n variant: t.text({ enum: imageVariants }).notNull(),\n disposition: t.text({ enum: imageDispositions }).notNull(),\n expiresAt: t.timestamp().notNull(),\n nodeId: t\n .uuid()\n .notNull()\n .references(() => nodes.id, { onDelete: \"cascade\" }),\n variantId: t.uuid().references(() => nodeVariants.id, { onDelete: \"set null\" }),\n }),\n (t) => [unique(\"node_presigned_url_unique\").on(t.nodeId, t.variant, t.disposition)],\n);\n\nexport const nodePresignedUrlsRelations = relations(nodePresignedUrls, ({ one }) => ({\n node: one(nodes, {\n fields: [nodePresignedUrls.nodeId],\n references: [nodes.id],\n relationName: \"node\",\n }),\n}));\n\n/**\n * Drive schema\n **/\nconst driveSchema = {\n nodes,\n nodesRelations,\n nodeVariants,\n nodeVariantsRelations,\n nodePresignedUrls,\n nodePresignedUrlsRelations,\n};\nexport type DriveSchema = typeof driveSchema;\n"],"mappings":";;;;;;;;AAOA,MAAa,eAAe,OAAO,cAAc,CAAC,QAAQ,SAAS,CAAC;AACpE,MAAa,eAAe,OAAO,aAAa,CAAC,WAAW,SAAS,CAAC;;;;AAKtE,MAAa,QAAQ,QAAQ,UAAU,OAAO;CAC5C,GAAG;CACH,MAAM,EAAE,MAAM,CAAC,SAAS;CACxB,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ,SAAS;CAC/C,MAAM,cAAc;CACpB,MAAM,cAAc,CAAC,QAAQ,UAAU;CACvC,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC,CAAC,SAAS,CAAC,QAAQ,QAAQ;CAClE,MAAM,EAAE,SAAS,CAAC,QAAQ,EAAE;CAC5B,aAAa,EAAE,QAAQ,EAAE,QAAQ,KAAK,CAAC;CACvC,UAAU,SAAS,CAAC,QAAQ,MAAM;CAClC,WAAW,EAAE,MAAM,CAAC,iBAAiB,MAAM,IAAI,EAAE,UAAU,YAAY,CAAC;CACxE,WAAW,EAAE,SAAS,CAAC,QAAQ,MAAM,CAAC,SAAS;CAC/C,WAAW,EAAE,SAAS,CAAC,QAAQ,MAAM,CAAC,SAAS;CAC/C,UAAU,EAAE,MAAM,CAAC,iBAA8B,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CACpF,EAAE;AAEH,MAAa,iBAAiB,UAAU,QAAQ,EAAE,KAAK,YAAY;CACjE,QAAQ,IAAI,OAAO;EACjB,QAAQ,CAAC,MAAM,SAAS;EACxB,YAAY,CAAC,MAAM,GAAG;EACtB,cAAc;EACf,CAAC;CACF,UAAU,KAAK,OAAO,EACpB,cAAc,UACf,CAAC;CACF,MAAM,KAAK,kBAAkB;CAC7B,UAAU,KAAK,aAAa;CAC7B,EAAE;;;;AAKH,MAAa,eAAe,QAAQ,kBAAkB,OAAO;CAC3D,GAAG;CACH,QAAQ,EACL,MAAM,CACN,SAAS,CACT,iBAAiB,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CACtD,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC,CAAC,SAAS;CAClD,OAAO,EAAE,SAAS,CAAC,SAAS;CAC7B,EAAE;AAEH,MAAa,wBAAwB,UAAU,eAAe,EAAE,WAAW,EACzE,MAAM,IAAI,OAAO;CACf,QAAQ,CAAC,aAAa,OAAO;CAC7B,YAAY,CAAC,MAAM,GAAG;CACtB,cAAc;CACf,CAAC,EACH,EAAE;;;;AAKH,MAAa,oBAAoB,QAC/B,wBACC,OAAO;CACN,GAAG;CACH,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ;CAChC,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC,CAAC,SAAS;CAClD,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC,CAAC,SAAS;CAC1D,WAAW,EAAE,WAAW,CAAC,SAAS;CAClC,QAAQ,EACL,MAAM,CACN,SAAS,CACT,iBAAiB,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CACtD,WAAW,EAAE,MAAM,CAAC,iBAAiB,aAAa,IAAI,EAAE,UAAU,YAAY,CAAC;CAChF,IACA,MAAM,CAAC,OAAO,4BAA4B,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CACpF;AAED,MAAa,6BAA6B,UAAU,oBAAoB,EAAE,WAAW,EACnF,MAAM,IAAI,OAAO;CACf,QAAQ,CAAC,kBAAkB,OAAO;CAClC,YAAY,CAAC,MAAM,GAAG;CACtB,cAAc;CACf,CAAC,EACH,EAAE"}
1
+ {"version":3,"file":"schema.mjs","names":[],"sources":["../../../../src/modules/storage/lib/schema.ts"],"sourcesContent":["import { users } from \"@/modules/auth/db/schema\";\nimport { baseColumns } from \"@/modules/config/db/helpers\";\nimport { relations } from \"drizzle-orm\";\nimport { AnyPgColumn, boolean, pgEnum, pgTable, unique } from \"drizzle-orm/pg-core\";\nimport { imageDispositions, imageVariants } from \"./constants\";\nimport { nodeSubtypes } from \"./helpers\";\n\nexport const nodeTypeEnum = pgEnum(\"node_types\", [\"file\", \"folder\"]);\nexport const nodeModeEnum = pgEnum(\"node_mode\", [\"private\", \"public\"]);\n\n/**\n * Node table\n */\nexport const nodes = pgTable(\"nodes\", (t) => ({\n ...baseColumns,\n name: t.text().notNull(),\n namespace: t.text().notNull().default(\"global\"),\n type: nodeTypeEnum(),\n mode: nodeModeEnum().default(\"private\"),\n subtype: t.text({ enum: nodeSubtypes }).notNull().default(\"other\"),\n size: t.integer().default(0),\n contentType: t.varchar({ length: 255 }),\n readonly: boolean().default(false),\n hidden: boolean().default(false),\n createdBy: t.uuid().references(() => users.id, { onDelete: \"set null\" }),\n isPending: t.boolean().default(false).notNull(),\n isDeleted: t.boolean().default(false).notNull(),\n parentId: t.uuid().references((): AnyPgColumn => nodes.id, { onDelete: \"cascade\" }),\n}));\n\nexport const nodesRelations = relations(nodes, ({ one, many }) => ({\n parent: one(nodes, {\n fields: [nodes.parentId],\n references: [nodes.id],\n relationName: \"parent\",\n }),\n children: many(nodes, {\n relationName: \"parent\",\n }),\n urls: many(nodePresignedUrls),\n variants: many(nodeVariants),\n}));\n\n/**\n * Node variants table\n */\nexport const nodeVariants = pgTable(\"node_variants\", (t) => ({\n ...baseColumns,\n nodeId: t\n .uuid()\n .notNull()\n .references(() => nodes.id, { onDelete: \"cascade\" }),\n variant: t.text({ enum: imageVariants }).notNull(),\n width: t.integer().notNull(),\n}));\n\nexport const nodeVariantsRelations = relations(nodeVariants, ({ one }) => ({\n node: one(nodes, {\n fields: [nodeVariants.nodeId],\n references: [nodes.id],\n relationName: \"node\",\n }),\n}));\n\n/**\n * Node presigned urls table\n */\nexport const nodePresignedUrls = pgTable(\n \"node_presigned_urls\",\n (t) => ({\n ...baseColumns,\n url: t.text().notNull().unique(),\n variant: t.text({ enum: imageVariants }).notNull(),\n disposition: t.text({ enum: imageDispositions }).notNull(),\n expiresAt: t.timestamp().notNull(),\n nodeId: t\n .uuid()\n .notNull()\n .references(() => nodes.id, { onDelete: \"cascade\" }),\n variantId: t.uuid().references(() => nodeVariants.id, { onDelete: \"set null\" }),\n }),\n (t) => [unique(\"node_presigned_url_unique\").on(t.nodeId, t.variant, t.disposition)],\n);\n\nexport const nodePresignedUrlsRelations = relations(nodePresignedUrls, ({ one }) => ({\n node: one(nodes, {\n fields: [nodePresignedUrls.nodeId],\n references: [nodes.id],\n relationName: \"node\",\n }),\n}));\n\n/**\n * Drive schema\n **/\nconst driveSchema = {\n nodes,\n nodesRelations,\n nodeVariants,\n nodeVariantsRelations,\n nodePresignedUrls,\n nodePresignedUrlsRelations,\n};\nexport type DriveSchema = typeof driveSchema;\n"],"mappings":";;;;;;;;AAOA,MAAa,eAAe,OAAO,cAAc,CAAC,QAAQ,SAAS,CAAC;AACpE,MAAa,eAAe,OAAO,aAAa,CAAC,WAAW,SAAS,CAAC;;;;AAKtE,MAAa,QAAQ,QAAQ,UAAU,OAAO;CAC5C,GAAG;CACH,MAAM,EAAE,MAAM,CAAC,SAAS;CACxB,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ,SAAS;CAC/C,MAAM,cAAc;CACpB,MAAM,cAAc,CAAC,QAAQ,UAAU;CACvC,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC,CAAC,SAAS,CAAC,QAAQ,QAAQ;CAClE,MAAM,EAAE,SAAS,CAAC,QAAQ,EAAE;CAC5B,aAAa,EAAE,QAAQ,EAAE,QAAQ,KAAK,CAAC;CACvC,UAAU,SAAS,CAAC,QAAQ,MAAM;CAClC,QAAQ,SAAS,CAAC,QAAQ,MAAM;CAChC,WAAW,EAAE,MAAM,CAAC,iBAAiB,MAAM,IAAI,EAAE,UAAU,YAAY,CAAC;CACxE,WAAW,EAAE,SAAS,CAAC,QAAQ,MAAM,CAAC,SAAS;CAC/C,WAAW,EAAE,SAAS,CAAC,QAAQ,MAAM,CAAC,SAAS;CAC/C,UAAU,EAAE,MAAM,CAAC,iBAA8B,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CACpF,EAAE;AAEH,MAAa,iBAAiB,UAAU,QAAQ,EAAE,KAAK,YAAY;CACjE,QAAQ,IAAI,OAAO;EACjB,QAAQ,CAAC,MAAM,SAAS;EACxB,YAAY,CAAC,MAAM,GAAG;EACtB,cAAc;EACf,CAAC;CACF,UAAU,KAAK,OAAO,EACpB,cAAc,UACf,CAAC;CACF,MAAM,KAAK,kBAAkB;CAC7B,UAAU,KAAK,aAAa;CAC7B,EAAE;;;;AAKH,MAAa,eAAe,QAAQ,kBAAkB,OAAO;CAC3D,GAAG;CACH,QAAQ,EACL,MAAM,CACN,SAAS,CACT,iBAAiB,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CACtD,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC,CAAC,SAAS;CAClD,OAAO,EAAE,SAAS,CAAC,SAAS;CAC7B,EAAE;AAEH,MAAa,wBAAwB,UAAU,eAAe,EAAE,WAAW,EACzE,MAAM,IAAI,OAAO;CACf,QAAQ,CAAC,aAAa,OAAO;CAC7B,YAAY,CAAC,MAAM,GAAG;CACtB,cAAc;CACf,CAAC,EACH,EAAE;;;;AAKH,MAAa,oBAAoB,QAC/B,wBACC,OAAO;CACN,GAAG;CACH,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ;CAChC,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC,CAAC,SAAS;CAClD,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC,CAAC,SAAS;CAC1D,WAAW,EAAE,WAAW,CAAC,SAAS;CAClC,QAAQ,EACL,MAAM,CACN,SAAS,CACT,iBAAiB,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CACtD,WAAW,EAAE,MAAM,CAAC,iBAAiB,aAAa,IAAI,EAAE,UAAU,YAAY,CAAC;CAChF,IACA,MAAM,CAAC,OAAO,4BAA4B,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CACpF;AAED,MAAa,6BAA6B,UAAU,oBAAoB,EAAE,WAAW,EACnF,MAAM,IAAI,OAAO;CACf,QAAQ,CAAC,kBAAkB,OAAO;CAClC,YAAY,CAAC,MAAM,GAAG;CACtB,cAAc;CACf,CAAC,EACH,EAAE"}
@@ -36,6 +36,27 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
36
36
  * Get object
37
37
  */
38
38
  getObject(id: string, options?: GetFileURLSchema): Promise<_aws_sdk_client_s30.GetObjectCommandOutput>;
39
+ /**
40
+ * Get node by id
41
+ */
42
+ getNodeById(id: string): Promise<{
43
+ name: string;
44
+ namespace: string;
45
+ type: "file" | "folder" | null;
46
+ mode: "private" | "public" | null;
47
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
48
+ size: number | null;
49
+ contentType: string | null;
50
+ readonly: boolean | null;
51
+ hidden: boolean | null;
52
+ createdBy: string | null;
53
+ isPending: boolean;
54
+ isDeleted: boolean;
55
+ parentId: string | null;
56
+ id: string;
57
+ createdAt: Date;
58
+ updatedAt: Date;
59
+ }[]>;
39
60
  /**
40
61
  * Get nodes by parent id
41
62
  */
@@ -51,6 +72,7 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
51
72
  size: number | null;
52
73
  contentType: string | null;
53
74
  readonly: boolean | null;
75
+ hidden: boolean | null;
54
76
  createdBy: string | null;
55
77
  isPending: boolean;
56
78
  isDeleted: boolean;
@@ -67,19 +89,20 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
67
89
  * Upload file to S3 and add it to the database
68
90
  **/
69
91
  uploadFile(input: UploadFileSchema & Pick<PutObjectInput, "body">): Promise<{
92
+ name: string;
93
+ readonly: boolean | null;
70
94
  id: string;
95
+ type: "file" | "folder" | null;
71
96
  createdAt: Date;
72
97
  updatedAt: Date;
73
- name: string;
74
- type: "file" | "folder" | null;
75
- readonly: boolean | null;
76
98
  mode: "private" | "public" | null;
77
99
  size: number | null;
100
+ isPending: boolean;
78
101
  namespace: string;
79
102
  subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
80
103
  contentType: string | null;
104
+ hidden: boolean | null;
81
105
  createdBy: string | null;
82
- isPending: boolean;
83
106
  isDeleted: boolean;
84
107
  parentId: string | null;
85
108
  }>;
@@ -90,19 +113,20 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
90
113
  id: string;
91
114
  presignedUrl: string;
92
115
  node: {
116
+ name: string;
117
+ readonly: boolean | null;
93
118
  id: string;
119
+ type: "file" | "folder" | null;
94
120
  createdAt: Date;
95
121
  updatedAt: Date;
96
- name: string;
97
- type: "file" | "folder" | null;
98
- readonly: boolean | null;
99
122
  mode: "private" | "public" | null;
100
123
  size: number | null;
124
+ isPending: boolean;
101
125
  namespace: string;
102
126
  subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
103
127
  contentType: string | null;
128
+ hidden: boolean | null;
104
129
  createdBy: string | null;
105
- isPending: boolean;
106
130
  isDeleted: boolean;
107
131
  parentId: string | null;
108
132
  };
@@ -121,6 +145,7 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
121
145
  size: number | null;
122
146
  contentType: string | null;
123
147
  readonly: boolean | null;
148
+ hidden: boolean | null;
124
149
  createdBy: string | null;
125
150
  isPending: boolean;
126
151
  isDeleted: boolean;
@@ -139,19 +164,20 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
139
164
  * Create a new folder
140
165
  */
141
166
  createFolder(input: CreateFolderNodeSchema): Promise<{
167
+ name: string;
168
+ readonly: boolean | null;
142
169
  id: string;
170
+ type: "file" | "folder" | null;
143
171
  createdAt: Date;
144
172
  updatedAt: Date;
145
- name: string;
146
- type: "file" | "folder" | null;
147
- readonly: boolean | null;
148
173
  mode: "private" | "public" | null;
149
174
  size: number | null;
175
+ isPending: boolean;
150
176
  namespace: string;
151
177
  subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
152
178
  contentType: string | null;
179
+ hidden: boolean | null;
153
180
  createdBy: string | null;
154
- isPending: boolean;
155
181
  isDeleted: boolean;
156
182
  parentId: string | null;
157
183
  }>;
@@ -170,6 +196,7 @@ declare class StorageService<TSchema extends TDatabaseSchema> {
170
196
  size: number | null;
171
197
  contentType: string | null;
172
198
  readonly: boolean | null;
199
+ hidden: boolean | null;
173
200
  createdBy: string | null;
174
201
  isPending: boolean;
175
202
  isDeleted: boolean;
@@ -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;;;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"}
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;;;kCAA3C,mBAA2C,QAAA,mBAAA,CAAA,sBAAA;EAmCpD;;;2BAAA;;IAO2C,SAAA,EAAA,MAAA;IAwB9C,IAAA,EAAA,MAAA,GAAA,QAAA,GAAA,IAAA;IAAe,IAAA,EAAA,SAAA,GAAA,QAAA,GAAA,IAAA;IAA2C,OAAA,EAAA,OAAA,GAAA,UAAA,GAAA,aAAA,GAAA,OAAA,GAAA,OAAA,GAAA,SAAA,GAAA,OAAA;IAmE3D,IAAA,EAAA,MAAA,GAAA,IAAA;IAAwB,WAAA,EAAA,MAAA,GAAA,IAAA;IAAL,QAAA,EAAA,OAAA,GAAA,IAAA;;;IAA4B,SAAA,EAAA,OAAA;IAyC5C,SAAA,EAAA,OAAA;;;IAAiB,SAAA,MAAA;;;EAqCH;;;;;;KAzKO,0BAAuB;;IAwPvB,SAAA,EAAA,MAAA;IA0CJ,IAAA,EAAA,MAAA,GAAA,QAAA,GAAA,IAAA;;;IAAkB,IAAA,EAAA,MAAA,GAAA,IAAA;IA4BrC,WAAA,EAAA,MAAA,GAAA,IAAA;IAAgB,QAAA,EAAA,OAAA,GAAA,IAAA;IAAA,MAAA,EAAA,OAAA,GAAA,IAAA;;;;;;;;;;;;qBAtShB,gBAAe,mBAA2C;;;;oBAmE3D,mBAAmB,KAAK,0BAAuB;;;;;;;;;;;;;;;;;;;;;uBAyC5C,oBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;MAqCH;;;;;;;;;;;;;;;;;;;;;;;MAuBG;;;;sBAwDlB,yBAAsB;;;;;;;;;;;;;;;;;;;;;;;UA0CJ;MAAkB;;;;;;;;;;;;;;;;;;;;;qBA4BrC,mBAAgB"}
@@ -66,8 +66,8 @@ var StorageService = class {
66
66
  Bucket: BUCKET_NAME,
67
67
  Key: getDriveBucketKey(input.id, input.variant),
68
68
  Body: input.body,
69
- ContentType: input.contentType,
70
- ContentLength: input.size,
69
+ ContentType: input.contentType ?? "",
70
+ ContentLength: input.size ?? 0,
71
71
  Metadata: { nodeId: input.id }
72
72
  });
73
73
  }
@@ -79,12 +79,18 @@ var StorageService = class {
79
79
  return await this.#blob.send(putCommand);
80
80
  }
81
81
  /**
82
+ * Get node by id
83
+ */
84
+ async getNodeById(id) {
85
+ return this.#db.select().from(nodes).where(eq(nodes.id, id));
86
+ }
87
+ /**
82
88
  * Get nodes by parent id
83
89
  */
84
90
  async getNodesByParentId({ filters, ...query }) {
85
91
  const orderBy = convertOrderByToQueryParams(query, nodes, asc(nodes.createdAt));
86
92
  const search = convertSearchToQueryParams(query, [nodes.name]);
87
- return this.#db.select().from(nodes).where(and(filters.nodeIds != null ? inArray(nodes.id, filters.nodeIds) : void 0, filters.types != null ? inArray(nodes.type, filters.types) : void 0, filters.isDeleted != null ? eq(nodes.isDeleted, filters.isDeleted) : void 0, filters.parentId ? eq(nodes.parentId, filters.parentId) : isNull(nodes.parentId), eq(nodes.namespace, filters.namespace), search)).orderBy(orderBy);
93
+ return this.#db.select().from(nodes).where(and(filters.nodeIds != null ? inArray(nodes.id, filters.nodeIds) : void 0, filters.types != null ? inArray(nodes.type, filters.types) : void 0, filters.isDeleted != null ? eq(nodes.isDeleted, filters.isDeleted) : void 0, filters.hidden != null ? eq(nodes.hidden, filters.hidden) : void 0, filters.parentId ? eq(nodes.parentId, filters.parentId) : isNull(nodes.parentId), eq(nodes.namespace, filters.namespace), search)).orderBy(orderBy);
88
94
  }
89
95
  /**
90
96
  * Get file url
@@ -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 },\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":";;;;;;;;;;;;;;;;;;;AAiDA,IAAa,iBAAb,MAA6D;;;;CAI3D;CACA;;;;CAKA,YAAY,EAAE,IAAI,UAAyC;AACzD,QAAKA,KAAM;AACX,QAAKC,OAAQ,IAAI,SAAS,OAAO;;;;;CAMnC,OAAO;AACL,SAAO,MAAKA;;;;;CAMd,kBAAkB,OAAuB;EACvC,MAAM,QAAQ,gBAAgB,MAAM,MAAM;AAE1C,SAAO,IAAI,iBAAiB;GAC1B,QAAQ;GACR,KAAK,kBAAkB,MAAM,IAAI,MAAM,QAAQ;GAC/C,4BAA4B,MAAM;GACnC,CAAC;;;;;CAMJ,MAAM,UAAU,IAAY,UAA4B,0BAA0B;EAChF,MAAM,aAAa,MAAKC,iBAAkB;GAAE,GAAG;GAAS;GAAI,CAAC;AAE7D,SAAO,MAAM,MAAKD,KAAM,KAAK,WAAW;;;;;CAM1C,kBAAkB,OAAuB;EACvC,MAAM,QAAQ,gBAAgB,MAAM,MAAM;AAE1C,SAAO,IAAI,iBAAiB;GAC1B,QAAQ;GACR,KAAK,kBAAkB,MAAM,IAAI,MAAM,QAAQ;GAC/C,MAAM,MAAM;GACZ,aAAa,MAAM;GACnB,eAAe,MAAM;GACrB,UAAU,EACR,QAAQ,MAAM,IACf;GACF,CAAC;;;;;CAMJ,OAAME,UAAW,OAAuB;EACtC,MAAM,aAAa,MAAKC,iBAAkB,MAAM;AAChD,SAAO,MAAM,MAAKH,KAAM,KAAK,WAAW;;;;;CAM1C,MAAM,mBAAmB,EAAE,SAAS,GAAG,SAAkC;EACvE,MAAM,UAAU,4BAA4B,OAAO,OAAO,IAAI,MAAM,UAAU,CAAC;EAC/E,MAAM,SAAS,2BAA2B,OAAO,CAAC,MAAM,KAAK,CAAC;AAE9D,SAAO,MAAKD,GACT,QAAQ,CACR,KAAK,MAAM,CACX,MACC,IACE,QAAQ,WAAW,OAAO,QAAQ,MAAM,IAAI,QAAQ,QAAQ,GAAG,QAC/D,QAAQ,SAAS,OAAO,QAAQ,MAAM,MAAM,QAAQ,MAAM,GAAG,QAC7D,QAAQ,aAAa,OAAO,GAAG,MAAM,WAAW,QAAQ,UAAU,GAAG,QACrE,QAAQ,WAAW,GAAG,MAAM,UAAU,QAAQ,SAAS,GAAG,OAAO,MAAM,SAAS,EAChF,GAAG,MAAM,WAAW,QAAQ,UAAU,EACtC,OACD,CACF,CACA,QAAQ,QAAe;;;;;CAM5B,MAAM,aAAa,MAAY,UAA4B,0BAA0B;EACnF,MAAM,CAAC,gBAAgB,MAAM,MAAKA,GAC/B,OAAO;GAAE,KAAK,kBAAkB;GAAK,WAAW,kBAAkB;GAAW,CAAC,CAC9E,KAAK,kBAAkB,CACvB,MACC,IACE,GAAG,kBAAkB,QAAQ,KAAK,GAAG,EACrC,GAAG,kBAAkB,SAAS,QAAQ,QAAQ,EAC9C,GAAG,kBAAkB,aAAa,QAAQ,YAAY,CACvD,CACF;AAEH,MAAI,gBAAgB,aAAa,4BAAY,IAAI,MAAM,CAAE,QAAO,aAAa;EAE7E,MAAM,YAAY,OAAO;EAUzB,MAAM,WAPW,MAAM,MAAKA,GACzB,QAAQ,CACR,KAAK,aAAa,CAClB,MAAM,GAAG,aAAa,QAAQ,KAAK,GAAG,CAAC,EAGX,MAAM,MAAM,EAAE,YAAY,QAAQ,QAAQ,GACzC,QAAQ,UAAU;AAElD,UAAQ,KACN,uCAAuC,KAAK,GAAG,iBAAiB,QAAQ,oBAAoB,QAAQ,cACrG;EAGD,MAAM,aAAa,MAAKE,iBAAkB;GACxC,IAAI,KAAK;GACT;GACA,aAAa,GAAG,QAAQ,YAAY,cAAc,KAAK,KAAK;GAC7D,CAAC;EAGF,MAAM,MAAM,MAAM,aAAa,MAAKD,MAAO,YAAY,EAAE,WAAW,CAAC;AAGrE,QAAM,YAAY;AAChB,SAAM,MAAKD,GACR,OAAO,kBAAkB,CACzB,OAAO;IACN,QAAQ,KAAK;IACb;IACA;IACA,aAAa,QAAQ;IACrB,WAAW,2BAAW,IAAI,MAAM,EAAE,UAAU;IAC7C,CAAC,CACD,mBAAmB;IAClB,QAAQ;KACN,kBAAkB;KAClB,kBAAkB;KAClB,kBAAkB;KACnB;IACD,KAAK;KAAE;KAAK,WAAW,2BAAW,IAAI,MAAM,EAAE,UAAU;KAAE;IAC3D,CAAC;IACJ;AAEF,SAAO;;;;;CAMT,MAAM,WAAW,OAAwD;EACvE,MAAM,KAAK,qBAAqB;AAEhC,SAAO,MAAM,MAAKA,GAAI,YAAY,OAAO,OAAO;GAC9C,MAAM,CAAC,UAAU,MAAM,GACpB,OAAO,MAAM,CACb,OAAO;IACN;IACA,MAAM;IACN,MAAM,MAAM;IACZ,WAAW,MAAM;IACjB,UAAU,MAAM;IAChB,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,MAAM,MAAM;IACZ,SAAS,iBAAiB,MAAM;IACjC,CAAC,CACD,WAAW;AAEd,OAAI,CAAC,OACH,OAAM,IAAI,YAAY,yBAAyB,EAC7C,SAAS,+BACV,CAAC;AAGJ,SAAM,MAAKG,UAAW;IACpB;IACA,MAAM,MAAM;IACZ,SAAS;IACT,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,MAAM,MAAM;IACb,CAAC;AAEF,UAAO;IACP;;;;;CAMJ,MAAM,cAAc,OAA0B;EAE5C,MAAM,aAAa,MAAKC,iBAAkB;GACxC,IAAI,MAAM;GACV,MAAM,MAAM;GACZ,SAAS;GACT,aAAa,MAAM;GACnB,MAAM,MAAM;GACb,CAAC;EAGF,MAAM,eAAe,MAAM,aAAa,MAAKH,MAAO,YAAY,EAAE,WAAW,MAAM,CAAC;EAEpF,MAAM,CAAC,QAAQ,MAAM,MAAKD,GACvB,OAAO,MAAM,CACb,OAAO;GACN,GAAG;GACH,SAAS,iBAAiB,MAAM;GAChC,WAAW;GACX,MAAM;GACN,IAAI,MAAM;GACX,CAAC,CACD,WAAW;AAEd,MAAI,CAAC,KACH,OAAM,IAAI,YAAY,yBAAyB,EAC7C,SAAS,+BACV,CAAC;AAIJ,SAAO;GAAE,IAAI,MAAM;GAAI;GAAc;GAAM;;;;;CAM7C,MAAM,cAAc,OAAuB;EACzC,MAAM,CAAC,UAAU,MAAM,MAAKA,GACzB,OAAO,MAAM,CACb,IAAI,EAAE,WAAW,OAAO,CAAC,CACzB,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,CAAC,CAC7B,WAAW;AAEd,MAAI,CAAC,OACH,OAAM,IAAI,YAAY,aAAa,EAAE,SAAS,kBAAkB,CAAC;;;;AAOnE,QAAM,KAAK,iBAAiB,MAAM;AAElC,SAAO;;;;;CAMT,MAAM,iBAAiB,OAAuB;;;;EAI5C,MAAM,aAAa,MAAKE,iBAAkB;GAAE,IAAI,MAAM;GAAI,SAAS;GAAQ,CAAC;EAE5E,MAAM,WAAW,MAAM,MAAKD,KAAM,KAAK,WAAW;EAClD,MAAM,cAAc,SAAS;AAC7B,MAAI,CAAC,SAAS,KACZ,OAAM,IAAI,YAAY,yBAAyB,EAC7C,SAAS,+BACV,CAAC;;;;EAMJ,MAAM,YAAY,MAAM,SAAS,KAAK,sBAAsB;EAC5D,MAAM,SAAS,OAAO,KAAK,UAAU;;;;AAKrC,MAAI,aAAa,WAAW,SAAS,EAAE;GACrC,MAAM,QAAQ,MAAM,OAAO;AAG3B,SAAM,QAAQ,WACZ,YAAY,QAAQ,OAAO,UAAU;IAEnC,MAAM,UAAU,MAAM,MAAM,QAAQ,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,UAAU;AAG/E,WAAO,MAAKD,GAAI,YAAY,OAAO,OAAO;AACxC,WAAM,MAAKG,UAAW;MACpB,IAAI,MAAM;MACV,MAAM;MACN,SAAS,WAAW;MACpB,aAAa;MACb,MAAM,QAAQ;MACf,CAAC;AAEF,WAAM,GAAG,OAAO,aAAa,CAAC,OAAO;MACnC,QAAQ,MAAM;MACd,SAAS,WAAW;MACpB;MACD,CAAC;MACF;KACF,CACH;;;;;;CAOL,MAAM,aAAa,OAA+B;EAChD,MAAM,CAAC,UAAU,MAAM,WACnB,MAAM,MAAKH,GAAI,QAAQ,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,MAAM,IAAI,MAAM,SAAS,CAAC,GACvE,EAAE;;;;AAKN,MAAI,MAAM,YAAY,CAAC,OACrB,OAAM,IAAI,YAAY,eAAe,EAAE,SAAS,oBAAoB,CAAC;AAGvE,MAAI,UAAU,CAAC,SAAS,OAAO,CAC7B,OAAM,IAAI,YAAY,eAAe,EAAE,SAAS,0BAA0B,CAAC;AAG7E,MAAI,UAAU,OAAO,cAAc,MAAM,UACvC,OAAM,IAAI,YAAY,eAAe,EACnC,SAAS,uCACV,CAAC;;;;EAMJ,MAAM,CAAC,UAAU,MAAM,MAAKA,GACzB,OAAO,MAAM,CACb,OAAO;GAAE,GAAG;GAAO,MAAM;GAAU,CAAC,CACpC,WAAW;AAEd,MAAI,CAAC,OACH,OAAM,IAAI,YAAY,yBAAyB,EAC7C,SAAS,qCACV,CAAC;AAGJ,SAAO;;;;;CAMT,MAAM,WAAW,OAA+C;EAC9D,MAAM,CAAC,QAAQ,MAAM,MAAKA,GACvB,OAAO,EAAE,UAAU,MAAM,UAAU,CAAC,CACpC,KAAK,MAAM,CACX,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,CAAC;AAEhC,MAAI,MAAM,SACR,OAAM,IAAI,YAAY,eAAe,EAAE,SAAS,oBAAoB,CAAC;EAGvE,MAAM,CAAC,UAAU,MAAM,MAAKA,GACzB,OAAO,MAAM,CACb,IAAI,MAAM,KAAK,CACf,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,CAAC,CAC7B,WAAW;AAEd,MAAI,CAAC,OACH,OAAM,IAAI,YAAY,yBAAyB,EAC7C,SAAS,kCACV,CAAC;AAGJ,SAAO;;;;;CAMT,MAAM,YAAY,OAAyB;EACzC,MAAM,QAAQ,MAAM,MAAKA,GACtB,OAAO;GAAE,IAAI,MAAM;GAAI,MAAM,MAAM;GAAM,UAAU,MAAM;GAAU,CAAC,CACpE,KAAK,MAAM,CACX,MAAM,QAAQ,MAAM,IAAI,MAAM,IAAI,CAAC;AAGtC,MAAI,MAAM,MAAM,SAAS,KAAK,SAAS,CACrC,OAAM,IAAI,YAAY,eAAe,EAAE,SAAS,sBAAsB,CAAC;EAIzE,MAAM,UAAU,MAAM,OAAO,SAAS,CAAC,KAAK,WAAW,OAAO,GAAG;EACjE,MAAM,QAAQ,MAAM,OAAO,OAAO,CAAC,KAAK,SAAS,KAAK,GAAG;EAGzD,MAAM,gBACJ,MAAM,SAAS,IACX,IAAI,qBAAqB;GACvB,QAAQ;GACR,QAAQ,EACN,SAAS,MAAM,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,EAC1C;GACF,CAAC,GACF;;;;AAKN,QAAM,MAAKA,GAAI,YAAY,OAAO,OAAO;AACvC,SAAM,GAAG,OAAO,MAAM,CAAC,MAAM,QAAQ,MAAM,IAAI,QAAQ,CAAC;AACxD,SAAM,GAAG,OAAO,MAAM,CAAC,MAAM,QAAQ,MAAM,IAAI,MAAM,CAAC;AAEtD,OAAI,cAAe,OAAM,MAAKC,KAAM,KAAK,cAAc;IACvD"}
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 ?? 0,\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 node by id\n */\n async getNodeById(id: string) {\n return this.#db.select().from(nodes).where(eq(nodes.id, id));\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.hidden != null ? eq(nodes.hidden, filters.hidden) : 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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"}