@edkstack/files 0.1.9 → 0.1.11
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/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/{backend-BOAJlrA-.mjs → server-BDKHu5Yk.mjs} +30 -35
- package/dist/server-BDKHu5Yk.mjs.map +1 -0
- package/dist/server-DYtdHDUx.d.mts +306 -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-DpyA_n4-.d.mts +0 -482
package/dist/client/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import "../
|
|
1
|
+
import { n as createFilesServer } from "../server-DYtdHDUx.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["routes"]>;
|
|
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[\"routes\"]> {\r\n return treaty<TServer[\"routes\"]>(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 FileRecord, i as Visibility, n as
|
|
2
|
-
import "./
|
|
3
|
-
export { FileRecord,
|
|
1
|
+
import { a as FileRecord, i as Visibility, n as createFilesServer, r as PurposePolicy, t as FilesServerOptions } from "./server-DYtdHDUx.mjs";
|
|
2
|
+
import "./server/index.mjs";
|
|
3
|
+
export { FileRecord, FilesServerOptions, PurposePolicy, Visibility, createFilesServer };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import "./
|
|
1
|
+
import { t as createFilesServer } from "./server-BDKHu5Yk.mjs";
|
|
2
|
+
import "./server/index.mjs";
|
|
3
3
|
|
|
4
|
-
export {
|
|
4
|
+
export { createFilesServer };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Elysia, t } from "elysia";
|
|
2
|
-
import { nanoid } from "nanoid";
|
|
3
|
-
import { index, integer, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
4
2
|
import { S3Client } from "bun";
|
|
3
|
+
import { nanoid } from "nanoid";
|
|
5
4
|
import { extname } from "path";
|
|
6
5
|
import { and, eq, sql } from "drizzle-orm";
|
|
6
|
+
import { index, integer, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
7
7
|
|
|
8
|
-
//#region src/
|
|
8
|
+
//#region src/server/routes.ts
|
|
9
9
|
function createRoutes(options) {
|
|
10
10
|
const { services, policies } = options;
|
|
11
11
|
return new Elysia({ prefix: "/api" }).post("/files/upload", async ({ status, body }) => {
|
|
@@ -53,39 +53,37 @@ const FileResponse = t.Object({
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
//#endregion
|
|
56
|
-
//#region src/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}, (table) => [index("files_key_idx").on(table.key), index("files_created_at_idx").on(table.createdAt)]) };
|
|
70
|
-
}
|
|
56
|
+
//#region src/server/schemas.ts
|
|
57
|
+
const files = pgTable("files", {
|
|
58
|
+
id: text("id").primaryKey().$defaultFn(() => `file_${nanoid()}`),
|
|
59
|
+
purpose: text("purpose").notNull(),
|
|
60
|
+
name: text("name"),
|
|
61
|
+
key: text("key").notNull().unique(),
|
|
62
|
+
size: integer("size").notNull(),
|
|
63
|
+
mimeType: text("mime_type").notNull().default("application/octet-stream"),
|
|
64
|
+
refCount: integer("ref_count").notNull().default(0),
|
|
65
|
+
visibility: pgEnum("visibility", ["private", "public"])().notNull().default("private"),
|
|
66
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
67
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow()
|
|
68
|
+
}, (table) => [index("files_key_idx").on(table.key), index("files_created_at_idx").on(table.createdAt)]);
|
|
71
69
|
|
|
72
70
|
//#endregion
|
|
73
|
-
//#region src/
|
|
71
|
+
//#region src/server/services.ts
|
|
74
72
|
function createServices(options) {
|
|
75
|
-
const { db,
|
|
73
|
+
const { db, s3Options, keyPrefix = "files", presignExpiresIn = 3600 } = options;
|
|
76
74
|
const { endpoint } = s3Options;
|
|
77
75
|
const s3Client = new S3Client(s3Options);
|
|
78
76
|
return {
|
|
79
77
|
async getFile(params) {
|
|
80
|
-
const [found] = await db.select().from(
|
|
78
|
+
const [found] = await db.select().from(files).where(eq(files.id, params.id)).limit(1);
|
|
81
79
|
if (!found) throw new Error("File not found");
|
|
82
80
|
return found;
|
|
83
81
|
},
|
|
84
82
|
async getUrl(params) {
|
|
85
83
|
const [found] = await db.select({
|
|
86
|
-
key:
|
|
87
|
-
visibility:
|
|
88
|
-
}).from(
|
|
84
|
+
key: files.key,
|
|
85
|
+
visibility: files.visibility
|
|
86
|
+
}).from(files).where(eq(files.id, params.id)).limit(1);
|
|
89
87
|
if (!found) throw new Error("File not found");
|
|
90
88
|
if (found.visibility === "public") return `${endpoint}/${found.key}`;
|
|
91
89
|
return s3Client.presign(found.key, { expiresIn: presignExpiresIn });
|
|
@@ -105,7 +103,7 @@ function createServices(options) {
|
|
|
105
103
|
acl: params.visibility === "public" ? "public-read" : "private"
|
|
106
104
|
});
|
|
107
105
|
try {
|
|
108
|
-
const [created] = await db.insert(
|
|
106
|
+
const [created] = await db.insert(files).values({
|
|
109
107
|
purpose: params.purpose,
|
|
110
108
|
key,
|
|
111
109
|
size: s3file.size,
|
|
@@ -121,17 +119,17 @@ function createServices(options) {
|
|
|
121
119
|
}
|
|
122
120
|
},
|
|
123
121
|
async deleteFile(params) {
|
|
124
|
-
const [deleted] = await db.delete(
|
|
122
|
+
const [deleted] = await db.delete(files).where(eq(files.id, params.id)).returning();
|
|
125
123
|
if (!deleted) return;
|
|
126
124
|
await s3Client.delete(deleted.key);
|
|
127
125
|
},
|
|
128
126
|
async acquireFile(params) {
|
|
129
|
-
const [updated] = await db.update(
|
|
127
|
+
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
128
|
if (!updated) throw new Error("File not found");
|
|
131
129
|
return updated;
|
|
132
130
|
},
|
|
133
131
|
async releaseFile(params) {
|
|
134
|
-
const [updated] = await db.update(
|
|
132
|
+
const [updated] = await db.update(files).set({ refCount: sql`${files.refCount} - 1` }).where(eq(files.id, params.id)).returning();
|
|
135
133
|
if (!updated) throw new Error("File not found");
|
|
136
134
|
if (updated.refCount < 1) await this.deleteFile({ id: params.id });
|
|
137
135
|
}
|
|
@@ -139,17 +137,14 @@ function createServices(options) {
|
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
//#endregion
|
|
142
|
-
//#region src/
|
|
143
|
-
function
|
|
144
|
-
const schemas = createSchemas();
|
|
140
|
+
//#region src/server/server.ts
|
|
141
|
+
function createFilesServer(options) {
|
|
145
142
|
const services = createServices({
|
|
146
143
|
db: options.db,
|
|
147
|
-
schemas,
|
|
148
144
|
s3Options: options.s3Options,
|
|
149
145
|
presignExpiresIn: options.presignExpiresIn
|
|
150
146
|
});
|
|
151
147
|
return {
|
|
152
|
-
schemas,
|
|
153
148
|
routes: createRoutes({
|
|
154
149
|
services,
|
|
155
150
|
policies: options.policies
|
|
@@ -159,5 +154,5 @@ function createFilesBackend(options) {
|
|
|
159
154
|
}
|
|
160
155
|
|
|
161
156
|
//#endregion
|
|
162
|
-
export {
|
|
163
|
-
//# sourceMappingURL=
|
|
157
|
+
export { createFilesServer as t };
|
|
158
|
+
//# sourceMappingURL=server-BDKHu5Yk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-BDKHu5Yk.mjs","names":[],"sources":["../src/server/routes.ts","../src/server/schemas.ts","../src/server/services.ts","../src/server/server.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\nexport type FileRecord = typeof files[\"$inferSelect\"];\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 { 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 { FileRecord } from \"./schemas\";\r\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\r\nimport { files } from \"./schemas\";\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 s3Options: S3Options;\r\n keyPrefix?: string;\r\n presignExpiresIn?: number;\r\n }\r\n) {\r\n const { \r\n db, \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(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<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(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<FileRecord> {\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 { createRoutes, type PurposePolicy } from \"./routes\";\r\nimport { createServices, type Services } from \"./services\";\r\nimport type { S3Options } from \"bun\";\r\n\r\nexport interface FilesServerOptions<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 createFilesServer<const TPurpose extends string>(\r\n options: FilesServerOptions<TPurpose>\r\n): {\r\n readonly routes: ReturnType<typeof createRoutes<TPurpose>>;\r\n readonly services: Services;\r\n} {\r\n const services = createServices({\r\n db: options.db,\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 routes,\r\n services,\r\n };\r\n}"],"mappings":"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"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { Elysia } from "elysia";
|
|
2
|
+
import { S3Options } from "bun";
|
|
3
|
+
import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
|
|
4
|
+
import { PgDatabase } from "drizzle-orm/pg-core";
|
|
5
|
+
|
|
6
|
+
//#region src/server/schemas.d.ts
|
|
7
|
+
type FileRecord = typeof files["$inferSelect"];
|
|
8
|
+
declare const files: drizzle_orm_pg_core0.PgTableWithColumns<{
|
|
9
|
+
name: "files";
|
|
10
|
+
schema: undefined;
|
|
11
|
+
columns: {
|
|
12
|
+
id: drizzle_orm_pg_core0.PgColumn<{
|
|
13
|
+
name: "id";
|
|
14
|
+
tableName: "files";
|
|
15
|
+
dataType: "string";
|
|
16
|
+
columnType: "PgText";
|
|
17
|
+
data: string;
|
|
18
|
+
driverParam: string;
|
|
19
|
+
notNull: true;
|
|
20
|
+
hasDefault: true;
|
|
21
|
+
isPrimaryKey: true;
|
|
22
|
+
isAutoincrement: false;
|
|
23
|
+
hasRuntimeDefault: true;
|
|
24
|
+
enumValues: [string, ...string[]];
|
|
25
|
+
baseColumn: never;
|
|
26
|
+
identity: undefined;
|
|
27
|
+
generated: undefined;
|
|
28
|
+
}, {}, {}>;
|
|
29
|
+
purpose: drizzle_orm_pg_core0.PgColumn<{
|
|
30
|
+
name: "purpose";
|
|
31
|
+
tableName: "files";
|
|
32
|
+
dataType: "string";
|
|
33
|
+
columnType: "PgText";
|
|
34
|
+
data: string;
|
|
35
|
+
driverParam: string;
|
|
36
|
+
notNull: true;
|
|
37
|
+
hasDefault: false;
|
|
38
|
+
isPrimaryKey: false;
|
|
39
|
+
isAutoincrement: false;
|
|
40
|
+
hasRuntimeDefault: false;
|
|
41
|
+
enumValues: [string, ...string[]];
|
|
42
|
+
baseColumn: never;
|
|
43
|
+
identity: undefined;
|
|
44
|
+
generated: undefined;
|
|
45
|
+
}, {}, {}>;
|
|
46
|
+
name: drizzle_orm_pg_core0.PgColumn<{
|
|
47
|
+
name: "name";
|
|
48
|
+
tableName: "files";
|
|
49
|
+
dataType: "string";
|
|
50
|
+
columnType: "PgText";
|
|
51
|
+
data: string;
|
|
52
|
+
driverParam: string;
|
|
53
|
+
notNull: false;
|
|
54
|
+
hasDefault: false;
|
|
55
|
+
isPrimaryKey: false;
|
|
56
|
+
isAutoincrement: false;
|
|
57
|
+
hasRuntimeDefault: false;
|
|
58
|
+
enumValues: [string, ...string[]];
|
|
59
|
+
baseColumn: never;
|
|
60
|
+
identity: undefined;
|
|
61
|
+
generated: undefined;
|
|
62
|
+
}, {}, {}>;
|
|
63
|
+
key: drizzle_orm_pg_core0.PgColumn<{
|
|
64
|
+
name: "key";
|
|
65
|
+
tableName: "files";
|
|
66
|
+
dataType: "string";
|
|
67
|
+
columnType: "PgText";
|
|
68
|
+
data: string;
|
|
69
|
+
driverParam: string;
|
|
70
|
+
notNull: true;
|
|
71
|
+
hasDefault: false;
|
|
72
|
+
isPrimaryKey: false;
|
|
73
|
+
isAutoincrement: false;
|
|
74
|
+
hasRuntimeDefault: false;
|
|
75
|
+
enumValues: [string, ...string[]];
|
|
76
|
+
baseColumn: never;
|
|
77
|
+
identity: undefined;
|
|
78
|
+
generated: undefined;
|
|
79
|
+
}, {}, {}>;
|
|
80
|
+
size: drizzle_orm_pg_core0.PgColumn<{
|
|
81
|
+
name: "size";
|
|
82
|
+
tableName: "files";
|
|
83
|
+
dataType: "number";
|
|
84
|
+
columnType: "PgInteger";
|
|
85
|
+
data: number;
|
|
86
|
+
driverParam: string | number;
|
|
87
|
+
notNull: true;
|
|
88
|
+
hasDefault: false;
|
|
89
|
+
isPrimaryKey: false;
|
|
90
|
+
isAutoincrement: false;
|
|
91
|
+
hasRuntimeDefault: false;
|
|
92
|
+
enumValues: undefined;
|
|
93
|
+
baseColumn: never;
|
|
94
|
+
identity: undefined;
|
|
95
|
+
generated: undefined;
|
|
96
|
+
}, {}, {}>;
|
|
97
|
+
mimeType: drizzle_orm_pg_core0.PgColumn<{
|
|
98
|
+
name: "mime_type";
|
|
99
|
+
tableName: "files";
|
|
100
|
+
dataType: "string";
|
|
101
|
+
columnType: "PgText";
|
|
102
|
+
data: string;
|
|
103
|
+
driverParam: string;
|
|
104
|
+
notNull: true;
|
|
105
|
+
hasDefault: true;
|
|
106
|
+
isPrimaryKey: false;
|
|
107
|
+
isAutoincrement: false;
|
|
108
|
+
hasRuntimeDefault: false;
|
|
109
|
+
enumValues: [string, ...string[]];
|
|
110
|
+
baseColumn: never;
|
|
111
|
+
identity: undefined;
|
|
112
|
+
generated: undefined;
|
|
113
|
+
}, {}, {}>;
|
|
114
|
+
refCount: drizzle_orm_pg_core0.PgColumn<{
|
|
115
|
+
name: "ref_count";
|
|
116
|
+
tableName: "files";
|
|
117
|
+
dataType: "number";
|
|
118
|
+
columnType: "PgInteger";
|
|
119
|
+
data: number;
|
|
120
|
+
driverParam: string | number;
|
|
121
|
+
notNull: true;
|
|
122
|
+
hasDefault: true;
|
|
123
|
+
isPrimaryKey: false;
|
|
124
|
+
isAutoincrement: false;
|
|
125
|
+
hasRuntimeDefault: false;
|
|
126
|
+
enumValues: undefined;
|
|
127
|
+
baseColumn: never;
|
|
128
|
+
identity: undefined;
|
|
129
|
+
generated: undefined;
|
|
130
|
+
}, {}, {}>;
|
|
131
|
+
visibility: drizzle_orm_pg_core0.PgColumn<{
|
|
132
|
+
name: "visibility";
|
|
133
|
+
tableName: "files";
|
|
134
|
+
dataType: "string";
|
|
135
|
+
columnType: "PgEnumColumn";
|
|
136
|
+
data: "private" | "public";
|
|
137
|
+
driverParam: string;
|
|
138
|
+
notNull: true;
|
|
139
|
+
hasDefault: true;
|
|
140
|
+
isPrimaryKey: false;
|
|
141
|
+
isAutoincrement: false;
|
|
142
|
+
hasRuntimeDefault: false;
|
|
143
|
+
enumValues: ["private", "public"];
|
|
144
|
+
baseColumn: never;
|
|
145
|
+
identity: undefined;
|
|
146
|
+
generated: undefined;
|
|
147
|
+
}, {}, {}>;
|
|
148
|
+
createdAt: drizzle_orm_pg_core0.PgColumn<{
|
|
149
|
+
name: "created_at";
|
|
150
|
+
tableName: "files";
|
|
151
|
+
dataType: "date";
|
|
152
|
+
columnType: "PgTimestamp";
|
|
153
|
+
data: Date;
|
|
154
|
+
driverParam: string;
|
|
155
|
+
notNull: true;
|
|
156
|
+
hasDefault: true;
|
|
157
|
+
isPrimaryKey: false;
|
|
158
|
+
isAutoincrement: false;
|
|
159
|
+
hasRuntimeDefault: false;
|
|
160
|
+
enumValues: undefined;
|
|
161
|
+
baseColumn: never;
|
|
162
|
+
identity: undefined;
|
|
163
|
+
generated: undefined;
|
|
164
|
+
}, {}, {}>;
|
|
165
|
+
updatedAt: drizzle_orm_pg_core0.PgColumn<{
|
|
166
|
+
name: "updated_at";
|
|
167
|
+
tableName: "files";
|
|
168
|
+
dataType: "date";
|
|
169
|
+
columnType: "PgTimestamp";
|
|
170
|
+
data: Date;
|
|
171
|
+
driverParam: string;
|
|
172
|
+
notNull: true;
|
|
173
|
+
hasDefault: true;
|
|
174
|
+
isPrimaryKey: false;
|
|
175
|
+
isAutoincrement: false;
|
|
176
|
+
hasRuntimeDefault: false;
|
|
177
|
+
enumValues: undefined;
|
|
178
|
+
baseColumn: never;
|
|
179
|
+
identity: undefined;
|
|
180
|
+
generated: undefined;
|
|
181
|
+
}, {}, {}>;
|
|
182
|
+
};
|
|
183
|
+
dialect: "pg";
|
|
184
|
+
}>;
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/server/services.d.ts
|
|
187
|
+
type Services = ReturnType<typeof createServices>;
|
|
188
|
+
type ById = {
|
|
189
|
+
id: string;
|
|
190
|
+
};
|
|
191
|
+
declare function createServices(options: {
|
|
192
|
+
db: PgDatabase<any, any, any>;
|
|
193
|
+
s3Options: S3Options;
|
|
194
|
+
keyPrefix?: string;
|
|
195
|
+
presignExpiresIn?: number;
|
|
196
|
+
}): {
|
|
197
|
+
getFile(params: ById): Promise<FileRecord>;
|
|
198
|
+
getUrl(params: ById): Promise<string>;
|
|
199
|
+
uploadFile(params: {
|
|
200
|
+
file: File;
|
|
201
|
+
purpose: string;
|
|
202
|
+
visibility: "private" | "public";
|
|
203
|
+
}): Promise<FileRecord>;
|
|
204
|
+
deleteFile(params: {
|
|
205
|
+
id: string;
|
|
206
|
+
}): Promise<void>;
|
|
207
|
+
acquireFile(params: ById & {
|
|
208
|
+
purpose?: string;
|
|
209
|
+
}): Promise<FileRecord>;
|
|
210
|
+
releaseFile(params: ById): Promise<void>;
|
|
211
|
+
};
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/server/routes.d.ts
|
|
214
|
+
type Visibility = "private" | "public";
|
|
215
|
+
interface PurposePolicy {
|
|
216
|
+
maxSize?: number;
|
|
217
|
+
allowedMimeTypes?: string[];
|
|
218
|
+
visibility?: Visibility;
|
|
219
|
+
}
|
|
220
|
+
declare function createRoutes<const TPurpose extends string>(options: {
|
|
221
|
+
services: Services;
|
|
222
|
+
policies: Record<TPurpose, PurposePolicy>;
|
|
223
|
+
}): Elysia<"/api", {
|
|
224
|
+
decorator: {};
|
|
225
|
+
store: {};
|
|
226
|
+
derive: {};
|
|
227
|
+
resolve: {};
|
|
228
|
+
}, {
|
|
229
|
+
typebox: {};
|
|
230
|
+
error: {};
|
|
231
|
+
}, {
|
|
232
|
+
schema: {};
|
|
233
|
+
standaloneSchema: {};
|
|
234
|
+
macro: {};
|
|
235
|
+
macroFn: {};
|
|
236
|
+
parser: {};
|
|
237
|
+
response: {};
|
|
238
|
+
}, {
|
|
239
|
+
api: {
|
|
240
|
+
files: {
|
|
241
|
+
upload: {
|
|
242
|
+
post: {
|
|
243
|
+
body: {
|
|
244
|
+
purpose: TPurpose;
|
|
245
|
+
file: File;
|
|
246
|
+
};
|
|
247
|
+
params: {};
|
|
248
|
+
query: unknown;
|
|
249
|
+
headers: unknown;
|
|
250
|
+
response: {
|
|
251
|
+
200: {
|
|
252
|
+
id: string;
|
|
253
|
+
name: string | null;
|
|
254
|
+
key: string;
|
|
255
|
+
size: number;
|
|
256
|
+
mimeType: string;
|
|
257
|
+
createdAt: Date;
|
|
258
|
+
};
|
|
259
|
+
400: {
|
|
260
|
+
message: string;
|
|
261
|
+
};
|
|
262
|
+
500: {
|
|
263
|
+
message: string;
|
|
264
|
+
};
|
|
265
|
+
422: {
|
|
266
|
+
type: "validation";
|
|
267
|
+
on: string;
|
|
268
|
+
summary?: string;
|
|
269
|
+
message?: string;
|
|
270
|
+
found?: unknown;
|
|
271
|
+
property?: string;
|
|
272
|
+
expected?: string;
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
}, {
|
|
280
|
+
derive: {};
|
|
281
|
+
resolve: {};
|
|
282
|
+
schema: {};
|
|
283
|
+
standaloneSchema: {};
|
|
284
|
+
response: {};
|
|
285
|
+
}, {
|
|
286
|
+
derive: {};
|
|
287
|
+
resolve: {};
|
|
288
|
+
schema: {};
|
|
289
|
+
standaloneSchema: {};
|
|
290
|
+
response: {};
|
|
291
|
+
}>;
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/server/server.d.ts
|
|
294
|
+
interface FilesServerOptions<TPurposes extends string> {
|
|
295
|
+
db: PgDatabase<any, any, any>;
|
|
296
|
+
s3Options: S3Options;
|
|
297
|
+
policies: Record<TPurposes, PurposePolicy>;
|
|
298
|
+
presignExpiresIn?: number;
|
|
299
|
+
}
|
|
300
|
+
declare function createFilesServer<const TPurpose extends string>(options: FilesServerOptions<TPurpose>): {
|
|
301
|
+
readonly routes: ReturnType<typeof createRoutes<TPurpose>>;
|
|
302
|
+
readonly services: Services;
|
|
303
|
+
};
|
|
304
|
+
//#endregion
|
|
305
|
+
export { FileRecord as a, Visibility as i, createFilesServer as n, PurposePolicy as r, FilesServerOptions as t };
|
|
306
|
+
//# sourceMappingURL=server-DYtdHDUx.d.mts.map
|
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.11",
|
|
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 } from \"./schemas\";\r\nimport { createServices } 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 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 } as const;\r\n}"],"mappings":"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"}
|
|
@@ -1,482 +0,0 @@
|
|
|
1
|
-
import * as elysia from "elysia";
|
|
2
|
-
import { Elysia } from "elysia";
|
|
3
|
-
import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
|
|
4
|
-
import { PgDatabase } from "drizzle-orm/pg-core";
|
|
5
|
-
import { S3Options } from "bun";
|
|
6
|
-
|
|
7
|
-
//#region src/backend/schemas.d.ts
|
|
8
|
-
type Schemas = ReturnType<typeof createSchemas>;
|
|
9
|
-
type FileSchema = Schemas["files"];
|
|
10
|
-
type FileRecord = FileSchema["$inferSelect"];
|
|
11
|
-
declare function createSchemas(): {
|
|
12
|
-
files: drizzle_orm_pg_core0.PgTableWithColumns<{
|
|
13
|
-
name: "files";
|
|
14
|
-
schema: undefined;
|
|
15
|
-
columns: {
|
|
16
|
-
id: drizzle_orm_pg_core0.PgColumn<{
|
|
17
|
-
name: "id";
|
|
18
|
-
tableName: "files";
|
|
19
|
-
dataType: "string";
|
|
20
|
-
columnType: "PgText";
|
|
21
|
-
data: string;
|
|
22
|
-
driverParam: string;
|
|
23
|
-
notNull: true;
|
|
24
|
-
hasDefault: true;
|
|
25
|
-
isPrimaryKey: true;
|
|
26
|
-
isAutoincrement: false;
|
|
27
|
-
hasRuntimeDefault: true;
|
|
28
|
-
enumValues: [string, ...string[]];
|
|
29
|
-
baseColumn: never;
|
|
30
|
-
identity: undefined;
|
|
31
|
-
generated: undefined;
|
|
32
|
-
}, {}, {}>;
|
|
33
|
-
purpose: drizzle_orm_pg_core0.PgColumn<{
|
|
34
|
-
name: "purpose";
|
|
35
|
-
tableName: "files";
|
|
36
|
-
dataType: "string";
|
|
37
|
-
columnType: "PgText";
|
|
38
|
-
data: string;
|
|
39
|
-
driverParam: string;
|
|
40
|
-
notNull: true;
|
|
41
|
-
hasDefault: false;
|
|
42
|
-
isPrimaryKey: false;
|
|
43
|
-
isAutoincrement: false;
|
|
44
|
-
hasRuntimeDefault: false;
|
|
45
|
-
enumValues: [string, ...string[]];
|
|
46
|
-
baseColumn: never;
|
|
47
|
-
identity: undefined;
|
|
48
|
-
generated: undefined;
|
|
49
|
-
}, {}, {}>;
|
|
50
|
-
name: drizzle_orm_pg_core0.PgColumn<{
|
|
51
|
-
name: "name";
|
|
52
|
-
tableName: "files";
|
|
53
|
-
dataType: "string";
|
|
54
|
-
columnType: "PgText";
|
|
55
|
-
data: string;
|
|
56
|
-
driverParam: string;
|
|
57
|
-
notNull: false;
|
|
58
|
-
hasDefault: false;
|
|
59
|
-
isPrimaryKey: false;
|
|
60
|
-
isAutoincrement: false;
|
|
61
|
-
hasRuntimeDefault: false;
|
|
62
|
-
enumValues: [string, ...string[]];
|
|
63
|
-
baseColumn: never;
|
|
64
|
-
identity: undefined;
|
|
65
|
-
generated: undefined;
|
|
66
|
-
}, {}, {}>;
|
|
67
|
-
key: drizzle_orm_pg_core0.PgColumn<{
|
|
68
|
-
name: "key";
|
|
69
|
-
tableName: "files";
|
|
70
|
-
dataType: "string";
|
|
71
|
-
columnType: "PgText";
|
|
72
|
-
data: string;
|
|
73
|
-
driverParam: string;
|
|
74
|
-
notNull: true;
|
|
75
|
-
hasDefault: false;
|
|
76
|
-
isPrimaryKey: false;
|
|
77
|
-
isAutoincrement: false;
|
|
78
|
-
hasRuntimeDefault: false;
|
|
79
|
-
enumValues: [string, ...string[]];
|
|
80
|
-
baseColumn: never;
|
|
81
|
-
identity: undefined;
|
|
82
|
-
generated: undefined;
|
|
83
|
-
}, {}, {}>;
|
|
84
|
-
size: drizzle_orm_pg_core0.PgColumn<{
|
|
85
|
-
name: "size";
|
|
86
|
-
tableName: "files";
|
|
87
|
-
dataType: "number";
|
|
88
|
-
columnType: "PgInteger";
|
|
89
|
-
data: number;
|
|
90
|
-
driverParam: string | number;
|
|
91
|
-
notNull: true;
|
|
92
|
-
hasDefault: false;
|
|
93
|
-
isPrimaryKey: false;
|
|
94
|
-
isAutoincrement: false;
|
|
95
|
-
hasRuntimeDefault: false;
|
|
96
|
-
enumValues: undefined;
|
|
97
|
-
baseColumn: never;
|
|
98
|
-
identity: undefined;
|
|
99
|
-
generated: undefined;
|
|
100
|
-
}, {}, {}>;
|
|
101
|
-
mimeType: drizzle_orm_pg_core0.PgColumn<{
|
|
102
|
-
name: "mime_type";
|
|
103
|
-
tableName: "files";
|
|
104
|
-
dataType: "string";
|
|
105
|
-
columnType: "PgText";
|
|
106
|
-
data: string;
|
|
107
|
-
driverParam: string;
|
|
108
|
-
notNull: true;
|
|
109
|
-
hasDefault: true;
|
|
110
|
-
isPrimaryKey: false;
|
|
111
|
-
isAutoincrement: false;
|
|
112
|
-
hasRuntimeDefault: false;
|
|
113
|
-
enumValues: [string, ...string[]];
|
|
114
|
-
baseColumn: never;
|
|
115
|
-
identity: undefined;
|
|
116
|
-
generated: undefined;
|
|
117
|
-
}, {}, {}>;
|
|
118
|
-
refCount: drizzle_orm_pg_core0.PgColumn<{
|
|
119
|
-
name: "ref_count";
|
|
120
|
-
tableName: "files";
|
|
121
|
-
dataType: "number";
|
|
122
|
-
columnType: "PgInteger";
|
|
123
|
-
data: number;
|
|
124
|
-
driverParam: string | number;
|
|
125
|
-
notNull: true;
|
|
126
|
-
hasDefault: true;
|
|
127
|
-
isPrimaryKey: false;
|
|
128
|
-
isAutoincrement: false;
|
|
129
|
-
hasRuntimeDefault: false;
|
|
130
|
-
enumValues: undefined;
|
|
131
|
-
baseColumn: never;
|
|
132
|
-
identity: undefined;
|
|
133
|
-
generated: undefined;
|
|
134
|
-
}, {}, {}>;
|
|
135
|
-
visibility: drizzle_orm_pg_core0.PgColumn<{
|
|
136
|
-
name: "visibility";
|
|
137
|
-
tableName: "files";
|
|
138
|
-
dataType: "string";
|
|
139
|
-
columnType: "PgEnumColumn";
|
|
140
|
-
data: "private" | "public";
|
|
141
|
-
driverParam: string;
|
|
142
|
-
notNull: true;
|
|
143
|
-
hasDefault: true;
|
|
144
|
-
isPrimaryKey: false;
|
|
145
|
-
isAutoincrement: false;
|
|
146
|
-
hasRuntimeDefault: false;
|
|
147
|
-
enumValues: ["private", "public"];
|
|
148
|
-
baseColumn: never;
|
|
149
|
-
identity: undefined;
|
|
150
|
-
generated: undefined;
|
|
151
|
-
}, {}, {}>;
|
|
152
|
-
createdAt: drizzle_orm_pg_core0.PgColumn<{
|
|
153
|
-
name: "created_at";
|
|
154
|
-
tableName: "files";
|
|
155
|
-
dataType: "date";
|
|
156
|
-
columnType: "PgTimestamp";
|
|
157
|
-
data: Date;
|
|
158
|
-
driverParam: string;
|
|
159
|
-
notNull: true;
|
|
160
|
-
hasDefault: true;
|
|
161
|
-
isPrimaryKey: false;
|
|
162
|
-
isAutoincrement: false;
|
|
163
|
-
hasRuntimeDefault: false;
|
|
164
|
-
enumValues: undefined;
|
|
165
|
-
baseColumn: never;
|
|
166
|
-
identity: undefined;
|
|
167
|
-
generated: undefined;
|
|
168
|
-
}, {}, {}>;
|
|
169
|
-
updatedAt: drizzle_orm_pg_core0.PgColumn<{
|
|
170
|
-
name: "updated_at";
|
|
171
|
-
tableName: "files";
|
|
172
|
-
dataType: "date";
|
|
173
|
-
columnType: "PgTimestamp";
|
|
174
|
-
data: Date;
|
|
175
|
-
driverParam: string;
|
|
176
|
-
notNull: true;
|
|
177
|
-
hasDefault: true;
|
|
178
|
-
isPrimaryKey: false;
|
|
179
|
-
isAutoincrement: false;
|
|
180
|
-
hasRuntimeDefault: false;
|
|
181
|
-
enumValues: undefined;
|
|
182
|
-
baseColumn: never;
|
|
183
|
-
identity: undefined;
|
|
184
|
-
generated: undefined;
|
|
185
|
-
}, {}, {}>;
|
|
186
|
-
};
|
|
187
|
-
dialect: "pg";
|
|
188
|
-
}>;
|
|
189
|
-
};
|
|
190
|
-
//#endregion
|
|
191
|
-
//#region src/backend/routes.d.ts
|
|
192
|
-
type Visibility = "private" | "public";
|
|
193
|
-
interface PurposePolicy {
|
|
194
|
-
maxSize?: number;
|
|
195
|
-
allowedMimeTypes?: string[];
|
|
196
|
-
visibility?: Visibility;
|
|
197
|
-
}
|
|
198
|
-
//#endregion
|
|
199
|
-
//#region src/backend/backend.d.ts
|
|
200
|
-
interface FilesBackendOptions<TPurposes extends string> {
|
|
201
|
-
db: PgDatabase<any, any, any>;
|
|
202
|
-
s3Options: S3Options;
|
|
203
|
-
policies: Record<TPurposes, PurposePolicy>;
|
|
204
|
-
presignExpiresIn?: number;
|
|
205
|
-
}
|
|
206
|
-
declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {
|
|
207
|
-
readonly schemas: {
|
|
208
|
-
files: drizzle_orm_pg_core0.PgTableWithColumns<{
|
|
209
|
-
name: "files";
|
|
210
|
-
schema: undefined;
|
|
211
|
-
columns: {
|
|
212
|
-
id: drizzle_orm_pg_core0.PgColumn<{
|
|
213
|
-
name: "id";
|
|
214
|
-
tableName: "files";
|
|
215
|
-
dataType: "string";
|
|
216
|
-
columnType: "PgText";
|
|
217
|
-
data: string;
|
|
218
|
-
driverParam: string;
|
|
219
|
-
notNull: true;
|
|
220
|
-
hasDefault: true;
|
|
221
|
-
isPrimaryKey: true;
|
|
222
|
-
isAutoincrement: false;
|
|
223
|
-
hasRuntimeDefault: true;
|
|
224
|
-
enumValues: [string, ...string[]];
|
|
225
|
-
baseColumn: never;
|
|
226
|
-
identity: undefined;
|
|
227
|
-
generated: undefined;
|
|
228
|
-
}, {}, {}>;
|
|
229
|
-
purpose: drizzle_orm_pg_core0.PgColumn<{
|
|
230
|
-
name: "purpose";
|
|
231
|
-
tableName: "files";
|
|
232
|
-
dataType: "string";
|
|
233
|
-
columnType: "PgText";
|
|
234
|
-
data: string;
|
|
235
|
-
driverParam: string;
|
|
236
|
-
notNull: true;
|
|
237
|
-
hasDefault: false;
|
|
238
|
-
isPrimaryKey: false;
|
|
239
|
-
isAutoincrement: false;
|
|
240
|
-
hasRuntimeDefault: false;
|
|
241
|
-
enumValues: [string, ...string[]];
|
|
242
|
-
baseColumn: never;
|
|
243
|
-
identity: undefined;
|
|
244
|
-
generated: undefined;
|
|
245
|
-
}, {}, {}>;
|
|
246
|
-
name: drizzle_orm_pg_core0.PgColumn<{
|
|
247
|
-
name: "name";
|
|
248
|
-
tableName: "files";
|
|
249
|
-
dataType: "string";
|
|
250
|
-
columnType: "PgText";
|
|
251
|
-
data: string;
|
|
252
|
-
driverParam: string;
|
|
253
|
-
notNull: false;
|
|
254
|
-
hasDefault: false;
|
|
255
|
-
isPrimaryKey: false;
|
|
256
|
-
isAutoincrement: false;
|
|
257
|
-
hasRuntimeDefault: false;
|
|
258
|
-
enumValues: [string, ...string[]];
|
|
259
|
-
baseColumn: never;
|
|
260
|
-
identity: undefined;
|
|
261
|
-
generated: undefined;
|
|
262
|
-
}, {}, {}>;
|
|
263
|
-
key: drizzle_orm_pg_core0.PgColumn<{
|
|
264
|
-
name: "key";
|
|
265
|
-
tableName: "files";
|
|
266
|
-
dataType: "string";
|
|
267
|
-
columnType: "PgText";
|
|
268
|
-
data: string;
|
|
269
|
-
driverParam: string;
|
|
270
|
-
notNull: true;
|
|
271
|
-
hasDefault: false;
|
|
272
|
-
isPrimaryKey: false;
|
|
273
|
-
isAutoincrement: false;
|
|
274
|
-
hasRuntimeDefault: false;
|
|
275
|
-
enumValues: [string, ...string[]];
|
|
276
|
-
baseColumn: never;
|
|
277
|
-
identity: undefined;
|
|
278
|
-
generated: undefined;
|
|
279
|
-
}, {}, {}>;
|
|
280
|
-
size: drizzle_orm_pg_core0.PgColumn<{
|
|
281
|
-
name: "size";
|
|
282
|
-
tableName: "files";
|
|
283
|
-
dataType: "number";
|
|
284
|
-
columnType: "PgInteger";
|
|
285
|
-
data: number;
|
|
286
|
-
driverParam: string | number;
|
|
287
|
-
notNull: true;
|
|
288
|
-
hasDefault: false;
|
|
289
|
-
isPrimaryKey: false;
|
|
290
|
-
isAutoincrement: false;
|
|
291
|
-
hasRuntimeDefault: false;
|
|
292
|
-
enumValues: undefined;
|
|
293
|
-
baseColumn: never;
|
|
294
|
-
identity: undefined;
|
|
295
|
-
generated: undefined;
|
|
296
|
-
}, {}, {}>;
|
|
297
|
-
mimeType: drizzle_orm_pg_core0.PgColumn<{
|
|
298
|
-
name: "mime_type";
|
|
299
|
-
tableName: "files";
|
|
300
|
-
dataType: "string";
|
|
301
|
-
columnType: "PgText";
|
|
302
|
-
data: string;
|
|
303
|
-
driverParam: string;
|
|
304
|
-
notNull: true;
|
|
305
|
-
hasDefault: true;
|
|
306
|
-
isPrimaryKey: false;
|
|
307
|
-
isAutoincrement: false;
|
|
308
|
-
hasRuntimeDefault: false;
|
|
309
|
-
enumValues: [string, ...string[]];
|
|
310
|
-
baseColumn: never;
|
|
311
|
-
identity: undefined;
|
|
312
|
-
generated: undefined;
|
|
313
|
-
}, {}, {}>;
|
|
314
|
-
refCount: drizzle_orm_pg_core0.PgColumn<{
|
|
315
|
-
name: "ref_count";
|
|
316
|
-
tableName: "files";
|
|
317
|
-
dataType: "number";
|
|
318
|
-
columnType: "PgInteger";
|
|
319
|
-
data: number;
|
|
320
|
-
driverParam: string | number;
|
|
321
|
-
notNull: true;
|
|
322
|
-
hasDefault: true;
|
|
323
|
-
isPrimaryKey: false;
|
|
324
|
-
isAutoincrement: false;
|
|
325
|
-
hasRuntimeDefault: false;
|
|
326
|
-
enumValues: undefined;
|
|
327
|
-
baseColumn: never;
|
|
328
|
-
identity: undefined;
|
|
329
|
-
generated: undefined;
|
|
330
|
-
}, {}, {}>;
|
|
331
|
-
visibility: drizzle_orm_pg_core0.PgColumn<{
|
|
332
|
-
name: "visibility";
|
|
333
|
-
tableName: "files";
|
|
334
|
-
dataType: "string";
|
|
335
|
-
columnType: "PgEnumColumn";
|
|
336
|
-
data: "private" | "public";
|
|
337
|
-
driverParam: string;
|
|
338
|
-
notNull: true;
|
|
339
|
-
hasDefault: true;
|
|
340
|
-
isPrimaryKey: false;
|
|
341
|
-
isAutoincrement: false;
|
|
342
|
-
hasRuntimeDefault: false;
|
|
343
|
-
enumValues: ["private", "public"];
|
|
344
|
-
baseColumn: never;
|
|
345
|
-
identity: undefined;
|
|
346
|
-
generated: undefined;
|
|
347
|
-
}, {}, {}>;
|
|
348
|
-
createdAt: drizzle_orm_pg_core0.PgColumn<{
|
|
349
|
-
name: "created_at";
|
|
350
|
-
tableName: "files";
|
|
351
|
-
dataType: "date";
|
|
352
|
-
columnType: "PgTimestamp";
|
|
353
|
-
data: Date;
|
|
354
|
-
driverParam: string;
|
|
355
|
-
notNull: true;
|
|
356
|
-
hasDefault: true;
|
|
357
|
-
isPrimaryKey: false;
|
|
358
|
-
isAutoincrement: false;
|
|
359
|
-
hasRuntimeDefault: false;
|
|
360
|
-
enumValues: undefined;
|
|
361
|
-
baseColumn: never;
|
|
362
|
-
identity: undefined;
|
|
363
|
-
generated: undefined;
|
|
364
|
-
}, {}, {}>;
|
|
365
|
-
updatedAt: drizzle_orm_pg_core0.PgColumn<{
|
|
366
|
-
name: "updated_at";
|
|
367
|
-
tableName: "files";
|
|
368
|
-
dataType: "date";
|
|
369
|
-
columnType: "PgTimestamp";
|
|
370
|
-
data: Date;
|
|
371
|
-
driverParam: string;
|
|
372
|
-
notNull: true;
|
|
373
|
-
hasDefault: true;
|
|
374
|
-
isPrimaryKey: false;
|
|
375
|
-
isAutoincrement: false;
|
|
376
|
-
hasRuntimeDefault: false;
|
|
377
|
-
enumValues: undefined;
|
|
378
|
-
baseColumn: never;
|
|
379
|
-
identity: undefined;
|
|
380
|
-
generated: undefined;
|
|
381
|
-
}, {}, {}>;
|
|
382
|
-
};
|
|
383
|
-
dialect: "pg";
|
|
384
|
-
}>;
|
|
385
|
-
};
|
|
386
|
-
readonly routes: elysia.default<"/api", {
|
|
387
|
-
decorator: {};
|
|
388
|
-
store: {};
|
|
389
|
-
derive: {};
|
|
390
|
-
resolve: {};
|
|
391
|
-
}, {
|
|
392
|
-
typebox: {};
|
|
393
|
-
error: {};
|
|
394
|
-
}, {
|
|
395
|
-
schema: {};
|
|
396
|
-
standaloneSchema: {};
|
|
397
|
-
macro: {};
|
|
398
|
-
macroFn: {};
|
|
399
|
-
parser: {};
|
|
400
|
-
response: {};
|
|
401
|
-
}, {
|
|
402
|
-
api: {
|
|
403
|
-
files: {
|
|
404
|
-
upload: {
|
|
405
|
-
post: {
|
|
406
|
-
body: {
|
|
407
|
-
purpose: TPurpose;
|
|
408
|
-
file: File;
|
|
409
|
-
};
|
|
410
|
-
params: {};
|
|
411
|
-
query: unknown;
|
|
412
|
-
headers: unknown;
|
|
413
|
-
response: {
|
|
414
|
-
200: {
|
|
415
|
-
id: string;
|
|
416
|
-
name: string | null;
|
|
417
|
-
key: string;
|
|
418
|
-
size: number;
|
|
419
|
-
mimeType: string;
|
|
420
|
-
createdAt: Date;
|
|
421
|
-
};
|
|
422
|
-
400: {
|
|
423
|
-
message: string;
|
|
424
|
-
};
|
|
425
|
-
500: {
|
|
426
|
-
message: string;
|
|
427
|
-
};
|
|
428
|
-
422: {
|
|
429
|
-
type: "validation";
|
|
430
|
-
on: string;
|
|
431
|
-
summary?: string;
|
|
432
|
-
message?: string;
|
|
433
|
-
found?: unknown;
|
|
434
|
-
property?: string;
|
|
435
|
-
expected?: string;
|
|
436
|
-
};
|
|
437
|
-
};
|
|
438
|
-
};
|
|
439
|
-
};
|
|
440
|
-
};
|
|
441
|
-
};
|
|
442
|
-
}, {
|
|
443
|
-
derive: {};
|
|
444
|
-
resolve: {};
|
|
445
|
-
schema: {};
|
|
446
|
-
standaloneSchema: {};
|
|
447
|
-
response: {};
|
|
448
|
-
}, {
|
|
449
|
-
derive: {};
|
|
450
|
-
resolve: {};
|
|
451
|
-
schema: {};
|
|
452
|
-
standaloneSchema: {};
|
|
453
|
-
response: {};
|
|
454
|
-
}>;
|
|
455
|
-
readonly services: {
|
|
456
|
-
getFile(params: {
|
|
457
|
-
id: string;
|
|
458
|
-
}): Promise<FileRecord>;
|
|
459
|
-
getUrl(params: {
|
|
460
|
-
id: string;
|
|
461
|
-
}): Promise<string>;
|
|
462
|
-
uploadFile(params: {
|
|
463
|
-
file: File;
|
|
464
|
-
purpose: string;
|
|
465
|
-
visibility: "private" | "public";
|
|
466
|
-
}): Promise<FileRecord>;
|
|
467
|
-
deleteFile(params: {
|
|
468
|
-
id: string;
|
|
469
|
-
}): Promise<void>;
|
|
470
|
-
acquireFile(params: {
|
|
471
|
-
id: string;
|
|
472
|
-
} & {
|
|
473
|
-
purpose?: string;
|
|
474
|
-
}): Promise<FileRecord>;
|
|
475
|
-
releaseFile(params: {
|
|
476
|
-
id: string;
|
|
477
|
-
}): Promise<void>;
|
|
478
|
-
};
|
|
479
|
-
};
|
|
480
|
-
//#endregion
|
|
481
|
-
export { FileRecord as a, Visibility as i, createFilesBackend as n, PurposePolicy as r, FilesBackendOptions as t };
|
|
482
|
-
//# sourceMappingURL=backend-DpyA_n4-.d.mts.map
|