@edkstack/files 0.1.10 → 0.1.12
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/chunk-DQk6qfdC.mjs +18 -0
- package/dist/client/index.d.mts +3 -3
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/server/index.d.mts +2 -0
- package/dist/server/index.mjs +3 -0
- package/dist/server-Cxx9ToSf.d.mts +308 -0
- package/dist/{backend-BOAJlrA-.mjs → server-D1-_uiJo.mjs} +42 -45
- package/dist/server-D1-_uiJo.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/backend/index.d.mts +0 -2
- package/dist/backend/index.mjs +0 -3
- package/dist/backend-BOAJlrA-.mjs.map +0 -1
- package/dist/backend-CNgPTn_D.d.mts +0 -312
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (!no_symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { __exportAll as t };
|
package/dist/client/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import "../
|
|
1
|
+
import { n as createFilesServer } from "../server-Cxx9ToSf.mjs";
|
|
2
|
+
import "../server/index.mjs";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
import * as _elysiajs_eden0 from "@elysiajs/eden";
|
|
@@ -8,7 +8,7 @@ import * as _tanstack_react_query0 from "@tanstack/react-query";
|
|
|
8
8
|
import { InferMutationOptions } from "eden2query";
|
|
9
9
|
|
|
10
10
|
//#region src/client/client.d.ts
|
|
11
|
-
declare function createFilesClient<
|
|
11
|
+
declare function createFilesClient<TServer extends ReturnType<typeof createFilesServer<any>>>(domain: string, config?: Treaty.Config): Treaty.Create<TServer["router"]>;
|
|
12
12
|
type FilesClient = ReturnType<typeof createFilesClient>;
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/client/provider.d.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/client/provider.tsx","../../src/client/client.ts","../../src/client/hooks.ts"],"sourcesContent":["import { createContext, useContext, type ReactNode } from \"react\";\r\nimport type { FilesClient } from \"./client\";\r\n\r\n\r\nconst FilesClientContext = createContext<FilesClient | null>(null);\r\n\r\nexport function FilesClientProvider(props: {\r\n filesClient: FilesClient;\r\n children: ReactNode;\r\n}) {\r\n return (\r\n <FilesClientContext.Provider value={props.filesClient}>\r\n {props.children}\r\n </FilesClientContext.Provider>\r\n );\r\n}\r\n\r\nexport function useFilesClient() {\r\n const context = useContext(FilesClientContext);\r\n if (!context) {\r\n throw new Error(\"useFilesClient must be used within a FilesClientProvider\");\r\n }\r\n return context;\r\n}\r\n","import { treaty, type Treaty } from \"@elysiajs/eden\";\r\nimport type {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/client/provider.tsx","../../src/client/client.ts","../../src/client/hooks.ts"],"sourcesContent":["import { createContext, useContext, type ReactNode } from \"react\";\r\nimport type { FilesClient } from \"./client\";\r\n\r\n\r\nconst FilesClientContext = createContext<FilesClient | null>(null);\r\n\r\nexport function FilesClientProvider(props: {\r\n filesClient: FilesClient;\r\n children: ReactNode;\r\n}) {\r\n return (\r\n <FilesClientContext.Provider value={props.filesClient}>\r\n {props.children}\r\n </FilesClientContext.Provider>\r\n );\r\n}\r\n\r\nexport function useFilesClient() {\r\n const context = useContext(FilesClientContext);\r\n if (!context) {\r\n throw new Error(\"useFilesClient must be used within a FilesClientProvider\");\r\n }\r\n return context;\r\n}\r\n","import { treaty, type Treaty } from \"@elysiajs/eden\";\r\nimport type { createFilesServer } from \"../server\";\r\n\r\nexport function createFilesClient<\r\n TServer extends ReturnType<typeof createFilesServer<any>>\r\n>(\r\n domain: string,\r\n config?: Treaty.Config\r\n): Treaty.Create<TServer[\"router\"]> {\r\n return treaty<TServer[\"router\"]>(domain, config);\r\n}\r\n\r\nexport type FilesClient = ReturnType<typeof createFilesClient>;","import { useMutation, type UseMutationOptions } from \"@tanstack/react-query\";\r\nimport type { FilesClient } from \"./client\";\r\nimport { edenMutationOptions, type InferMutationOptions } from \"eden2query\";\r\nimport { useFilesClient } from \"./provider\";\r\n\r\nexport function useUpload(\r\n options: InferMutationOptions<FilesClient[\"api\"][\"files\"][\"upload\"][\"post\"]>\r\n) {\r\n const filesClient = useFilesClient() ;\r\n return useMutation({\r\n ...options,\r\n ...edenMutationOptions(\r\n filesClient.api.files.upload.post,\r\n ),\r\n });\r\n}"],"mappings":";;;;;;;AAIA,MAAM,qBAAqB,cAAkC,KAAK;AAElE,SAAgB,oBAAoB,OAGjC;AACD,QACE,oBAAC,mBAAmB;EAAS,OAAO,MAAM;YACvC,MAAM;GACqB;;AAIlC,SAAgB,iBAAiB;CAC/B,MAAM,UAAU,WAAW,mBAAmB;AAC9C,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,2DAA2D;AAE7E,QAAO;;;;;ACnBT,SAAgB,kBAGd,QACA,QACkC;AAClC,QAAO,OAA0B,QAAQ,OAAO;;;;;ACJlD,SAAgB,UACd,SACA;CACA,MAAM,cAAc,gBAAgB;AACpC,QAAO,YAAY;EACjB,GAAG;EACH,GAAG,oBACD,YAAY,IAAI,MAAM,OAAO,KAC9B;EACF,CAAC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import "./
|
|
3
|
-
export {
|
|
1
|
+
import { a as schema_d_exports, i as Visibility, n as createFilesServer, r as PurposePolicy, t as FilesServerOptions } from "./server-Cxx9ToSf.mjs";
|
|
2
|
+
import "./server/index.mjs";
|
|
3
|
+
export { FilesServerOptions, PurposePolicy, Visibility, createFilesServer, schema_d_exports as schema };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import "./
|
|
1
|
+
import { n as schema_exports, t as createFilesServer } from "./server-D1-_uiJo.mjs";
|
|
2
|
+
import "./server/index.mjs";
|
|
3
3
|
|
|
4
|
-
export {
|
|
4
|
+
export { createFilesServer, schema_exports as schema };
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
|
|
2
|
+
import { PgDatabase } from "drizzle-orm/pg-core";
|
|
3
|
+
import { Elysia } from "elysia";
|
|
4
|
+
import { S3Options } from "bun";
|
|
5
|
+
|
|
6
|
+
//#region src/server/schema.d.ts
|
|
7
|
+
declare namespace schema_d_exports {
|
|
8
|
+
export { files };
|
|
9
|
+
}
|
|
10
|
+
declare const files: drizzle_orm_pg_core0.PgTableWithColumns<{
|
|
11
|
+
name: "files";
|
|
12
|
+
schema: undefined;
|
|
13
|
+
columns: {
|
|
14
|
+
id: drizzle_orm_pg_core0.PgColumn<{
|
|
15
|
+
name: "id";
|
|
16
|
+
tableName: "files";
|
|
17
|
+
dataType: "string";
|
|
18
|
+
columnType: "PgText";
|
|
19
|
+
data: string;
|
|
20
|
+
driverParam: string;
|
|
21
|
+
notNull: true;
|
|
22
|
+
hasDefault: true;
|
|
23
|
+
isPrimaryKey: true;
|
|
24
|
+
isAutoincrement: false;
|
|
25
|
+
hasRuntimeDefault: true;
|
|
26
|
+
enumValues: [string, ...string[]];
|
|
27
|
+
baseColumn: never;
|
|
28
|
+
identity: undefined;
|
|
29
|
+
generated: undefined;
|
|
30
|
+
}, {}, {}>;
|
|
31
|
+
purpose: drizzle_orm_pg_core0.PgColumn<{
|
|
32
|
+
name: "purpose";
|
|
33
|
+
tableName: "files";
|
|
34
|
+
dataType: "string";
|
|
35
|
+
columnType: "PgText";
|
|
36
|
+
data: string;
|
|
37
|
+
driverParam: string;
|
|
38
|
+
notNull: true;
|
|
39
|
+
hasDefault: false;
|
|
40
|
+
isPrimaryKey: false;
|
|
41
|
+
isAutoincrement: false;
|
|
42
|
+
hasRuntimeDefault: false;
|
|
43
|
+
enumValues: [string, ...string[]];
|
|
44
|
+
baseColumn: never;
|
|
45
|
+
identity: undefined;
|
|
46
|
+
generated: undefined;
|
|
47
|
+
}, {}, {}>;
|
|
48
|
+
name: drizzle_orm_pg_core0.PgColumn<{
|
|
49
|
+
name: "name";
|
|
50
|
+
tableName: "files";
|
|
51
|
+
dataType: "string";
|
|
52
|
+
columnType: "PgText";
|
|
53
|
+
data: string;
|
|
54
|
+
driverParam: string;
|
|
55
|
+
notNull: false;
|
|
56
|
+
hasDefault: false;
|
|
57
|
+
isPrimaryKey: false;
|
|
58
|
+
isAutoincrement: false;
|
|
59
|
+
hasRuntimeDefault: false;
|
|
60
|
+
enumValues: [string, ...string[]];
|
|
61
|
+
baseColumn: never;
|
|
62
|
+
identity: undefined;
|
|
63
|
+
generated: undefined;
|
|
64
|
+
}, {}, {}>;
|
|
65
|
+
key: drizzle_orm_pg_core0.PgColumn<{
|
|
66
|
+
name: "key";
|
|
67
|
+
tableName: "files";
|
|
68
|
+
dataType: "string";
|
|
69
|
+
columnType: "PgText";
|
|
70
|
+
data: string;
|
|
71
|
+
driverParam: string;
|
|
72
|
+
notNull: true;
|
|
73
|
+
hasDefault: false;
|
|
74
|
+
isPrimaryKey: false;
|
|
75
|
+
isAutoincrement: false;
|
|
76
|
+
hasRuntimeDefault: false;
|
|
77
|
+
enumValues: [string, ...string[]];
|
|
78
|
+
baseColumn: never;
|
|
79
|
+
identity: undefined;
|
|
80
|
+
generated: undefined;
|
|
81
|
+
}, {}, {}>;
|
|
82
|
+
size: drizzle_orm_pg_core0.PgColumn<{
|
|
83
|
+
name: "size";
|
|
84
|
+
tableName: "files";
|
|
85
|
+
dataType: "number";
|
|
86
|
+
columnType: "PgInteger";
|
|
87
|
+
data: number;
|
|
88
|
+
driverParam: string | number;
|
|
89
|
+
notNull: true;
|
|
90
|
+
hasDefault: false;
|
|
91
|
+
isPrimaryKey: false;
|
|
92
|
+
isAutoincrement: false;
|
|
93
|
+
hasRuntimeDefault: false;
|
|
94
|
+
enumValues: undefined;
|
|
95
|
+
baseColumn: never;
|
|
96
|
+
identity: undefined;
|
|
97
|
+
generated: undefined;
|
|
98
|
+
}, {}, {}>;
|
|
99
|
+
mimeType: drizzle_orm_pg_core0.PgColumn<{
|
|
100
|
+
name: "mime_type";
|
|
101
|
+
tableName: "files";
|
|
102
|
+
dataType: "string";
|
|
103
|
+
columnType: "PgText";
|
|
104
|
+
data: string;
|
|
105
|
+
driverParam: string;
|
|
106
|
+
notNull: true;
|
|
107
|
+
hasDefault: true;
|
|
108
|
+
isPrimaryKey: false;
|
|
109
|
+
isAutoincrement: false;
|
|
110
|
+
hasRuntimeDefault: false;
|
|
111
|
+
enumValues: [string, ...string[]];
|
|
112
|
+
baseColumn: never;
|
|
113
|
+
identity: undefined;
|
|
114
|
+
generated: undefined;
|
|
115
|
+
}, {}, {}>;
|
|
116
|
+
refCount: drizzle_orm_pg_core0.PgColumn<{
|
|
117
|
+
name: "ref_count";
|
|
118
|
+
tableName: "files";
|
|
119
|
+
dataType: "number";
|
|
120
|
+
columnType: "PgInteger";
|
|
121
|
+
data: number;
|
|
122
|
+
driverParam: string | number;
|
|
123
|
+
notNull: true;
|
|
124
|
+
hasDefault: true;
|
|
125
|
+
isPrimaryKey: false;
|
|
126
|
+
isAutoincrement: false;
|
|
127
|
+
hasRuntimeDefault: false;
|
|
128
|
+
enumValues: undefined;
|
|
129
|
+
baseColumn: never;
|
|
130
|
+
identity: undefined;
|
|
131
|
+
generated: undefined;
|
|
132
|
+
}, {}, {}>;
|
|
133
|
+
visibility: drizzle_orm_pg_core0.PgColumn<{
|
|
134
|
+
name: "visibility";
|
|
135
|
+
tableName: "files";
|
|
136
|
+
dataType: "string";
|
|
137
|
+
columnType: "PgEnumColumn";
|
|
138
|
+
data: "private" | "public";
|
|
139
|
+
driverParam: string;
|
|
140
|
+
notNull: true;
|
|
141
|
+
hasDefault: true;
|
|
142
|
+
isPrimaryKey: false;
|
|
143
|
+
isAutoincrement: false;
|
|
144
|
+
hasRuntimeDefault: false;
|
|
145
|
+
enumValues: ["private", "public"];
|
|
146
|
+
baseColumn: never;
|
|
147
|
+
identity: undefined;
|
|
148
|
+
generated: undefined;
|
|
149
|
+
}, {}, {}>;
|
|
150
|
+
createdAt: drizzle_orm_pg_core0.PgColumn<{
|
|
151
|
+
name: "created_at";
|
|
152
|
+
tableName: "files";
|
|
153
|
+
dataType: "date";
|
|
154
|
+
columnType: "PgTimestamp";
|
|
155
|
+
data: Date;
|
|
156
|
+
driverParam: string;
|
|
157
|
+
notNull: true;
|
|
158
|
+
hasDefault: true;
|
|
159
|
+
isPrimaryKey: false;
|
|
160
|
+
isAutoincrement: false;
|
|
161
|
+
hasRuntimeDefault: false;
|
|
162
|
+
enumValues: undefined;
|
|
163
|
+
baseColumn: never;
|
|
164
|
+
identity: undefined;
|
|
165
|
+
generated: undefined;
|
|
166
|
+
}, {}, {}>;
|
|
167
|
+
updatedAt: drizzle_orm_pg_core0.PgColumn<{
|
|
168
|
+
name: "updated_at";
|
|
169
|
+
tableName: "files";
|
|
170
|
+
dataType: "date";
|
|
171
|
+
columnType: "PgTimestamp";
|
|
172
|
+
data: Date;
|
|
173
|
+
driverParam: string;
|
|
174
|
+
notNull: true;
|
|
175
|
+
hasDefault: true;
|
|
176
|
+
isPrimaryKey: false;
|
|
177
|
+
isAutoincrement: false;
|
|
178
|
+
hasRuntimeDefault: false;
|
|
179
|
+
enumValues: undefined;
|
|
180
|
+
baseColumn: never;
|
|
181
|
+
identity: undefined;
|
|
182
|
+
generated: undefined;
|
|
183
|
+
}, {}, {}>;
|
|
184
|
+
};
|
|
185
|
+
dialect: "pg";
|
|
186
|
+
}>;
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/server/service.d.ts
|
|
189
|
+
type Service = ReturnType<typeof createService>;
|
|
190
|
+
type ById = {
|
|
191
|
+
id: string;
|
|
192
|
+
};
|
|
193
|
+
declare function createService(options: {
|
|
194
|
+
db: PgDatabase<any, any, any>;
|
|
195
|
+
s3: S3Options;
|
|
196
|
+
keyPrefix?: string;
|
|
197
|
+
presignExpiresIn?: number;
|
|
198
|
+
}): {
|
|
199
|
+
getFile(params: ById): Promise<typeof files.$inferSelect>;
|
|
200
|
+
getUrl(params: ById): Promise<string>;
|
|
201
|
+
uploadFile(params: {
|
|
202
|
+
file: File;
|
|
203
|
+
purpose: string;
|
|
204
|
+
visibility: "private" | "public";
|
|
205
|
+
}): Promise<typeof files.$inferSelect>;
|
|
206
|
+
deleteFile(params: {
|
|
207
|
+
id: string;
|
|
208
|
+
}): Promise<void>;
|
|
209
|
+
acquireFile(params: ById & {
|
|
210
|
+
purpose?: string;
|
|
211
|
+
}): Promise<typeof files.$inferSelect>;
|
|
212
|
+
releaseFile(params: ById): Promise<void>;
|
|
213
|
+
};
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/server/router.d.ts
|
|
216
|
+
type Visibility = "private" | "public";
|
|
217
|
+
interface PurposePolicy {
|
|
218
|
+
maxSize?: number;
|
|
219
|
+
allowedMimeTypes?: string[];
|
|
220
|
+
visibility?: Visibility;
|
|
221
|
+
}
|
|
222
|
+
declare function createRouter<const TPurpose extends string>(options: {
|
|
223
|
+
service: Service;
|
|
224
|
+
policies: Record<TPurpose, PurposePolicy>;
|
|
225
|
+
}): Elysia<"/api", {
|
|
226
|
+
decorator: {};
|
|
227
|
+
store: {};
|
|
228
|
+
derive: {};
|
|
229
|
+
resolve: {};
|
|
230
|
+
}, {
|
|
231
|
+
typebox: {};
|
|
232
|
+
error: {};
|
|
233
|
+
}, {
|
|
234
|
+
schema: {};
|
|
235
|
+
standaloneSchema: {};
|
|
236
|
+
macro: {};
|
|
237
|
+
macroFn: {};
|
|
238
|
+
parser: {};
|
|
239
|
+
response: {};
|
|
240
|
+
}, {
|
|
241
|
+
api: {
|
|
242
|
+
files: {
|
|
243
|
+
upload: {
|
|
244
|
+
post: {
|
|
245
|
+
body: {
|
|
246
|
+
purpose: TPurpose;
|
|
247
|
+
file: File;
|
|
248
|
+
};
|
|
249
|
+
params: {};
|
|
250
|
+
query: unknown;
|
|
251
|
+
headers: unknown;
|
|
252
|
+
response: {
|
|
253
|
+
200: {
|
|
254
|
+
id: string;
|
|
255
|
+
name: string | null;
|
|
256
|
+
key: string;
|
|
257
|
+
size: number;
|
|
258
|
+
mimeType: string;
|
|
259
|
+
createdAt: Date;
|
|
260
|
+
};
|
|
261
|
+
400: {
|
|
262
|
+
message: string;
|
|
263
|
+
};
|
|
264
|
+
500: {
|
|
265
|
+
message: string;
|
|
266
|
+
};
|
|
267
|
+
422: {
|
|
268
|
+
type: "validation";
|
|
269
|
+
on: string;
|
|
270
|
+
summary?: string;
|
|
271
|
+
message?: string;
|
|
272
|
+
found?: unknown;
|
|
273
|
+
property?: string;
|
|
274
|
+
expected?: string;
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
}, {
|
|
282
|
+
derive: {};
|
|
283
|
+
resolve: {};
|
|
284
|
+
schema: {};
|
|
285
|
+
standaloneSchema: {};
|
|
286
|
+
response: {};
|
|
287
|
+
}, {
|
|
288
|
+
derive: {};
|
|
289
|
+
resolve: {};
|
|
290
|
+
schema: {};
|
|
291
|
+
standaloneSchema: {};
|
|
292
|
+
response: {};
|
|
293
|
+
}>;
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/server/server.d.ts
|
|
296
|
+
interface FilesServerOptions<TPurposes extends string> {
|
|
297
|
+
db: PgDatabase<any, any, any>;
|
|
298
|
+
s3: S3Options;
|
|
299
|
+
policies: Record<TPurposes, PurposePolicy>;
|
|
300
|
+
presignExpiresIn?: number;
|
|
301
|
+
}
|
|
302
|
+
declare function createFilesServer<const TPurpose extends string>(options: FilesServerOptions<TPurpose>): {
|
|
303
|
+
readonly router: ReturnType<typeof createRouter<TPurpose>>;
|
|
304
|
+
readonly service: Service;
|
|
305
|
+
};
|
|
306
|
+
//#endregion
|
|
307
|
+
export { schema_d_exports as a, Visibility as i, createFilesServer as n, PurposePolicy as r, FilesServerOptions as t };
|
|
308
|
+
//# sourceMappingURL=server-Cxx9ToSf.d.mts.map
|
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
|
|
2
2
|
import { nanoid } from "nanoid";
|
|
3
3
|
import { index, integer, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
4
|
+
import { Elysia, t } from "elysia";
|
|
4
5
|
import { S3Client } from "bun";
|
|
5
6
|
import { extname } from "path";
|
|
6
7
|
import { and, eq, sql } from "drizzle-orm";
|
|
7
8
|
|
|
8
|
-
//#region src/
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
//#region src/server/schema.ts
|
|
10
|
+
var schema_exports = /* @__PURE__ */ __exportAll({ files: () => files });
|
|
11
|
+
const files = pgTable("files", {
|
|
12
|
+
id: text("id").primaryKey().$defaultFn(() => `file_${nanoid()}`),
|
|
13
|
+
purpose: text("purpose").notNull(),
|
|
14
|
+
name: text("name"),
|
|
15
|
+
key: text("key").notNull().unique(),
|
|
16
|
+
size: integer("size").notNull(),
|
|
17
|
+
mimeType: text("mime_type").notNull().default("application/octet-stream"),
|
|
18
|
+
refCount: integer("ref_count").notNull().default(0),
|
|
19
|
+
visibility: pgEnum("visibility", ["private", "public"])().notNull().default("private"),
|
|
20
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
21
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow()
|
|
22
|
+
}, (table) => [index("files_key_idx").on(table.key), index("files_created_at_idx").on(table.createdAt)]);
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/server/router.ts
|
|
26
|
+
function createRouter(options) {
|
|
27
|
+
const { service, policies } = options;
|
|
11
28
|
return new Elysia({ prefix: "/api" }).post("/files/upload", async ({ status, body }) => {
|
|
12
29
|
const { file, purpose } = body;
|
|
13
30
|
const policy = policies[purpose];
|
|
14
31
|
if (!policy) return status(400, { message: "Purpose not supported" });
|
|
15
32
|
if (policy.maxSize !== void 0 && file.size > policy.maxSize) return status(400, { message: "File size exceeds the maximum allowed size" });
|
|
16
33
|
if (policy.allowedMimeTypes !== void 0 && !policy.allowedMimeTypes.includes(file.type)) return status(400, { message: "File type not allowed" });
|
|
17
|
-
const uploaded = await
|
|
34
|
+
const uploaded = await service.uploadFile({
|
|
18
35
|
file,
|
|
19
36
|
purpose,
|
|
20
37
|
visibility: policy.visibility ?? "private"
|
|
@@ -53,39 +70,22 @@ const FileResponse = t.Object({
|
|
|
53
70
|
});
|
|
54
71
|
|
|
55
72
|
//#endregion
|
|
56
|
-
//#region src/
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
id: text("id").primaryKey().$defaultFn(() => `file_${nanoid()}`),
|
|
60
|
-
purpose: text("purpose").notNull(),
|
|
61
|
-
name: text("name"),
|
|
62
|
-
key: text("key").notNull().unique(),
|
|
63
|
-
size: integer("size").notNull(),
|
|
64
|
-
mimeType: text("mime_type").notNull().default("application/octet-stream"),
|
|
65
|
-
refCount: integer("ref_count").notNull().default(0),
|
|
66
|
-
visibility: pgEnum("visibility", ["private", "public"])().notNull().default("private"),
|
|
67
|
-
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
68
|
-
updatedAt: timestamp("updated_at").notNull().defaultNow()
|
|
69
|
-
}, (table) => [index("files_key_idx").on(table.key), index("files_created_at_idx").on(table.createdAt)]) };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
//#endregion
|
|
73
|
-
//#region src/backend/services.ts
|
|
74
|
-
function createServices(options) {
|
|
75
|
-
const { db, schemas, s3Options, keyPrefix = "files", presignExpiresIn = 3600 } = options;
|
|
73
|
+
//#region src/server/service.ts
|
|
74
|
+
function createService(options) {
|
|
75
|
+
const { db, s3: s3Options, keyPrefix = "files", presignExpiresIn = 3600 } = options;
|
|
76
76
|
const { endpoint } = s3Options;
|
|
77
77
|
const s3Client = new S3Client(s3Options);
|
|
78
78
|
return {
|
|
79
79
|
async getFile(params) {
|
|
80
|
-
const [found] = await db.select().from(
|
|
80
|
+
const [found] = await db.select().from(files).where(eq(files.id, params.id)).limit(1);
|
|
81
81
|
if (!found) throw new Error("File not found");
|
|
82
82
|
return found;
|
|
83
83
|
},
|
|
84
84
|
async getUrl(params) {
|
|
85
85
|
const [found] = await db.select({
|
|
86
|
-
key:
|
|
87
|
-
visibility:
|
|
88
|
-
}).from(
|
|
86
|
+
key: files.key,
|
|
87
|
+
visibility: files.visibility
|
|
88
|
+
}).from(files).where(eq(files.id, params.id)).limit(1);
|
|
89
89
|
if (!found) throw new Error("File not found");
|
|
90
90
|
if (found.visibility === "public") return `${endpoint}/${found.key}`;
|
|
91
91
|
return s3Client.presign(found.key, { expiresIn: presignExpiresIn });
|
|
@@ -105,7 +105,7 @@ function createServices(options) {
|
|
|
105
105
|
acl: params.visibility === "public" ? "public-read" : "private"
|
|
106
106
|
});
|
|
107
107
|
try {
|
|
108
|
-
const [created] = await db.insert(
|
|
108
|
+
const [created] = await db.insert(files).values({
|
|
109
109
|
purpose: params.purpose,
|
|
110
110
|
key,
|
|
111
111
|
size: s3file.size,
|
|
@@ -121,17 +121,17 @@ function createServices(options) {
|
|
|
121
121
|
}
|
|
122
122
|
},
|
|
123
123
|
async deleteFile(params) {
|
|
124
|
-
const [deleted] = await db.delete(
|
|
124
|
+
const [deleted] = await db.delete(files).where(eq(files.id, params.id)).returning();
|
|
125
125
|
if (!deleted) return;
|
|
126
126
|
await s3Client.delete(deleted.key);
|
|
127
127
|
},
|
|
128
128
|
async acquireFile(params) {
|
|
129
|
-
const [updated] = await db.update(
|
|
129
|
+
const [updated] = await db.update(files).set({ refCount: sql`${files.refCount} + 1` }).where(and(eq(files.id, params.id), params.purpose ? eq(files.purpose, params.purpose) : void 0)).returning();
|
|
130
130
|
if (!updated) throw new Error("File not found");
|
|
131
131
|
return updated;
|
|
132
132
|
},
|
|
133
133
|
async releaseFile(params) {
|
|
134
|
-
const [updated] = await db.update(
|
|
134
|
+
const [updated] = await db.update(files).set({ refCount: sql`${files.refCount} - 1` }).where(eq(files.id, params.id)).returning();
|
|
135
135
|
if (!updated) throw new Error("File not found");
|
|
136
136
|
if (updated.refCount < 1) await this.deleteFile({ id: params.id });
|
|
137
137
|
}
|
|
@@ -139,25 +139,22 @@ function createServices(options) {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
//#endregion
|
|
142
|
-
//#region src/
|
|
143
|
-
function
|
|
144
|
-
const
|
|
145
|
-
const services = createServices({
|
|
142
|
+
//#region src/server/server.ts
|
|
143
|
+
function createFilesServer(options) {
|
|
144
|
+
const service = createService({
|
|
146
145
|
db: options.db,
|
|
147
|
-
|
|
148
|
-
s3Options: options.s3Options,
|
|
146
|
+
s3: options.s3,
|
|
149
147
|
presignExpiresIn: options.presignExpiresIn
|
|
150
148
|
});
|
|
151
149
|
return {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
services,
|
|
150
|
+
router: createRouter({
|
|
151
|
+
service,
|
|
155
152
|
policies: options.policies
|
|
156
153
|
}),
|
|
157
|
-
|
|
154
|
+
service
|
|
158
155
|
};
|
|
159
156
|
}
|
|
160
157
|
|
|
161
158
|
//#endregion
|
|
162
|
-
export {
|
|
163
|
-
//# sourceMappingURL=
|
|
159
|
+
export { schema_exports as n, createFilesServer as t };
|
|
160
|
+
//# sourceMappingURL=server-D1-_uiJo.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-D1-_uiJo.mjs","names":[],"sources":["../src/server/schema.ts","../src/server/router.ts","../src/server/service.ts","../src/server/server.ts"],"sourcesContent":["import { nanoid } from \"nanoid\";\r\nimport { index, integer, pgTable, text, timestamp, pgEnum } from \"drizzle-orm/pg-core\";\r\n\r\nexport const files = pgTable(\"files\", {\r\n id: text(\"id\").primaryKey().$defaultFn(() => `file_${nanoid()}`),\r\n purpose: text(\"purpose\").notNull(),\r\n name: text(\"name\"),\r\n key: text(\"key\").notNull().unique(),\r\n size: integer(\"size\").notNull(),\r\n mimeType: text(\"mime_type\").notNull().default(\"application/octet-stream\"),\r\n refCount: integer(\"ref_count\").notNull().default(0),\r\n visibility: pgEnum(\"visibility\", [\"private\", \"public\"])().notNull().default(\"private\"),\r\n createdAt: timestamp(\"created_at\").notNull().defaultNow(),\r\n updatedAt: timestamp(\"updated_at\").notNull().defaultNow(),\r\n}, (table) => [\r\n index(\"files_key_idx\").on(table.key),\r\n index(\"files_created_at_idx\").on(table.createdAt),\r\n]);","import { Elysia, t } from \"elysia\";\r\nimport type { Service } from \"./service\";\r\n\r\nexport type Visibility = \"private\" | \"public\";\r\n\r\nexport interface PurposePolicy {\r\n maxSize?: number;\r\n allowedMimeTypes?: string[];\r\n visibility?: Visibility;\r\n}\r\n\r\nexport function createRouter<const TPurpose extends string>(\r\n options: {\r\n service: Service;\r\n policies: Record<TPurpose, PurposePolicy>\r\n }\r\n) {\r\n \r\n const { \r\n service, \r\n policies,\r\n } = options;\r\n\r\n return new Elysia({\r\n prefix: \"/api\",\r\n }).post(\"/files/upload\", async ({ status, body }) => {\r\n const { file, purpose } = body;\r\n const policy = policies[purpose as TPurpose];\r\n if (!policy) {\r\n return status(400, {\r\n message: \"Purpose not supported\",\r\n });\r\n }\r\n if (policy.maxSize !== undefined && file.size > policy.maxSize) {\r\n return status(400, {\r\n message: \"File size exceeds the maximum allowed size\",\r\n });\r\n }\r\n if (policy.allowedMimeTypes !== undefined && !policy.allowedMimeTypes.includes(file.type)) {\r\n return status(400, {\r\n message: \"File type not allowed\",\r\n });\r\n }\r\n const uploaded = await service.uploadFile({ \r\n file, \r\n purpose, \r\n visibility: policy.visibility ?? \"private\" \r\n });\r\n return status(200, {\r\n id: uploaded.id,\r\n name: uploaded.name,\r\n key: uploaded.key,\r\n size: uploaded.size,\r\n mimeType: uploaded.mimeType,\r\n createdAt: uploaded.createdAt,\r\n });\r\n }, {\r\n body: UploadRequest(Object.keys(policies) as TPurpose[]),\r\n response: {\r\n 200: FileResponse,\r\n 400: ErrorResponse,\r\n 500: ErrorResponse,\r\n }\r\n });\r\n}\r\n\r\nfunction UploadRequest<const TPurpose extends string>(\r\n purposes: TPurpose[]\r\n) {\r\n return t.Object({\r\n file: t.File(),\r\n purpose: t.Union(\r\n purposes.map((p) => t.Literal(p)) as [\r\n ReturnType<typeof t.Literal<TPurpose>>,\r\n ...ReturnType<typeof t.Literal<TPurpose>>[],\r\n ]\r\n )\r\n });\r\n}\r\n\r\nconst ErrorResponse = t.Object({\r\n message: t.String(),\r\n});\r\n\r\nconst FileResponse = t.Object({\r\n id: t.String(),\r\n name: t.Nullable(t.String()),\r\n key: t.String(),\r\n size: t.Number(),\r\n mimeType: t.String(),\r\n createdAt: t.Date(),\r\n});","import { S3Client, type S3Options } from \"bun\";\r\nimport { nanoid } from \"nanoid\";\r\nimport { extname } from \"path\";\r\nimport { eq, sql, and } from \"drizzle-orm\";\r\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\r\nimport { files } from \"./schema\";\r\n\r\nexport type Service = ReturnType<typeof createService>;\r\n\r\ntype ById = { id: string };\r\n\r\nexport function createService(options: {\r\n db: PgDatabase<any, any, any>;\r\n s3: S3Options;\r\n keyPrefix?: string;\r\n presignExpiresIn?: number;\r\n}) {\r\n const { \r\n db, \r\n s3: s3Options,\r\n keyPrefix = \"files\",\r\n presignExpiresIn = 3600\r\n } = options;\r\n \r\n const { endpoint } = s3Options;\r\n const s3Client = new S3Client(s3Options);\r\n\r\n return {\r\n\r\n async getFile(params: ById): Promise<typeof files.$inferSelect> {\r\n const [found] = await db\r\n .select()\r\n .from(files)\r\n .where(eq(files.id, params.id))\r\n .limit(1);\r\n if (!found) {\r\n throw new Error(\"File not found\");\r\n }\r\n return found;\r\n },\r\n\r\n async getUrl(params: ById): Promise<string> {\r\n const [found] = await db\r\n .select({ \r\n key: files.key,\r\n visibility: files.visibility,\r\n })\r\n .from(files)\r\n .where(eq(files.id, params.id))\r\n .limit(1);\r\n if (!found) {\r\n throw new Error(\"File not found\");\r\n }\r\n if (found.visibility === \"public\") {\r\n return `${endpoint}/${found.key}`;\r\n }\r\n return s3Client.presign(found.key, { expiresIn: presignExpiresIn });\r\n },\r\n\r\n async uploadFile(params: {\r\n file: File;\r\n purpose: string;\r\n visibility: \"private\" | \"public\";\r\n }): Promise<typeof files.$inferSelect> {\r\n const id = nanoid();\r\n const ext = extname(params.file.name);\r\n const key = [keyPrefix, params.visibility, params.purpose, `${id}${ext}`].join(\"/\");\r\n const s3file = s3Client.file(key);\r\n await s3file.write(params.file, {\r\n type: params.file.type,\r\n acl: params.visibility === \"public\" ? \"public-read\" : \"private\",\r\n });\r\n try {\r\n const [created] = await db.insert(files)\r\n .values({\r\n purpose: params.purpose,\r\n key,\r\n size: s3file.size,\r\n name: s3file.name ?? null,\r\n mimeType: s3file.type,\r\n visibility: params.visibility,\r\n })\r\n .returning();\r\n if (!created) {\r\n throw new Error(\"Failed to create file record\");\r\n }\r\n return created;\r\n } catch (error) {\r\n await s3file.delete().catch();\r\n throw error;\r\n }\r\n },\r\n\r\n async deleteFile(params: { id: string }): Promise<void> {\r\n const [deleted] = await db\r\n .delete(files)\r\n .where(eq(files.id, params.id))\r\n .returning();\r\n if (!deleted) return;\r\n await s3Client.delete(deleted.key);\r\n },\r\n\r\n async acquireFile(params: ById & { \r\n purpose?: string \r\n }): Promise<typeof files.$inferSelect> {\r\n const [updated] = await db\r\n .update(files)\r\n .set({ refCount: sql`${files.refCount} + 1` })\r\n .where(\r\n and(\r\n eq(files.id, params.id),\r\n params.purpose ? eq(files.purpose, params.purpose) : undefined,\r\n )\r\n )\r\n .returning();\r\n if (!updated) {\r\n throw new Error(\"File not found\");\r\n }\r\n return updated;\r\n },\r\n\r\n async releaseFile(params: ById): Promise<void> {\r\n const [updated] = await db\r\n .update(files)\r\n .set({ refCount: sql`${files.refCount} - 1` })\r\n .where(eq(files.id, params.id))\r\n .returning();\r\n if (!updated) {\r\n throw new Error(\"File not found\");\r\n }\r\n if (updated.refCount < 1) {\r\n await this.deleteFile({ id: params.id });\r\n }\r\n },\r\n };\r\n}","import type { PgDatabase } from \"drizzle-orm/pg-core\";\r\nimport { createRouter, type PurposePolicy } from \"./router\";\r\nimport { createService, type Service } from \"./service\";\r\nimport type { S3Options } from \"bun\";\r\n\r\nexport interface FilesServerOptions<TPurposes extends string> {\r\n db: PgDatabase<any, any, any>;\r\n s3: S3Options;\r\n policies: Record<TPurposes, PurposePolicy>;\r\n presignExpiresIn?: number;\r\n}\r\n\r\nexport function createFilesServer<const TPurpose extends string>(\r\n options: FilesServerOptions<TPurpose>\r\n): {\r\n readonly router: ReturnType<typeof createRouter<TPurpose>>;\r\n readonly service: Service;\r\n} {\r\n const service = createService({\r\n db: options.db,\r\n s3: options.s3,\r\n presignExpiresIn: options.presignExpiresIn,\r\n });\r\n const router = createRouter({\r\n service,\r\n policies: options.policies,\r\n });\r\n return {\r\n router,\r\n service,\r\n };\r\n}"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edkstack/files",
|
|
3
3
|
"description": "File management utilities for EDK Stack",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.12",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"types": "./dist/index.d.mts",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./dist/index.mjs",
|
|
13
|
-
"./backend": "./dist/backend/index.mjs",
|
|
14
13
|
"./client": "./dist/client/index.mjs",
|
|
14
|
+
"./server": "./dist/server/index.mjs",
|
|
15
15
|
"./package.json": "./package.json"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
package/dist/backend/index.d.mts
DELETED
package/dist/backend/index.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backend-BOAJlrA-.mjs","names":[],"sources":["../src/backend/routes.ts","../src/backend/schemas.ts","../src/backend/services.ts","../src/backend/backend.ts"],"sourcesContent":["import { Elysia, t } from \"elysia\";\r\nimport type { Services } from \"./services\";\r\n\r\nexport type Visibility = \"private\" | \"public\";\r\n\r\nexport interface PurposePolicy {\r\n maxSize?: number;\r\n allowedMimeTypes?: string[];\r\n visibility?: Visibility;\r\n}\r\n\r\nexport function createRoutes<const TPurpose extends string>(\r\n options: {\r\n services: Services;\r\n policies: Record<TPurpose, PurposePolicy>\r\n }\r\n) {\r\n \r\n const { \r\n services, \r\n policies,\r\n } = options;\r\n\r\n return new Elysia({\r\n prefix: \"/api\",\r\n }).post(\"/files/upload\", async ({ status, body }) => {\r\n const { file, purpose } = body;\r\n const policy = policies[purpose as TPurpose];\r\n if (!policy) {\r\n return status(400, {\r\n message: \"Purpose not supported\",\r\n });\r\n }\r\n if (policy.maxSize !== undefined && file.size > policy.maxSize) {\r\n return status(400, {\r\n message: \"File size exceeds the maximum allowed size\",\r\n });\r\n }\r\n if (policy.allowedMimeTypes !== undefined && !policy.allowedMimeTypes.includes(file.type)) {\r\n return status(400, {\r\n message: \"File type not allowed\",\r\n });\r\n }\r\n const uploaded = await services.uploadFile({ \r\n file, \r\n purpose, \r\n visibility: policy.visibility ?? \"private\" \r\n });\r\n return status(200, {\r\n id: uploaded.id,\r\n name: uploaded.name,\r\n key: uploaded.key,\r\n size: uploaded.size,\r\n mimeType: uploaded.mimeType,\r\n createdAt: uploaded.createdAt,\r\n });\r\n }, {\r\n body: UploadRequest(Object.keys(policies) as TPurpose[]),\r\n response: {\r\n 200: FileResponse,\r\n 400: ErrorResponse,\r\n 500: ErrorResponse,\r\n }\r\n });\r\n}\r\n\r\nfunction UploadRequest<const TPurpose extends string>(\r\n purposes: TPurpose[]\r\n) {\r\n return t.Object({\r\n file: t.File(),\r\n purpose: t.Union(\r\n purposes.map((p) => t.Literal(p)) as [\r\n ReturnType<typeof t.Literal<TPurpose>>,\r\n ...ReturnType<typeof t.Literal<TPurpose>>[],\r\n ]\r\n )\r\n });\r\n}\r\n\r\nconst ErrorResponse = t.Object({\r\n message: t.String(),\r\n});\r\n\r\nconst FileResponse = t.Object({\r\n id: t.String(),\r\n name: t.Nullable(t.String()),\r\n key: t.String(),\r\n size: t.Number(),\r\n mimeType: t.String(),\r\n createdAt: t.Date(),\r\n});","import { nanoid } from \"nanoid\";\r\nimport { index, integer, pgTable, text, timestamp, pgEnum } from \"drizzle-orm/pg-core\";\r\n\r\n\r\nexport type Schemas = ReturnType<typeof createSchemas>;\r\nexport type FileSchema = Schemas[\"files\"];\r\nexport type FileRecord = FileSchema[\"$inferSelect\"];\r\n\r\nexport function createSchemas() {\r\n\r\n const files = pgTable(\"files\", {\r\n id: text(\"id\").primaryKey().$defaultFn(() => `file_${nanoid()}`),\r\n purpose: text(\"purpose\").notNull(),\r\n name: text(\"name\"),\r\n key: text(\"key\").notNull().unique(),\r\n size: integer(\"size\").notNull(),\r\n mimeType: text(\"mime_type\").notNull().default(\"application/octet-stream\"),\r\n refCount: integer(\"ref_count\").notNull().default(0),\r\n visibility: pgEnum(\"visibility\", [\"private\", \"public\"])().notNull().default(\"private\"),\r\n createdAt: timestamp(\"created_at\").notNull().defaultNow(),\r\n updatedAt: timestamp(\"updated_at\").notNull().defaultNow(),\r\n }, (table) => [\r\n index(\"files_key_idx\").on(table.key),\r\n index(\"files_created_at_idx\").on(table.createdAt),\r\n ]);\r\n\r\n return {\r\n files,\r\n }\r\n}","import { S3Client, type S3Options } from \"bun\";\r\nimport { nanoid } from \"nanoid\";\r\nimport { extname } from \"path\";\r\nimport { eq, sql, lt, and } from \"drizzle-orm\";\r\nimport type { Schemas, FileRecord } from \"./schemas\";\r\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\r\n\r\nexport type Services = ReturnType<typeof createServices>;\r\n\r\ntype ById = { id: string };\r\n\r\nexport function createServices(\r\n options: {\r\n db: PgDatabase<any, any, any>;\r\n schemas: Schemas;\r\n s3Options: S3Options;\r\n keyPrefix?: string;\r\n presignExpiresIn?: number;\r\n }\r\n) {\r\n const { \r\n db, \r\n schemas, \r\n s3Options,\r\n keyPrefix = \"files\",\r\n presignExpiresIn = 3600\r\n } = options;\r\n \r\n const { endpoint } = s3Options;\r\n const s3Client = new S3Client(s3Options);\r\n\r\n return {\r\n\r\n async getFile(params: ById): Promise<FileRecord> {\r\n const [found] = await db\r\n .select()\r\n .from(schemas.files)\r\n .where(eq(schemas.files.id, params.id))\r\n .limit(1);\r\n if (!found) {\r\n throw new Error(\"File not found\");\r\n }\r\n return found;\r\n },\r\n\r\n async getUrl(params: ById): Promise<string> {\r\n const [found] = await db\r\n .select({ \r\n key: schemas.files.key,\r\n visibility: schemas.files.visibility,\r\n })\r\n .from(schemas.files)\r\n .where(eq(schemas.files.id, params.id))\r\n .limit(1);\r\n if (!found) {\r\n throw new Error(\"File not found\");\r\n }\r\n if (found.visibility === \"public\") {\r\n return `${endpoint}/${found.key}`;\r\n }\r\n return s3Client.presign(found.key, { expiresIn: presignExpiresIn });\r\n },\r\n\r\n async uploadFile(params: {\r\n file: File;\r\n purpose: string;\r\n visibility: \"private\" | \"public\";\r\n }): Promise<FileRecord> {\r\n const id = nanoid();\r\n const ext = extname(params.file.name);\r\n const key = [keyPrefix, params.visibility, params.purpose, `${id}${ext}`].join(\"/\");\r\n const s3file = s3Client.file(key);\r\n await s3file.write(params.file, {\r\n type: params.file.type,\r\n acl: params.visibility === \"public\" ? \"public-read\" : \"private\",\r\n });\r\n try {\r\n const [created] = await db.insert(schemas.files)\r\n .values({\r\n purpose: params.purpose,\r\n key,\r\n size: s3file.size,\r\n name: s3file.name ?? null,\r\n mimeType: s3file.type,\r\n visibility: params.visibility,\r\n })\r\n .returning();\r\n if (!created) {\r\n throw new Error(\"Failed to create file record\");\r\n }\r\n return created;\r\n } catch (error) {\r\n await s3file.delete().catch();\r\n throw error;\r\n }\r\n },\r\n\r\n async deleteFile(params: { id: string }): Promise<void> {\r\n const [deleted] = await db\r\n .delete(schemas.files)\r\n .where(eq(schemas.files.id, params.id))\r\n .returning();\r\n if (!deleted) return;\r\n await s3Client.delete(deleted.key);\r\n },\r\n\r\n async acquireFile(params: ById & { \r\n purpose?: string \r\n }): Promise<FileRecord> {\r\n const [updated] = await db\r\n .update(schemas.files)\r\n .set({ refCount: sql`${schemas.files.refCount} + 1` })\r\n .where(\r\n and(\r\n eq(schemas.files.id, params.id),\r\n params.purpose ? eq(schemas.files.purpose, params.purpose) : undefined,\r\n )\r\n )\r\n .returning();\r\n if (!updated) {\r\n throw new Error(\"File not found\");\r\n }\r\n return updated;\r\n },\r\n\r\n async releaseFile(params: ById): Promise<void> {\r\n const [updated] = await db\r\n .update(schemas.files)\r\n .set({ refCount: sql`${schemas.files.refCount} - 1` })\r\n .where(eq(schemas.files.id, params.id))\r\n .returning();\r\n if (!updated) {\r\n throw new Error(\"File not found\");\r\n }\r\n if (updated.refCount < 1) {\r\n await this.deleteFile({ id: params.id });\r\n }\r\n },\r\n };\r\n}","import type { PgDatabase } from \"drizzle-orm/pg-core\";\r\nimport { createRoutes, type PurposePolicy } from \"./routes\";\r\nimport { createSchemas, type Schemas } from \"./schemas\";\r\nimport { createServices, type Services } from \"./services\";\r\nimport type { S3Options } from \"bun\";\r\n\r\nexport interface FilesBackendOptions<TPurposes extends string> {\r\n db: PgDatabase<any, any, any>;\r\n s3Options: S3Options;\r\n policies: Record<TPurposes, PurposePolicy>;\r\n presignExpiresIn?: number;\r\n}\r\n\r\nexport function createFilesBackend<const TPurpose extends string>(\r\n options: FilesBackendOptions<TPurpose>\r\n): {\r\n readonly schemas: Schemas;\r\n readonly routes: ReturnType<typeof createRoutes<TPurpose>>;\r\n readonly services: Services;\r\n} {\r\n const schemas = createSchemas();\r\n const services = createServices({\r\n db: options.db,\r\n schemas,\r\n s3Options: options.s3Options,\r\n presignExpiresIn: options.presignExpiresIn,\r\n });\r\n const routes = createRoutes({\r\n services,\r\n policies: options.policies,\r\n });\r\n return {\r\n schemas,\r\n routes,\r\n services,\r\n };\r\n}"],"mappings":"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"}
|
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
import { Elysia } from "elysia";
|
|
2
|
-
import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
|
|
3
|
-
import { PgDatabase } from "drizzle-orm/pg-core";
|
|
4
|
-
import { S3Options } from "bun";
|
|
5
|
-
|
|
6
|
-
//#region src/backend/schemas.d.ts
|
|
7
|
-
type Schemas = ReturnType<typeof createSchemas>;
|
|
8
|
-
type FileSchema = Schemas["files"];
|
|
9
|
-
type FileRecord = FileSchema["$inferSelect"];
|
|
10
|
-
declare function createSchemas(): {
|
|
11
|
-
files: drizzle_orm_pg_core0.PgTableWithColumns<{
|
|
12
|
-
name: "files";
|
|
13
|
-
schema: undefined;
|
|
14
|
-
columns: {
|
|
15
|
-
id: drizzle_orm_pg_core0.PgColumn<{
|
|
16
|
-
name: "id";
|
|
17
|
-
tableName: "files";
|
|
18
|
-
dataType: "string";
|
|
19
|
-
columnType: "PgText";
|
|
20
|
-
data: string;
|
|
21
|
-
driverParam: string;
|
|
22
|
-
notNull: true;
|
|
23
|
-
hasDefault: true;
|
|
24
|
-
isPrimaryKey: true;
|
|
25
|
-
isAutoincrement: false;
|
|
26
|
-
hasRuntimeDefault: true;
|
|
27
|
-
enumValues: [string, ...string[]];
|
|
28
|
-
baseColumn: never;
|
|
29
|
-
identity: undefined;
|
|
30
|
-
generated: undefined;
|
|
31
|
-
}, {}, {}>;
|
|
32
|
-
purpose: drizzle_orm_pg_core0.PgColumn<{
|
|
33
|
-
name: "purpose";
|
|
34
|
-
tableName: "files";
|
|
35
|
-
dataType: "string";
|
|
36
|
-
columnType: "PgText";
|
|
37
|
-
data: string;
|
|
38
|
-
driverParam: string;
|
|
39
|
-
notNull: true;
|
|
40
|
-
hasDefault: false;
|
|
41
|
-
isPrimaryKey: false;
|
|
42
|
-
isAutoincrement: false;
|
|
43
|
-
hasRuntimeDefault: false;
|
|
44
|
-
enumValues: [string, ...string[]];
|
|
45
|
-
baseColumn: never;
|
|
46
|
-
identity: undefined;
|
|
47
|
-
generated: undefined;
|
|
48
|
-
}, {}, {}>;
|
|
49
|
-
name: drizzle_orm_pg_core0.PgColumn<{
|
|
50
|
-
name: "name";
|
|
51
|
-
tableName: "files";
|
|
52
|
-
dataType: "string";
|
|
53
|
-
columnType: "PgText";
|
|
54
|
-
data: string;
|
|
55
|
-
driverParam: string;
|
|
56
|
-
notNull: false;
|
|
57
|
-
hasDefault: false;
|
|
58
|
-
isPrimaryKey: false;
|
|
59
|
-
isAutoincrement: false;
|
|
60
|
-
hasRuntimeDefault: false;
|
|
61
|
-
enumValues: [string, ...string[]];
|
|
62
|
-
baseColumn: never;
|
|
63
|
-
identity: undefined;
|
|
64
|
-
generated: undefined;
|
|
65
|
-
}, {}, {}>;
|
|
66
|
-
key: drizzle_orm_pg_core0.PgColumn<{
|
|
67
|
-
name: "key";
|
|
68
|
-
tableName: "files";
|
|
69
|
-
dataType: "string";
|
|
70
|
-
columnType: "PgText";
|
|
71
|
-
data: string;
|
|
72
|
-
driverParam: string;
|
|
73
|
-
notNull: true;
|
|
74
|
-
hasDefault: false;
|
|
75
|
-
isPrimaryKey: false;
|
|
76
|
-
isAutoincrement: false;
|
|
77
|
-
hasRuntimeDefault: false;
|
|
78
|
-
enumValues: [string, ...string[]];
|
|
79
|
-
baseColumn: never;
|
|
80
|
-
identity: undefined;
|
|
81
|
-
generated: undefined;
|
|
82
|
-
}, {}, {}>;
|
|
83
|
-
size: drizzle_orm_pg_core0.PgColumn<{
|
|
84
|
-
name: "size";
|
|
85
|
-
tableName: "files";
|
|
86
|
-
dataType: "number";
|
|
87
|
-
columnType: "PgInteger";
|
|
88
|
-
data: number;
|
|
89
|
-
driverParam: string | number;
|
|
90
|
-
notNull: true;
|
|
91
|
-
hasDefault: false;
|
|
92
|
-
isPrimaryKey: false;
|
|
93
|
-
isAutoincrement: false;
|
|
94
|
-
hasRuntimeDefault: false;
|
|
95
|
-
enumValues: undefined;
|
|
96
|
-
baseColumn: never;
|
|
97
|
-
identity: undefined;
|
|
98
|
-
generated: undefined;
|
|
99
|
-
}, {}, {}>;
|
|
100
|
-
mimeType: drizzle_orm_pg_core0.PgColumn<{
|
|
101
|
-
name: "mime_type";
|
|
102
|
-
tableName: "files";
|
|
103
|
-
dataType: "string";
|
|
104
|
-
columnType: "PgText";
|
|
105
|
-
data: string;
|
|
106
|
-
driverParam: string;
|
|
107
|
-
notNull: true;
|
|
108
|
-
hasDefault: true;
|
|
109
|
-
isPrimaryKey: false;
|
|
110
|
-
isAutoincrement: false;
|
|
111
|
-
hasRuntimeDefault: false;
|
|
112
|
-
enumValues: [string, ...string[]];
|
|
113
|
-
baseColumn: never;
|
|
114
|
-
identity: undefined;
|
|
115
|
-
generated: undefined;
|
|
116
|
-
}, {}, {}>;
|
|
117
|
-
refCount: drizzle_orm_pg_core0.PgColumn<{
|
|
118
|
-
name: "ref_count";
|
|
119
|
-
tableName: "files";
|
|
120
|
-
dataType: "number";
|
|
121
|
-
columnType: "PgInteger";
|
|
122
|
-
data: number;
|
|
123
|
-
driverParam: string | number;
|
|
124
|
-
notNull: true;
|
|
125
|
-
hasDefault: true;
|
|
126
|
-
isPrimaryKey: false;
|
|
127
|
-
isAutoincrement: false;
|
|
128
|
-
hasRuntimeDefault: false;
|
|
129
|
-
enumValues: undefined;
|
|
130
|
-
baseColumn: never;
|
|
131
|
-
identity: undefined;
|
|
132
|
-
generated: undefined;
|
|
133
|
-
}, {}, {}>;
|
|
134
|
-
visibility: drizzle_orm_pg_core0.PgColumn<{
|
|
135
|
-
name: "visibility";
|
|
136
|
-
tableName: "files";
|
|
137
|
-
dataType: "string";
|
|
138
|
-
columnType: "PgEnumColumn";
|
|
139
|
-
data: "private" | "public";
|
|
140
|
-
driverParam: string;
|
|
141
|
-
notNull: true;
|
|
142
|
-
hasDefault: true;
|
|
143
|
-
isPrimaryKey: false;
|
|
144
|
-
isAutoincrement: false;
|
|
145
|
-
hasRuntimeDefault: false;
|
|
146
|
-
enumValues: ["private", "public"];
|
|
147
|
-
baseColumn: never;
|
|
148
|
-
identity: undefined;
|
|
149
|
-
generated: undefined;
|
|
150
|
-
}, {}, {}>;
|
|
151
|
-
createdAt: drizzle_orm_pg_core0.PgColumn<{
|
|
152
|
-
name: "created_at";
|
|
153
|
-
tableName: "files";
|
|
154
|
-
dataType: "date";
|
|
155
|
-
columnType: "PgTimestamp";
|
|
156
|
-
data: Date;
|
|
157
|
-
driverParam: string;
|
|
158
|
-
notNull: true;
|
|
159
|
-
hasDefault: true;
|
|
160
|
-
isPrimaryKey: false;
|
|
161
|
-
isAutoincrement: false;
|
|
162
|
-
hasRuntimeDefault: false;
|
|
163
|
-
enumValues: undefined;
|
|
164
|
-
baseColumn: never;
|
|
165
|
-
identity: undefined;
|
|
166
|
-
generated: undefined;
|
|
167
|
-
}, {}, {}>;
|
|
168
|
-
updatedAt: drizzle_orm_pg_core0.PgColumn<{
|
|
169
|
-
name: "updated_at";
|
|
170
|
-
tableName: "files";
|
|
171
|
-
dataType: "date";
|
|
172
|
-
columnType: "PgTimestamp";
|
|
173
|
-
data: Date;
|
|
174
|
-
driverParam: string;
|
|
175
|
-
notNull: true;
|
|
176
|
-
hasDefault: true;
|
|
177
|
-
isPrimaryKey: false;
|
|
178
|
-
isAutoincrement: false;
|
|
179
|
-
hasRuntimeDefault: false;
|
|
180
|
-
enumValues: undefined;
|
|
181
|
-
baseColumn: never;
|
|
182
|
-
identity: undefined;
|
|
183
|
-
generated: undefined;
|
|
184
|
-
}, {}, {}>;
|
|
185
|
-
};
|
|
186
|
-
dialect: "pg";
|
|
187
|
-
}>;
|
|
188
|
-
};
|
|
189
|
-
//#endregion
|
|
190
|
-
//#region src/backend/services.d.ts
|
|
191
|
-
type Services = ReturnType<typeof createServices>;
|
|
192
|
-
type ById = {
|
|
193
|
-
id: string;
|
|
194
|
-
};
|
|
195
|
-
declare function createServices(options: {
|
|
196
|
-
db: PgDatabase<any, any, any>;
|
|
197
|
-
schemas: Schemas;
|
|
198
|
-
s3Options: S3Options;
|
|
199
|
-
keyPrefix?: string;
|
|
200
|
-
presignExpiresIn?: number;
|
|
201
|
-
}): {
|
|
202
|
-
getFile(params: ById): Promise<FileRecord>;
|
|
203
|
-
getUrl(params: ById): Promise<string>;
|
|
204
|
-
uploadFile(params: {
|
|
205
|
-
file: File;
|
|
206
|
-
purpose: string;
|
|
207
|
-
visibility: "private" | "public";
|
|
208
|
-
}): Promise<FileRecord>;
|
|
209
|
-
deleteFile(params: {
|
|
210
|
-
id: string;
|
|
211
|
-
}): Promise<void>;
|
|
212
|
-
acquireFile(params: ById & {
|
|
213
|
-
purpose?: string;
|
|
214
|
-
}): Promise<FileRecord>;
|
|
215
|
-
releaseFile(params: ById): Promise<void>;
|
|
216
|
-
};
|
|
217
|
-
//#endregion
|
|
218
|
-
//#region src/backend/routes.d.ts
|
|
219
|
-
type Visibility = "private" | "public";
|
|
220
|
-
interface PurposePolicy {
|
|
221
|
-
maxSize?: number;
|
|
222
|
-
allowedMimeTypes?: string[];
|
|
223
|
-
visibility?: Visibility;
|
|
224
|
-
}
|
|
225
|
-
declare function createRoutes<const TPurpose extends string>(options: {
|
|
226
|
-
services: Services;
|
|
227
|
-
policies: Record<TPurpose, PurposePolicy>;
|
|
228
|
-
}): Elysia<"/api", {
|
|
229
|
-
decorator: {};
|
|
230
|
-
store: {};
|
|
231
|
-
derive: {};
|
|
232
|
-
resolve: {};
|
|
233
|
-
}, {
|
|
234
|
-
typebox: {};
|
|
235
|
-
error: {};
|
|
236
|
-
}, {
|
|
237
|
-
schema: {};
|
|
238
|
-
standaloneSchema: {};
|
|
239
|
-
macro: {};
|
|
240
|
-
macroFn: {};
|
|
241
|
-
parser: {};
|
|
242
|
-
response: {};
|
|
243
|
-
}, {
|
|
244
|
-
api: {
|
|
245
|
-
files: {
|
|
246
|
-
upload: {
|
|
247
|
-
post: {
|
|
248
|
-
body: {
|
|
249
|
-
purpose: TPurpose;
|
|
250
|
-
file: File;
|
|
251
|
-
};
|
|
252
|
-
params: {};
|
|
253
|
-
query: unknown;
|
|
254
|
-
headers: unknown;
|
|
255
|
-
response: {
|
|
256
|
-
200: {
|
|
257
|
-
id: string;
|
|
258
|
-
name: string | null;
|
|
259
|
-
key: string;
|
|
260
|
-
size: number;
|
|
261
|
-
mimeType: string;
|
|
262
|
-
createdAt: Date;
|
|
263
|
-
};
|
|
264
|
-
400: {
|
|
265
|
-
message: string;
|
|
266
|
-
};
|
|
267
|
-
500: {
|
|
268
|
-
message: string;
|
|
269
|
-
};
|
|
270
|
-
422: {
|
|
271
|
-
type: "validation";
|
|
272
|
-
on: string;
|
|
273
|
-
summary?: string;
|
|
274
|
-
message?: string;
|
|
275
|
-
found?: unknown;
|
|
276
|
-
property?: string;
|
|
277
|
-
expected?: string;
|
|
278
|
-
};
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
};
|
|
282
|
-
};
|
|
283
|
-
};
|
|
284
|
-
}, {
|
|
285
|
-
derive: {};
|
|
286
|
-
resolve: {};
|
|
287
|
-
schema: {};
|
|
288
|
-
standaloneSchema: {};
|
|
289
|
-
response: {};
|
|
290
|
-
}, {
|
|
291
|
-
derive: {};
|
|
292
|
-
resolve: {};
|
|
293
|
-
schema: {};
|
|
294
|
-
standaloneSchema: {};
|
|
295
|
-
response: {};
|
|
296
|
-
}>;
|
|
297
|
-
//#endregion
|
|
298
|
-
//#region src/backend/backend.d.ts
|
|
299
|
-
interface FilesBackendOptions<TPurposes extends string> {
|
|
300
|
-
db: PgDatabase<any, any, any>;
|
|
301
|
-
s3Options: S3Options;
|
|
302
|
-
policies: Record<TPurposes, PurposePolicy>;
|
|
303
|
-
presignExpiresIn?: number;
|
|
304
|
-
}
|
|
305
|
-
declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {
|
|
306
|
-
readonly schemas: Schemas;
|
|
307
|
-
readonly routes: ReturnType<typeof createRoutes<TPurpose>>;
|
|
308
|
-
readonly services: Services;
|
|
309
|
-
};
|
|
310
|
-
//#endregion
|
|
311
|
-
export { FileRecord as a, Visibility as i, createFilesBackend as n, PurposePolicy as r, FilesBackendOptions as t };
|
|
312
|
-
//# sourceMappingURL=backend-CNgPTn_D.d.mts.map
|