opacacms 0.1.12 → 0.1.13

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 (217) hide show
  1. package/dist/admin/index.js +0 -4
  2. package/dist/admin/webcomponent.d.ts +0 -1
  3. package/dist/admin/webcomponent.js +0 -4
  4. package/dist/admin.css +1 -0
  5. package/package.json +8 -2
  6. package/bun.lock +0 -34
  7. package/dist/admin/index.css +0 -47
  8. package/dist/admin/webcomponent.css +0 -47
  9. package/global.d.ts +0 -11
  10. package/src/admin/api-client.ts +0 -63
  11. package/src/admin/auth-client.ts +0 -40
  12. package/src/admin/custom-field.ts +0 -179
  13. package/src/admin/index.ts +0 -15
  14. package/src/admin/react.tsx +0 -72
  15. package/src/admin/router.ts +0 -9
  16. package/src/admin/stores/admin-queries.ts +0 -121
  17. package/src/admin/stores/auth.ts +0 -61
  18. package/src/admin/stores/column-visibility.ts +0 -67
  19. package/src/admin/stores/config.ts +0 -15
  20. package/src/admin/stores/media.ts +0 -95
  21. package/src/admin/stores/query.ts +0 -13
  22. package/src/admin/stores/ui.ts +0 -29
  23. package/src/admin/ui/admin-client.tsx +0 -283
  24. package/src/admin/ui/admin-layout.tsx +0 -276
  25. package/src/admin/ui/components/ColumnVisibilityToggle.tsx +0 -141
  26. package/src/admin/ui/components/DataDetailSheet.tsx +0 -141
  27. package/src/admin/ui/components/DataDetailView.tsx +0 -175
  28. package/src/admin/ui/components/Table.tsx +0 -67
  29. package/src/admin/ui/components/fields/ArrayField.tsx +0 -166
  30. package/src/admin/ui/components/fields/BlocksField.tsx +0 -202
  31. package/src/admin/ui/components/fields/BooleanField.tsx +0 -50
  32. package/src/admin/ui/components/fields/CollapsibleField.tsx +0 -75
  33. package/src/admin/ui/components/fields/DateField.tsx +0 -45
  34. package/src/admin/ui/components/fields/FileField.tsx +0 -322
  35. package/src/admin/ui/components/fields/GroupField.tsx +0 -50
  36. package/src/admin/ui/components/fields/JoinField.tsx +0 -23
  37. package/src/admin/ui/components/fields/NumberField.tsx +0 -46
  38. package/src/admin/ui/components/fields/RadioField.tsx +0 -62
  39. package/src/admin/ui/components/fields/RelationshipField.tsx +0 -278
  40. package/src/admin/ui/components/fields/RowField.tsx +0 -40
  41. package/src/admin/ui/components/fields/SelectField.tsx +0 -59
  42. package/src/admin/ui/components/fields/TabsField.tsx +0 -101
  43. package/src/admin/ui/components/fields/TextAreaField.tsx +0 -54
  44. package/src/admin/ui/components/fields/TextField.tsx +0 -49
  45. package/src/admin/ui/components/fields/VirtualField.tsx +0 -53
  46. package/src/admin/ui/components/fields/index.tsx +0 -371
  47. package/src/admin/ui/components/fields/richtext-editor/index.tsx +0 -211
  48. package/src/admin/ui/components/fields/richtext-editor/nodes/ImageComponent.tsx +0 -142
  49. package/src/admin/ui/components/fields/richtext-editor/nodes/ImageNode.tsx +0 -95
  50. package/src/admin/ui/components/fields/richtext-editor/plugins/ComponentPickerPlugin.tsx +0 -226
  51. package/src/admin/ui/components/fields/richtext-editor/plugins/EditableSyncPlugin.tsx +0 -16
  52. package/src/admin/ui/components/fields/richtext-editor/plugins/NotionToolbarPlugin.tsx +0 -184
  53. package/src/admin/ui/components/fields/richtext-editor/plugins/SimpleToolbarPlugin.tsx +0 -240
  54. package/src/admin/ui/components/fields/richtext-editor/plugins/ValueSyncPlugin.tsx +0 -40
  55. package/src/admin/ui/components/fields/utils.ts +0 -1
  56. package/src/admin/ui/components/link.tsx +0 -41
  57. package/src/admin/ui/components/media/AssetManagerModal.tsx +0 -334
  58. package/src/admin/ui/components/toast.tsx +0 -72
  59. package/src/admin/ui/components/ui/accordion.tsx +0 -51
  60. package/src/admin/ui/components/ui/alert-dialog.tsx +0 -98
  61. package/src/admin/ui/components/ui/blocks.tsx +0 -32
  62. package/src/admin/ui/components/ui/breadcrumbs.tsx +0 -59
  63. package/src/admin/ui/components/ui/button.tsx +0 -26
  64. package/src/admin/ui/components/ui/collapsible.tsx +0 -124
  65. package/src/admin/ui/components/ui/dialog.tsx +0 -79
  66. package/src/admin/ui/components/ui/group.tsx +0 -20
  67. package/src/admin/ui/components/ui/index.ts +0 -17
  68. package/src/admin/ui/components/ui/input.tsx +0 -12
  69. package/src/admin/ui/components/ui/join.tsx +0 -53
  70. package/src/admin/ui/components/ui/label.tsx +0 -11
  71. package/src/admin/ui/components/ui/radio-group.tsx +0 -75
  72. package/src/admin/ui/components/ui/relationship-detail-sheet.tsx +0 -122
  73. package/src/admin/ui/components/ui/relationship.tsx +0 -58
  74. package/src/admin/ui/components/ui/scroll-area.tsx +0 -19
  75. package/src/admin/ui/components/ui/select.tsx +0 -187
  76. package/src/admin/ui/components/ui/separator.tsx +0 -21
  77. package/src/admin/ui/components/ui/sheet.tsx +0 -106
  78. package/src/admin/ui/components/ui/tabs.tsx +0 -116
  79. package/src/admin/ui/components/ui/utils.ts +0 -3
  80. package/src/admin/ui/hooks/use-debounce.ts +0 -15
  81. package/src/admin/ui/styles/_locale-switcher.scss +0 -33
  82. package/src/admin/ui/styles/accordion.scss +0 -60
  83. package/src/admin/ui/styles/animations.scss +0 -41
  84. package/src/admin/ui/styles/asset-manager.scss +0 -547
  85. package/src/admin/ui/styles/badge.scss +0 -13
  86. package/src/admin/ui/styles/base.scss +0 -22
  87. package/src/admin/ui/styles/button.scss +0 -161
  88. package/src/admin/ui/styles/card.scss +0 -13
  89. package/src/admin/ui/styles/collapsible.scss +0 -75
  90. package/src/admin/ui/styles/data-detail.scss +0 -92
  91. package/src/admin/ui/styles/dialog.scss +0 -102
  92. package/src/admin/ui/styles/empty-state.scss +0 -22
  93. package/src/admin/ui/styles/group.scss +0 -19
  94. package/src/admin/ui/styles/index.scss +0 -33
  95. package/src/admin/ui/styles/input.scss +0 -80
  96. package/src/admin/ui/styles/label.scss +0 -12
  97. package/src/admin/ui/styles/layout.scss +0 -56
  98. package/src/admin/ui/styles/lexical.scss +0 -469
  99. package/src/admin/ui/styles/loading.scss +0 -102
  100. package/src/admin/ui/styles/media-registry.scss +0 -597
  101. package/src/admin/ui/styles/pagination.scss +0 -20
  102. package/src/admin/ui/styles/radio-group.scss +0 -66
  103. package/src/admin/ui/styles/row.scss +0 -17
  104. package/src/admin/ui/styles/scrollbar.scss +0 -36
  105. package/src/admin/ui/styles/select.scss +0 -121
  106. package/src/admin/ui/styles/separator.scss +0 -14
  107. package/src/admin/ui/styles/sheet.scss +0 -152
  108. package/src/admin/ui/styles/sidebar.scss +0 -148
  109. package/src/admin/ui/styles/switch.scss +0 -59
  110. package/src/admin/ui/styles/table.scss +0 -207
  111. package/src/admin/ui/styles/tabs.scss +0 -62
  112. package/src/admin/ui/styles/toast.scss +0 -45
  113. package/src/admin/ui/styles/variables.scss +0 -24
  114. package/src/admin/ui/views/collection-list-view.tsx +0 -720
  115. package/src/admin/ui/views/dashboard-view.tsx +0 -263
  116. package/src/admin/ui/views/document-edit-view.tsx +0 -384
  117. package/src/admin/ui/views/global-edit-view.tsx +0 -226
  118. package/src/admin/ui/views/init-view.tsx +0 -182
  119. package/src/admin/ui/views/login-view.tsx +0 -123
  120. package/src/admin/ui/views/media-registry-view.tsx +0 -1104
  121. package/src/admin/ui/views/settings-view.tsx +0 -729
  122. package/src/admin/webcomponent.tsx +0 -15
  123. package/src/auth/index.ts +0 -194
  124. package/src/auth/migrations.ts +0 -87
  125. package/src/auth/premissions.ts +0 -46
  126. package/src/cli/commands/generate-types.ts +0 -116
  127. package/src/cli/commands/init.ts +0 -95
  128. package/src/cli/commands/migrate-commands.ts +0 -160
  129. package/src/cli/commands/seed-command.ts +0 -11
  130. package/src/cli/d1-mock.ts +0 -101
  131. package/src/cli/index.test.ts +0 -84
  132. package/src/cli/index.ts +0 -183
  133. package/src/cli/r2-mock.ts +0 -217
  134. package/src/cli/seeding.ts +0 -409
  135. package/src/client.ts +0 -181
  136. package/src/config-utils.ts +0 -102
  137. package/src/config.ts +0 -49
  138. package/src/db/adapter.ts +0 -53
  139. package/src/db/better-sqlite.ts +0 -657
  140. package/src/db/bun-sqlite.ts +0 -666
  141. package/src/db/d1.ts +0 -721
  142. package/src/db/index.ts +0 -10
  143. package/src/db/kysely/data-mapper.ts +0 -142
  144. package/src/db/kysely/field-mapper.ts +0 -149
  145. package/src/db/kysely/migration-generator.ts +0 -223
  146. package/src/db/kysely/query-builder.ts +0 -92
  147. package/src/db/kysely/schema-builder.ts +0 -439
  148. package/src/db/kysely/sql-utils.ts +0 -13
  149. package/src/db/postgres.ts +0 -631
  150. package/src/db/sqlite.ts +0 -670
  151. package/src/db/system-schema.ts +0 -121
  152. package/src/index.ts +0 -13
  153. package/src/runtimes/README.md +0 -59
  154. package/src/runtimes/bun.ts +0 -49
  155. package/src/runtimes/cloudflare-workers.ts +0 -38
  156. package/src/runtimes/next.ts +0 -26
  157. package/src/runtimes/node.ts +0 -52
  158. package/src/schema/collection.ts +0 -184
  159. package/src/schema/fields/base.ts +0 -164
  160. package/src/schema/fields/index.ts +0 -427
  161. package/src/schema/global.ts +0 -145
  162. package/src/schema/index.ts +0 -4
  163. package/src/schema/infer.ts +0 -72
  164. package/src/server/admin-router.ts +0 -20
  165. package/src/server/admin.ts +0 -142
  166. package/src/server/assets.ts +0 -306
  167. package/src/server/collection-router.ts +0 -55
  168. package/src/server/handlers.ts +0 -722
  169. package/src/server/middlewares/admin.ts +0 -27
  170. package/src/server/middlewares/auth.ts +0 -89
  171. package/src/server/middlewares/context.ts +0 -17
  172. package/src/server/middlewares/cors.ts +0 -24
  173. package/src/server/middlewares/database-init.ts +0 -74
  174. package/src/server/middlewares/rate-limit.ts +0 -77
  175. package/src/server/router.ts +0 -47
  176. package/src/server/setup-middlewares.ts +0 -58
  177. package/src/server/system-router.ts +0 -35
  178. package/src/server.ts +0 -9
  179. package/src/storage/adapters/cloudflare-r2.ts +0 -136
  180. package/src/storage/adapters/local.ts +0 -146
  181. package/src/storage/adapters/s3.ts +0 -186
  182. package/src/storage/errors.ts +0 -46
  183. package/src/storage/index.ts +0 -5
  184. package/src/storage/types.ts +0 -39
  185. package/src/types.ts +0 -577
  186. package/src/utils/lexical.ts +0 -37
  187. package/src/utils/logger.ts +0 -73
  188. package/src/validation.ts +0 -429
  189. package/src/validator.ts +0 -179
  190. package/test/admin-custom-field.test.ts +0 -162
  191. package/test/admin-react-field.test.tsx +0 -134
  192. package/test/api-features.test.ts +0 -78
  193. package/test/api.test.ts +0 -178
  194. package/test/auth.test.ts +0 -62
  195. package/test/cli-integration.test.ts +0 -148
  196. package/test/cli.test.ts +0 -25
  197. package/test/db/postgres.test.ts +0 -95
  198. package/test/db/sqlite-filter.test.ts +0 -53
  199. package/test/db/sqlite.test.ts +0 -82
  200. package/test/engine-features.test.ts +0 -79
  201. package/test/globals.test.ts +0 -74
  202. package/test/integration-tmp/db-app/opacacms.config.ts +0 -15
  203. package/test/integration-tmp/my-sqlite-app/opacacms.config.ts +0 -25
  204. package/test/integration-tmp/my-test-app/index.ts +0 -8
  205. package/test/integration-tmp/my-test-app/opacacms.config.ts +0 -16
  206. package/test/integration-tmp/my-test-app/package.json +0 -12
  207. package/test/populate.test.ts +0 -79
  208. package/test/runtimes.test.ts +0 -43
  209. package/test/schema-builder.test.ts +0 -107
  210. package/test/schema-features.test.ts +0 -63
  211. package/test/seeding.test.ts +0 -68
  212. package/test/storage/local.test.ts +0 -72
  213. package/test/storage/s3.test.ts +0 -60
  214. package/test/structural-data.test.ts +0 -100
  215. package/test/test-setup.ts +0 -11
  216. package/test/validation.test.ts +0 -162
  217. package/tsconfig.json +0 -42
@@ -1,146 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { FileTooLargeError, Invalidmime_typeError, StorageError } from "../errors";
4
- import type {
5
- FileData,
6
- FileRecord,
7
- StorageAdapter,
8
- StorageAdapterConfig,
9
- UploadOptions,
10
- } from "../types";
11
-
12
- export interface LocalAdapterConfig extends StorageAdapterConfig {
13
- uploadDir: string;
14
- publicUrl: string;
15
- }
16
-
17
- export function createLocalAdapter(config: LocalAdapterConfig): StorageAdapter {
18
- fs.mkdir(config.uploadDir, { recursive: true }).catch((err) => {
19
- console.error(
20
- `[OpacaCMS:LocalAdapter] Failed to create upload directory ${config.uploadDir}:`,
21
- err,
22
- );
23
- });
24
-
25
- const getFullPath = (filename: string) => {
26
- return path.join(config.uploadDir, filename);
27
- };
28
-
29
- const getPublicUrl = (filename: string) => {
30
- const base = config.publicUrl.replace(/\/$/, "");
31
- return `${base}/${filename}`;
32
- };
33
-
34
- return {
35
- name: "local",
36
-
37
- async upload(file: FileRecord, options?: UploadOptions): Promise<FileData> {
38
- try {
39
- if (options?.allowedmime_types && !options.allowedmime_types.includes(file.mime_type)) {
40
- throw new Invalidmime_typeError("local", options.allowedmime_types, file.mime_type);
41
- }
42
-
43
- if (options?.maxFileSize && file.filesize > options.maxFileSize) {
44
- throw new FileTooLargeError("local", options.maxFileSize, file.filesize);
45
- }
46
-
47
- let finalFilename = file.filename;
48
- if (options?.generateUniqueName) {
49
- const ext = path.extname(file.original_filename);
50
- const hash = crypto.randomUUID().split("-")[0];
51
- const base = path
52
- .basename(finalFilename, ext)
53
- .replace(/[^a-z0-9]/gi, "-")
54
- .toLowerCase();
55
- finalFilename = `${base}-${hash}${ext}`;
56
- }
57
-
58
- // Sanitization against path traversal
59
- const sanitizedFilename = path.basename(finalFilename);
60
- const fullPath = path.resolve(config.uploadDir, sanitizedFilename);
61
-
62
- if (!fullPath.startsWith(path.resolve(config.uploadDir))) {
63
- throw new StorageError("local", "upload", "Path traversal attempt detected.");
64
- }
65
-
66
- if (file.buffer && typeof Bun !== "undefined") {
67
- await Bun.write(fullPath, file.buffer);
68
- } else if (file.buffer) {
69
- await fs.writeFile(fullPath, file.buffer);
70
- } else if (file.stream) {
71
- const writeStream = require("node:fs").createWriteStream(fullPath);
72
- const reader = file.stream.getReader();
73
-
74
- try {
75
- while (true) {
76
- const { done, value } = await reader.read();
77
- if (done) break;
78
- if (value) {
79
- writeStream.write(Buffer.from(value));
80
- }
81
- }
82
- } finally {
83
- writeStream.end();
84
- reader.releaseLock();
85
- }
86
- } else {
87
- throw new Error("Neither Buffer nor Stream provided.");
88
- }
89
-
90
- return {
91
- filename: sanitizedFilename,
92
- mime_type: file.mime_type,
93
- filesize: file.filesize,
94
- url: getPublicUrl(sanitizedFilename),
95
- };
96
- } catch (error) {
97
- if (error instanceof StorageError) throw error;
98
- throw new StorageError(
99
- "local",
100
- "upload",
101
- `Failed to write ${file.filename} to disk`,
102
- error,
103
- );
104
- }
105
- },
106
-
107
- async delete(filename: string): Promise<void> {
108
- try {
109
- const fullPath = getFullPath(path.basename(filename));
110
- await fs.unlink(fullPath);
111
- } catch (error: any) {
112
- if (error.code !== "ENOENT") {
113
- throw new StorageError("local", "delete", `Failed to delete ${filename}`, error);
114
- }
115
- }
116
- },
117
-
118
- async exists(filename: string): Promise<boolean> {
119
- try {
120
- await fs.access(getFullPath(path.basename(filename)), require("node:fs").constants.F_OK);
121
- return true;
122
- } catch {
123
- return false;
124
- }
125
- },
126
-
127
- async download(filename: string): Promise<ReadableStream<Uint8Array>> {
128
- const fullPath = getFullPath(path.basename(filename));
129
- if (typeof Bun !== "undefined") {
130
- return Bun.file(fullPath).stream();
131
- }
132
-
133
- const nodeStream = require("node:fs").createReadStream(fullPath);
134
- return new ReadableStream({
135
- start(controller) {
136
- nodeStream.on("data", (chunk: Buffer) => controller.enqueue(new Uint8Array(chunk)));
137
- nodeStream.on("end", () => controller.close());
138
- nodeStream.on("error", (err: Error) => controller.error(err));
139
- },
140
- cancel() {
141
- nodeStream.destroy();
142
- },
143
- });
144
- },
145
- };
146
- }
@@ -1,186 +0,0 @@
1
- import {
2
- DeleteObjectCommand,
3
- GetObjectCommand,
4
- HeadObjectCommand,
5
- PutObjectCommand,
6
- S3Client,
7
- } from "@aws-sdk/client-s3";
8
- import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
9
- import { FileTooLargeError, Invalidmime_typeError, StorageError } from "../errors";
10
- import type {
11
- FileData,
12
- FileRecord,
13
- StorageAdapter,
14
- StorageAdapterConfig,
15
- UploadOptions,
16
- } from "../types";
17
-
18
- export interface S3AdapterConfig extends StorageAdapterConfig {
19
- region: string;
20
- bucket: string;
21
- endpoint?: string;
22
- credentials?: {
23
- accessKeyId: string;
24
- secretAccessKey: string;
25
- };
26
- forcePathStyle?: boolean;
27
- acl?: "private" | "public-read";
28
- }
29
-
30
- export function createS3Adapter(config: S3AdapterConfig): StorageAdapter {
31
- const s3Client = new S3Client({
32
- region: config.region,
33
- endpoint: config.endpoint,
34
- credentials: config.credentials,
35
- forcePathStyle: config.forcePathStyle,
36
- });
37
-
38
- const getFullKey = (filename: string) => {
39
- if (config.prefix) {
40
- const p = config.prefix.replace(/\/$/, "");
41
- return `${p}/${filename}`;
42
- }
43
- return filename;
44
- };
45
-
46
- const getPublicUrl = (filename: string) => {
47
- if (config.publicUrl) {
48
- const base = config.publicUrl.replace(/\/$/, "");
49
- return `${base}/${getFullKey(filename)}`;
50
- }
51
- return `https://${config.bucket}.s3.${config.region}.amazonaws.com/${getFullKey(filename)}`;
52
- };
53
-
54
- return {
55
- name: "s3",
56
- async upload(file: FileRecord, options?: UploadOptions): Promise<FileData> {
57
- try {
58
- if (options?.allowedmime_types && !options.allowedmime_types.includes(file.mime_type)) {
59
- throw new Invalidmime_typeError("s3", options.allowedmime_types, file.mime_type);
60
- }
61
-
62
- if (options?.maxFileSize && file.filesize > options.maxFileSize) {
63
- throw new FileTooLargeError("s3", options.maxFileSize, file.filesize);
64
- }
65
-
66
- let finalFilename = file.filename;
67
- if (options?.generateUniqueName) {
68
- const ext = file.original_filename.split(".").pop() || "";
69
- const hash = crypto.randomUUID().split("-")[0];
70
- const base = finalFilename
71
- .replace(`.${ext}`, "")
72
- .replace(/[^a-z0-9]/gi, "-")
73
- .toLowerCase();
74
- finalFilename = `${base}-${hash}.${ext}`;
75
- }
76
-
77
- const key = getFullKey(finalFilename);
78
-
79
- const body = file.buffer || file.stream;
80
-
81
- const command = new PutObjectCommand({
82
- Bucket: config.bucket,
83
- Key: key,
84
- Body: body as any, // Stream/Buffer compatibility
85
- ContentType: file.mime_type,
86
- ContentLength: file.filesize,
87
- ACL: config.acl || "private",
88
- });
89
-
90
- await s3Client.send(command);
91
-
92
- return {
93
- filename: finalFilename,
94
- mime_type: file.mime_type,
95
- filesize: file.filesize,
96
- url: getPublicUrl(finalFilename),
97
- };
98
- } catch (error) {
99
- if (error instanceof StorageError) throw error;
100
- throw new StorageError(
101
- "s3",
102
- "upload",
103
- `Failed to upload ${file.filename} to S3 bucket ${config.bucket}`,
104
- error,
105
- );
106
- }
107
- },
108
-
109
- async delete(filename: string): Promise<void> {
110
- try {
111
- const command = new DeleteObjectCommand({
112
- Bucket: config.bucket,
113
- Key: getFullKey(filename),
114
- });
115
- await s3Client.send(command);
116
- } catch (error) {
117
- throw new StorageError(
118
- "s3",
119
- "delete",
120
- `Failed to delete ${filename} from S3 bucket ${config.bucket}`,
121
- error,
122
- );
123
- }
124
- },
125
-
126
- async exists(filename: string): Promise<boolean> {
127
- try {
128
- const command = new HeadObjectCommand({
129
- Bucket: config.bucket,
130
- Key: getFullKey(filename),
131
- });
132
- await s3Client.send(command);
133
- return true;
134
- } catch (error: any) {
135
- if (error.name === "NotFound" || error.$metadata?.httpStatusCode === 404) return false;
136
- throw new StorageError("s3", "download", `Failed to check existence of ${filename}`, error);
137
- }
138
- },
139
-
140
- async generatePresignedUrl(
141
- filename: string,
142
- operation: "read" | "write",
143
- expiresInSeconds = 3600,
144
- ): Promise<string> {
145
- try {
146
- let command;
147
- if (operation === "write") {
148
- command = new PutObjectCommand({ Bucket: config.bucket, Key: getFullKey(filename) });
149
- } else {
150
- command = new GetObjectCommand({ Bucket: config.bucket, Key: getFullKey(filename) });
151
- }
152
-
153
- return await getSignedUrl(s3Client, command, { expiresIn: expiresInSeconds });
154
- } catch (error) {
155
- throw new StorageError(
156
- "s3",
157
- "presign",
158
- `Failed to generate presigned URL for ${filename}`,
159
- error,
160
- );
161
- }
162
- },
163
-
164
- async download(filename: string): Promise<ReadableStream<Uint8Array>> {
165
- try {
166
- const command = new GetObjectCommand({
167
- Bucket: config.bucket,
168
- Key: getFullKey(filename),
169
- });
170
- const response = await s3Client.send(command);
171
- if (!response.Body) {
172
- throw new StorageError("s3", "download", `File not found: ${filename}`);
173
- }
174
- return response.Body.transformToWebStream() as ReadableStream<Uint8Array>;
175
- } catch (error) {
176
- if (error instanceof StorageError) throw error;
177
- throw new StorageError(
178
- "s3",
179
- "download",
180
- `Failed to download ${filename} from S3 bucket ${config.bucket}`,
181
- error,
182
- );
183
- }
184
- },
185
- };
186
- }
@@ -1,46 +0,0 @@
1
- export class StorageError extends Error {
2
- public provider: string;
3
- public operation: "upload" | "delete" | "download" | "presign" | "exists";
4
- public originalError: unknown;
5
-
6
- constructor(
7
- provider: string,
8
- operation: "upload" | "delete" | "download" | "presign" | "exists",
9
- message: string,
10
- originalError?: unknown,
11
- ) {
12
- const originalMessage =
13
- originalError instanceof Error
14
- ? `: ${originalError.message}`
15
- : originalError
16
- ? `: ${String(originalError)}`
17
- : "";
18
- super(`[OpacaStorage::${provider}] Failed to ${operation}: ${message}${originalMessage}`);
19
- this.name = "StorageError";
20
- this.provider = provider;
21
- this.operation = operation;
22
- this.originalError = originalError;
23
- }
24
- }
25
-
26
- export class FileTooLargeError extends StorageError {
27
- constructor(provider: string, maxBytes: number, actualBytes: number) {
28
- super(
29
- provider,
30
- "upload",
31
- `File size (${actualBytes} bytes) exceeds the maximum allowed (${maxBytes} bytes).`,
32
- );
33
- this.name = "FileTooLargeError";
34
- }
35
- }
36
-
37
- export class Invalidmime_typeError extends StorageError {
38
- constructor(provider: string, allowedTypes: string[], actualType: string) {
39
- super(
40
- provider,
41
- "upload",
42
- `MIME type '${actualType}' is not allowed. Expected one of: ${allowedTypes.join(", ")}.`,
43
- );
44
- this.name = "Invalidmime_typeError";
45
- }
46
- }
@@ -1,5 +0,0 @@
1
- export * from "./adapters/cloudflare-r2";
2
- export * from "./adapters/local";
3
- export * from "./adapters/s3";
4
- export * from "./errors";
5
- export * from "./types";
@@ -1,39 +0,0 @@
1
- export interface StorageAdapterConfig {
2
- prefix?: string;
3
- publicUrl?: string;
4
- }
5
-
6
- export interface FileRecord {
7
- filename: string;
8
- original_filename: string;
9
- mime_type: string;
10
- filesize: number;
11
- buffer?: Uint8Array;
12
- stream?: ReadableStream<Uint8Array>;
13
- }
14
-
15
- export interface UploadOptions {
16
- allowedmime_types?: string[];
17
- maxFileSize?: number;
18
- generateUniqueName?: boolean;
19
- }
20
-
21
- export interface FileData {
22
- filename: string;
23
- url: string;
24
- filesize: number;
25
- mime_type: string;
26
- }
27
-
28
- export interface StorageAdapter {
29
- name: string;
30
- upload(file: FileRecord, options?: UploadOptions): Promise<FileData>;
31
- delete(filename: string): Promise<void>;
32
- exists?(filename: string): Promise<boolean>;
33
- download?(filename: string): Promise<ReadableStream<Uint8Array>>;
34
- generatePresignedUrl?(
35
- filename: string,
36
- operation: "read" | "write",
37
- expiresInSeconds?: number,
38
- ): Promise<string>;
39
- }