@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.
- package/dist/components/client.d.mts +2 -2
- package/dist/components/client.mjs +2 -2
- package/dist/components/common/icons.d.mts +52 -52
- package/dist/components/common/icons.d.mts.map +1 -1
- package/dist/components/common/status.d.mts +3 -3
- package/dist/components/common/status.d.mts.map +1 -1
- package/dist/components/editor/components/content.client.d.mts +2 -2
- package/dist/components/editor/components/editor.client.d.mts +5 -5
- package/dist/components/editor/components/editor.client.d.mts.map +1 -1
- package/dist/components/editor/components/{block-dropdown.mjs → menu-nodes.client.mjs} +7 -11
- package/dist/components/editor/components/menu-nodes.client.mjs.map +1 -0
- package/dist/components/editor/components/menu.client.d.mts +10 -0
- package/dist/components/editor/components/menu.client.d.mts.map +1 -0
- package/dist/components/editor/components/menu.client.mjs +166 -0
- package/dist/components/editor/components/menu.client.mjs.map +1 -0
- package/dist/components/editor/lib/extensions.d.mts +3 -0
- package/dist/components/editor/lib/extensions.d.mts.map +1 -1
- package/dist/components/editor/lib/extensions.mjs.map +1 -1
- package/dist/components/header/back-button.client.d.mts +2 -2
- package/dist/components/header/bottom-bar.client.d.mts +3 -3
- package/dist/components/header/breadcrumbs.client.d.mts +7 -7
- package/dist/components/header/header.client.d.mts +2 -2
- package/dist/components/header/mobile-nav-switcher.client.d.mts +2 -2
- package/dist/components/header/top-bar.client.d.mts +4 -4
- package/dist/components/layouts/admin-content.client.d.mts +2 -2
- package/dist/components/layouts/admin-layout.d.mts +2 -2
- package/dist/components/layouts/admin-layout.d.mts.map +1 -1
- package/dist/components/layouts/admin-loading.d.mts +2 -2
- package/dist/components/layouts/admin-loading.d.mts.map +1 -1
- package/dist/components/layouts/empty-page.d.mts +4 -4
- package/dist/components/layouts/empty-page.d.mts.map +1 -1
- package/dist/components/layouts/list-layout.d.mts +2 -2
- package/dist/components/layouts/list-layout.d.mts.map +1 -1
- package/dist/components/layouts/not-found-page.d.mts +3 -3
- package/dist/components/layouts/not-found-page.d.mts.map +1 -1
- package/dist/components/layouts/providers.client.d.mts +2 -2
- package/dist/components/layouts/root-layout.server.d.mts +2 -2
- package/dist/components/layouts/root-layout.server.d.mts.map +1 -1
- package/dist/components/layouts/root-loading.d.mts +2 -2
- package/dist/components/layouts/root-loading.d.mts.map +1 -1
- package/dist/components/layouts/tab-layout.d.mts +2 -2
- package/dist/components/layouts/tab-layout.d.mts.map +1 -1
- package/dist/components/lists/data-list.d.mts +5 -5
- package/dist/components/lists/data-list.d.mts.map +1 -1
- package/dist/components/lists/data-stack.d.mts +8 -8
- package/dist/components/lists/data-stack.d.mts.map +1 -1
- package/dist/components/navigation/admin-sidebar-paths.client.d.mts +10 -10
- package/dist/components/ui/accordion.d.mts +5 -5
- package/dist/components/ui/alert-dialog.d.mts +12 -12
- package/dist/components/ui/alert.d.mts +6 -6
- package/dist/components/ui/aspect-ratio.d.mts +2 -2
- package/dist/components/ui/avatar.client.d.mts +4 -4
- package/dist/components/ui/badge.d.mts +4 -4
- package/dist/components/ui/breadcrumb.d.mts +8 -8
- package/dist/components/ui/breadcrumb.d.mts.map +1 -1
- package/dist/components/ui/button.d.mts +5 -5
- package/dist/components/ui/button.d.mts.map +1 -1
- package/dist/components/ui/calendar.d.mts +3 -3
- package/dist/components/ui/calendar.d.mts.map +1 -1
- package/dist/components/ui/card.d.mts +7 -7
- package/dist/components/ui/card.d.mts.map +1 -1
- package/dist/components/ui/carousel.d.mts +6 -6
- package/dist/components/ui/carousel.d.mts.map +1 -1
- package/dist/components/ui/chart.client.d.mts +5 -5
- package/dist/components/ui/checkbox.d.mts +2 -2
- package/dist/components/ui/checkbox.d.mts.map +1 -1
- package/dist/components/ui/collapsible.client.d.mts +4 -4
- package/dist/components/ui/color-picker.client.d.mts +2 -2
- package/dist/components/ui/combobox-dropdown.client.d.mts +2 -2
- package/dist/components/ui/combobox.client.d.mts +2 -2
- package/dist/components/ui/command.d.mts +10 -10
- package/dist/components/ui/command.d.mts.map +1 -1
- package/dist/components/ui/context-menu.d.mts +16 -16
- package/dist/components/ui/date-picker.client.d.mts +2 -2
- package/dist/components/ui/dialog.client.d.mts +11 -11
- package/dist/components/ui/drawer.client.d.mts +11 -11
- package/dist/components/ui/dropdown-menu.d.mts +16 -16
- package/dist/components/ui/form.client.d.mts +7 -7
- package/dist/components/ui/hover-card.client.d.mts +4 -4
- package/dist/components/ui/input-recipient.d.mts +2 -2
- package/dist/components/ui/input.d.mts +2 -2
- package/dist/components/ui/label.d.mts +2 -2
- package/dist/components/ui/navigation-menu.d.mts +11 -11
- package/dist/components/ui/pagination.d.mts +8 -8
- package/dist/components/ui/popover.d.mts +5 -5
- package/dist/components/ui/progress.client.d.mts +2 -2
- package/dist/components/ui/radio-group.d.mts +3 -3
- package/dist/components/ui/resizable.client.d.mts +4 -4
- package/dist/components/ui/scroll-area.d.mts +3 -3
- package/dist/components/ui/select.client.d.mts +11 -11
- package/dist/components/ui/separator.d.mts +2 -2
- package/dist/components/ui/sheet.client.d.mts +9 -9
- package/dist/components/ui/sidebar.client.d.mts +24 -24
- package/dist/components/ui/skeleton.d.mts +2 -2
- package/dist/components/ui/slider.d.mts +2 -2
- package/dist/components/ui/sonner.client.d.mts +2 -2
- package/dist/components/ui/switch.d.mts +2 -2
- package/dist/components/ui/tabs.d.mts +5 -5
- package/dist/components/ui/textarea.d.mts +2 -2
- package/dist/components/ui/time-input.client.d.mts +2 -2
- package/dist/components/ui/toggle-group.client.d.mts +3 -3
- package/dist/components/ui/toggle.d.mts +2 -2
- package/dist/components/ui/tooltip.client.d.mts +5 -5
- package/dist/lib/hooks/use-action.d.mts +2 -2
- package/dist/lib/hooks/use-indicator.d.mts +2 -2
- package/dist/lib/hooks/use-indicator.d.mts.map +1 -1
- package/dist/modules/auth/components/allowed.client.d.mts +2 -2
- package/dist/modules/auth/components/auth-layout.server.d.mts +2 -2
- package/dist/modules/auth/components/auth-layout.server.d.mts.map +1 -1
- package/dist/modules/auth/components/auth-loading.d.mts +2 -2
- package/dist/modules/auth/components/create-first-user-page.client.d.mts +2 -2
- package/dist/modules/auth/components/forget-password-page.client.d.mts +2 -2
- package/dist/modules/auth/components/guard-first-user.server.d.mts +2 -2
- package/dist/modules/auth/components/guard-first-user.server.d.mts.map +1 -1
- package/dist/modules/auth/components/guard.server.d.mts +2 -2
- package/dist/modules/auth/components/guard.server.d.mts.map +1 -1
- package/dist/modules/auth/components/login-page.client.d.mts +2 -2
- package/dist/modules/auth/components/reset-password-page.client.d.mts +2 -2
- package/dist/modules/auth/components/update-password-command.d.mts +2 -2
- package/dist/modules/auth/db/schema.d.mts +73 -73
- package/dist/modules/auth/handler/client.client.d.mts +2 -2
- package/dist/modules/auth/hooks/use-permission.d.mts +2 -2
- package/dist/modules/auth/lib/validators.d.mts +2 -2
- package/dist/modules/auth/lib/validators.d.mts.map +1 -1
- package/dist/modules/commands/components/alert-dialog-command.client.d.mts +10 -10
- package/dist/modules/commands/components/alert-dialog-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/click-command.client.d.mts +2 -2
- package/dist/modules/commands/components/click-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/command-trigger.client.d.mts +6 -6
- package/dist/modules/commands/components/command-trigger.client.d.mts.map +1 -1
- package/dist/modules/commands/components/dialog-command.client.d.mts +8 -8
- package/dist/modules/commands/components/dialog-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/dropdown-command.client.d.mts +5 -5
- package/dist/modules/commands/components/dropdown-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/empty-command.client.d.mts +2 -2
- package/dist/modules/commands/components/empty-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/form-dialog-command.client.d.mts +11 -11
- package/dist/modules/commands/components/form-dialog-command.client.d.mts.map +1 -1
- package/dist/modules/commands/hooks/use-command-mutation.client.d.mts +2 -2
- package/dist/modules/commands/menus/context-menu.client.d.mts +2 -2
- package/dist/modules/commands/menus/context-menu.client.d.mts.map +1 -1
- package/dist/modules/commands/menus/dropdown-menu.client.d.mts +3 -3
- package/dist/modules/commands/menus/dropdown-menu.client.d.mts.map +1 -1
- package/dist/modules/commands/menus/inline-menu.client.d.mts +3 -3
- package/dist/modules/commands/menus/inline-menu.client.d.mts.map +1 -1
- package/dist/modules/commands/menus/responsive-menu.client.d.mts +3 -3
- package/dist/modules/commands/menus/responsive-menu.client.d.mts.map +1 -1
- package/dist/modules/commands/utils/archive-command.client.d.mts +3 -3
- package/dist/modules/commands/utils/archive-command.client.d.mts.map +1 -1
- package/dist/modules/commands/utils/delete-command.client.d.mts +3 -3
- package/dist/modules/commands/utils/delete-command.client.d.mts.map +1 -1
- package/dist/modules/config/db/helpers.d.mts +5 -5
- package/dist/modules/data-tables/components/cell/common.client.d.mts +5 -5
- package/dist/modules/data-tables/components/cell/common.client.d.mts.map +1 -1
- package/dist/modules/data-tables/components/column-header.d.mts +2 -2
- package/dist/modules/data-tables/components/filters/combobox.client.d.mts +2 -2
- package/dist/modules/data-tables/components/filters/combobox.client.d.mts.map +1 -1
- package/dist/modules/data-tables/components/filters/slider.client.d.mts +2 -2
- package/dist/modules/data-tables/components/filters/slider.client.d.mts.map +1 -1
- package/dist/modules/data-tables/components/header.d.mts +4 -4
- package/dist/modules/data-tables/components/layout.d.mts +2 -2
- package/dist/modules/data-tables/components/search-input.client.d.mts +2 -2
- package/dist/modules/data-tables/components/search-input.client.d.mts.map +1 -1
- package/dist/modules/data-tables/components/skeleton.d.mts +2 -2
- package/dist/modules/data-tables/components/table.d.mts +7 -7
- package/dist/modules/data-tables/components/toolbar.d.mts +3 -3
- package/dist/modules/data-tables/hooks/use-context.client.d.mts +2 -2
- package/dist/modules/data-tables/hooks/use-context.client.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/data-table/components/table.d.mts +2 -2
- package/dist/modules/data-tables/tables/data-table/components/table.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts +3 -3
- package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/table.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/components/table.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts +2 -2
- package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/combobox.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/combobox.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/date-input.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/date-input.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/date-picker.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/date-picker.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/editor.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/editor.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/input-recipient.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/input-recipient.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/input-toggle.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/input-toggle.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/input.client.d.mts +4 -4
- package/dist/modules/inline-edit/components/input.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/select.client.d.mts +6 -6
- package/dist/modules/inline-edit/components/select.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/switch.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/switch.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/toggle.client.d.mts +2 -2
- package/dist/modules/inline-edit/components/toggle.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/hooks/context.client.d.mts +2 -2
- package/dist/modules/inline-edit/hooks/context.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/lib/variants.d.mts +1 -1
- package/dist/modules/router/lib/query-client.server.d.mts +2 -2
- package/dist/modules/router/lib/query-client.server.d.mts.map +1 -1
- package/dist/modules/storage/components/dropzone.client.d.mts +5 -5
- package/dist/modules/storage/components/dropzone.client.d.mts.map +1 -1
- package/dist/modules/storage/components/image-grid.client.d.mts +3 -3
- package/dist/modules/storage/components/image-grid.client.d.mts.map +1 -1
- package/dist/modules/storage/components/upload-zone.client.d.mts +2 -2
- package/dist/modules/storage/components/upload-zone.client.d.mts.map +1 -1
- package/dist/modules/storage/lib/router.server.d.mts +1768 -1768
- package/dist/modules/storage/lib/router.server.d.mts.map +1 -1
- package/dist/modules/storage/lib/router.server.mjs +1 -0
- package/dist/modules/storage/lib/router.server.mjs.map +1 -1
- package/dist/modules/storage/lib/schema.d.mts +88 -88
- package/dist/modules/storage/lib/service.server.d.mts +21 -21
- package/dist/modules/storage/lib/service.server.d.mts.map +1 -1
- package/dist/modules/storage/lib/service.server.mjs +6 -5
- package/dist/modules/storage/lib/service.server.mjs.map +1 -1
- package/dist/modules/storage/lib/validators.d.mts +78 -78
- package/dist/modules/storage/lib/validators.d.mts.map +1 -1
- package/package.json +1 -1
- package/src/components/editor/components/{block-dropdown.tsx → menu-nodes.client.tsx} +24 -29
- package/src/components/editor/components/menu.client.tsx +214 -0
- package/src/components/editor/lib/extensions.ts +3 -3
- package/src/components/entry.client.ts +1 -1
- package/src/modules/storage/lib/router.server.ts +6 -0
- package/src/modules/storage/lib/service.server.ts +15 -2
- package/dist/components/editor/components/block-dropdown.mjs.map +0 -1
- package/dist/components/editor/components/menu-fixed.client.d.mts +0 -7
- package/dist/components/editor/components/menu-fixed.client.d.mts.map +0 -1
- package/dist/components/editor/components/menu-fixed.client.mjs +0 -128
- package/dist/components/editor/components/menu-fixed.client.mjs.map +0 -1
- 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
|
-
|
|
73
|
-
|
|
71
|
+
createdAt: Date;
|
|
72
|
+
updatedAt: Date;
|
|
74
73
|
name: string;
|
|
75
|
-
|
|
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
|
-
|
|
96
|
-
|
|
94
|
+
createdAt: Date;
|
|
95
|
+
updatedAt: Date;
|
|
97
96
|
name: string;
|
|
98
|
-
|
|
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
|
-
|
|
145
|
-
|
|
143
|
+
createdAt: Date;
|
|
144
|
+
updatedAt: Date;
|
|
146
145
|
name: string;
|
|
147
|
-
|
|
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;;;
|
|
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":";;;;;;;;;;;;;;;;;;;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"}
|