@mzedstudio/uploadthingtrack 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +264 -0
  3. package/dist/client/index.d.ts +157 -0
  4. package/dist/client/index.d.ts.map +1 -0
  5. package/dist/client/index.js +121 -0
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/component/_generated/api.d.ts +48 -0
  8. package/dist/component/_generated/api.d.ts.map +1 -0
  9. package/dist/component/_generated/api.js +31 -0
  10. package/dist/component/_generated/api.js.map +1 -0
  11. package/dist/component/_generated/component.d.ts +119 -0
  12. package/dist/component/_generated/component.d.ts.map +1 -0
  13. package/dist/component/_generated/component.js +11 -0
  14. package/dist/component/_generated/component.js.map +1 -0
  15. package/dist/component/_generated/dataModel.d.ts +46 -0
  16. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  17. package/dist/component/_generated/dataModel.js +11 -0
  18. package/dist/component/_generated/dataModel.js.map +1 -0
  19. package/dist/component/_generated/server.d.ts +121 -0
  20. package/dist/component/_generated/server.d.ts.map +1 -0
  21. package/dist/component/_generated/server.js +78 -0
  22. package/dist/component/_generated/server.js.map +1 -0
  23. package/dist/component/access.d.ts +9 -0
  24. package/dist/component/access.d.ts.map +1 -0
  25. package/dist/component/access.js +31 -0
  26. package/dist/component/access.js.map +1 -0
  27. package/dist/component/callbacks.d.ts +16 -0
  28. package/dist/component/callbacks.d.ts.map +1 -0
  29. package/dist/component/callbacks.js +147 -0
  30. package/dist/component/callbacks.js.map +1 -0
  31. package/dist/component/cleanup.d.ts +9 -0
  32. package/dist/component/cleanup.d.ts.map +1 -0
  33. package/dist/component/cleanup.js +32 -0
  34. package/dist/component/cleanup.js.map +1 -0
  35. package/dist/component/config.d.ts +42 -0
  36. package/dist/component/config.d.ts.map +1 -0
  37. package/dist/component/config.js +87 -0
  38. package/dist/component/config.js.map +1 -0
  39. package/dist/component/convex.config.d.ts +3 -0
  40. package/dist/component/convex.config.d.ts.map +1 -0
  41. package/dist/component/convex.config.js +3 -0
  42. package/dist/component/convex.config.js.map +1 -0
  43. package/dist/component/files.d.ts +72 -0
  44. package/dist/component/files.d.ts.map +1 -0
  45. package/dist/component/files.js +153 -0
  46. package/dist/component/files.js.map +1 -0
  47. package/dist/component/queries.d.ts +43 -0
  48. package/dist/component/queries.d.ts.map +1 -0
  49. package/dist/component/queries.js +113 -0
  50. package/dist/component/queries.js.map +1 -0
  51. package/dist/component/schema.d.ts +97 -0
  52. package/dist/component/schema.d.ts.map +1 -0
  53. package/dist/component/schema.js +42 -0
  54. package/dist/component/schema.js.map +1 -0
  55. package/dist/component/stats.d.ts +7 -0
  56. package/dist/component/stats.d.ts.map +1 -0
  57. package/dist/component/stats.js +20 -0
  58. package/dist/component/stats.js.map +1 -0
  59. package/dist/component/types.d.ts +78 -0
  60. package/dist/component/types.d.ts.map +1 -0
  61. package/dist/component/types.js +34 -0
  62. package/dist/component/types.js.map +1 -0
  63. package/package.json +84 -0
  64. package/src/client/index.ts +277 -0
  65. package/src/component/_generated/api.ts +64 -0
  66. package/src/component/_generated/component.ts +159 -0
  67. package/src/component/_generated/dataModel.ts +60 -0
  68. package/src/component/_generated/server.ts +156 -0
  69. package/src/component/access.ts +39 -0
  70. package/src/component/callbacks.ts +173 -0
  71. package/src/component/cleanup.ts +40 -0
  72. package/src/component/config.ts +115 -0
  73. package/src/component/convex.config.ts +2 -0
  74. package/src/component/files.ts +186 -0
  75. package/src/component/queries.ts +121 -0
  76. package/src/component/schema.ts +44 -0
  77. package/src/component/stats.ts +23 -0
  78. package/src/component/types.ts +49 -0
  79. package/src/test.ts +21 -0
@@ -0,0 +1,31 @@
1
+ export function canAccess(params) {
2
+ const { ownerId, viewerId } = params;
3
+ if (viewerId && viewerId === ownerId)
4
+ return true;
5
+ const rule = params.fileRule ?? params.folderRule;
6
+ if (!rule)
7
+ return false;
8
+ if (viewerId && rule.denyUserIds?.includes(viewerId))
9
+ return false;
10
+ if (rule.visibility === "public")
11
+ return true;
12
+ if (!viewerId)
13
+ return false;
14
+ if (rule.visibility === "private")
15
+ return false;
16
+ if (rule.visibility === "restricted") {
17
+ return Boolean(rule.allowUserIds?.includes(viewerId));
18
+ }
19
+ return false;
20
+ }
21
+ export function sanitizeAccessRule(rule) {
22
+ if (!rule)
23
+ return undefined;
24
+ const unique = (list) => list ? Array.from(new Set(list.filter((value) => value.length > 0))) : undefined;
25
+ return {
26
+ visibility: rule.visibility,
27
+ allowUserIds: unique(rule.allowUserIds),
28
+ denyUserIds: unique(rule.denyUserIds),
29
+ };
30
+ }
31
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/component/access.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,MAKzB;IACC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACrC,IAAI,QAAQ,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAElD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,UAAU,CAAC;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IAExB,IAAI,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAEnE,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE9C,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAE5B,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAEhD,IAAI,IAAI,CAAC,UAAU,KAAK,YAAY,EAAE,CAAC;QACrC,OAAO,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAiB;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,EAAE,CACjC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnF,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;QACvC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;KACtC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ type CallbackResult = {
2
+ ok: true;
3
+ fileId: string;
4
+ hook: string;
5
+ } | {
6
+ ok: false;
7
+ error: string;
8
+ };
9
+ export declare const handleUploadthingCallback: import("convex/server").RegisteredAction<"public", {
10
+ apiKey?: string | undefined;
11
+ hook: string;
12
+ rawBody: string;
13
+ signature: string;
14
+ }, Promise<CallbackResult>>;
15
+ export {};
16
+ //# sourceMappingURL=callbacks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callbacks.d.ts","sourceRoot":"","sources":["../../src/component/callbacks.ts"],"names":[],"mappings":"AAgGA,KAAK,cAAc,GACf;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,eAAO,MAAM,yBAAyB;;;;;2BAwEpC,CAAC"}
@@ -0,0 +1,147 @@
1
+ import { action } from "./_generated/server";
2
+ import { v } from "convex/values";
3
+ import { internal } from "./_generated/api";
4
+ function normalizeSignature(signature) {
5
+ const trimmed = signature.trim();
6
+ if (trimmed.startsWith("hmac-sha256=")) {
7
+ return trimmed.slice("hmac-sha256=".length);
8
+ }
9
+ return trimmed;
10
+ }
11
+ function hexToBytes(hex) {
12
+ if (hex.length % 2 !== 0)
13
+ return null;
14
+ const bytes = new Uint8Array(hex.length / 2);
15
+ for (let i = 0; i < bytes.length; i += 1) {
16
+ const chunk = hex.slice(i * 2, i * 2 + 2);
17
+ const value = Number.parseInt(chunk, 16);
18
+ if (Number.isNaN(value))
19
+ return null;
20
+ bytes[i] = value;
21
+ }
22
+ return bytes;
23
+ }
24
+ function timingSafeEqualBytes(a, b) {
25
+ if (a.length !== b.length)
26
+ return false;
27
+ let diff = 0;
28
+ for (let i = 0; i < a.length; i += 1) {
29
+ diff |= a[i] ^ b[i];
30
+ }
31
+ return diff === 0;
32
+ }
33
+ async function hmacSha256Hex(key, message) {
34
+ if (!globalThis.crypto?.subtle) {
35
+ throw new Error("WebCrypto unavailable in this runtime.");
36
+ }
37
+ const encoder = new TextEncoder();
38
+ const cryptoKey = await globalThis.crypto.subtle.importKey("raw", encoder.encode(key), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
39
+ const signature = await globalThis.crypto.subtle.sign("HMAC", cryptoKey, encoder.encode(message));
40
+ const bytes = new Uint8Array(signature);
41
+ return Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("");
42
+ }
43
+ async function verifySignature(rawBody, signature, apiKey) {
44
+ const expected = await hmacSha256Hex(apiKey, rawBody);
45
+ const actual = normalizeSignature(signature);
46
+ const expectedBytes = hexToBytes(expected);
47
+ const actualBytes = hexToBytes(actual);
48
+ if (!expectedBytes || !actualBytes)
49
+ return false;
50
+ return timingSafeEqualBytes(expectedBytes, actualBytes);
51
+ }
52
+ function extractUserId(metadata) {
53
+ if (!metadata)
54
+ return undefined;
55
+ return (metadata.userId ??
56
+ metadata.ownerId ??
57
+ metadata.uploadedBy ??
58
+ metadata.user);
59
+ }
60
+ function extractTags(metadata) {
61
+ if (!metadata)
62
+ return undefined;
63
+ if (Array.isArray(metadata.tags))
64
+ return metadata.tags;
65
+ if (typeof metadata.tags === "string") {
66
+ return metadata.tags
67
+ .split(",")
68
+ .map((tag) => tag.trim())
69
+ .filter(Boolean);
70
+ }
71
+ return undefined;
72
+ }
73
+ function toNumber(value) {
74
+ if (typeof value === "number")
75
+ return value;
76
+ if (typeof value === "string") {
77
+ const parsed = Number(value);
78
+ return Number.isNaN(parsed) ? undefined : parsed;
79
+ }
80
+ return undefined;
81
+ }
82
+ export const handleUploadthingCallback = action({
83
+ args: {
84
+ rawBody: v.string(),
85
+ signature: v.string(),
86
+ hook: v.string(),
87
+ apiKey: v.optional(v.string()),
88
+ },
89
+ handler: async (ctx, args) => {
90
+ const globals = await ctx.runQuery(internal.config.getGlobalsInternal, {});
91
+ const apiKey = args.apiKey ?? globals.uploadthingApiKey;
92
+ if (!apiKey) {
93
+ throw new Error("UploadThing API key not configured.");
94
+ }
95
+ const isValid = await verifySignature(args.rawBody, args.signature, apiKey);
96
+ if (!isValid) {
97
+ return { ok: false, error: "invalid_signature" };
98
+ }
99
+ let payload;
100
+ try {
101
+ payload = JSON.parse(args.rawBody);
102
+ }
103
+ catch (error) {
104
+ return { ok: false, error: "invalid_json" };
105
+ }
106
+ const file = payload.file ?? payload;
107
+ const metadata = payload.metadata ?? file.metadata ?? {};
108
+ const key = file.key ?? file.fileKey ?? file.id;
109
+ const url = file.url ?? file.fileUrl;
110
+ const name = file.name ?? file.filename ?? file.fileName;
111
+ const size = toNumber(file.size ?? file.fileSize);
112
+ const mimeType = file.type ?? file.mimeType ?? file.contentType;
113
+ const customId = file.customId ?? file.customID;
114
+ const fileType = file.fileType ?? metadata.fileType;
115
+ if (!key || !url || !name || size === undefined || !mimeType) {
116
+ return { ok: false, error: "missing_file_fields" };
117
+ }
118
+ const userId = extractUserId(metadata) ?? payload.userId;
119
+ if (!userId) {
120
+ return { ok: false, error: "missing_user_id" };
121
+ }
122
+ const options = {
123
+ tags: extractTags(metadata),
124
+ folder: metadata.folder,
125
+ access: metadata.access,
126
+ metadata,
127
+ expiresAt: toNumber(metadata.expiresAt),
128
+ ttlMs: toNumber(metadata.ttlMs),
129
+ fileType,
130
+ };
131
+ const fileId = await ctx.runMutation(internal.files.internalUpsertFile, {
132
+ file: {
133
+ key,
134
+ url,
135
+ name,
136
+ size,
137
+ mimeType,
138
+ customId,
139
+ fileType,
140
+ },
141
+ userId,
142
+ options,
143
+ });
144
+ return { ok: true, fileId, hook: args.hook };
145
+ },
146
+ });
147
+ //# sourceMappingURL=callbacks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callbacks.js","sourceRoot":"","sources":["../../src/component/callbacks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare const cleanupExpired: import("convex/server").RegisteredAction<"public", {
2
+ batchSize?: number | undefined;
3
+ dryRun?: boolean | undefined;
4
+ }, Promise<{
5
+ deletedCount: number;
6
+ keys: string[];
7
+ hasMore: boolean;
8
+ }>>;
9
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/component/cleanup.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc;;;;kBAWT,MAAM;UACd,MAAM,EAAE;aACL,OAAO;GAsBlB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { action } from "./_generated/server";
2
+ import { v } from "convex/values";
3
+ import { internal } from "./_generated/api";
4
+ export const cleanupExpired = action({
5
+ args: {
6
+ batchSize: v.optional(v.number()),
7
+ dryRun: v.optional(v.boolean()),
8
+ },
9
+ returns: v.object({
10
+ deletedCount: v.number(),
11
+ keys: v.array(v.string()),
12
+ hasMore: v.boolean(),
13
+ }),
14
+ handler: async (ctx, args) => {
15
+ const globals = await ctx.runQuery(internal.config.getGlobalsInternal, {});
16
+ const limit = args.batchSize ?? globals.deleteBatchSize ?? 100;
17
+ const expired = (await ctx.runQuery(internal.queries.expiredBatch, {
18
+ now: Date.now(),
19
+ limit,
20
+ }));
21
+ const keys = expired.map((item) => item.key);
22
+ if (!args.dryRun && keys.length > 0) {
23
+ await ctx.runMutation(internal.files.deleteFilesByKey, { keys });
24
+ }
25
+ return {
26
+ deletedCount: args.dryRun ? 0 : keys.length,
27
+ keys,
28
+ hasMore: expired.length >= limit,
29
+ };
30
+ },
31
+ });
32
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/component/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;IACnC,IAAI,EAAE;QACJ,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;KAChC;IACD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;KACrB,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAItB,EAAE;QACH,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAK,OAAe,CAAC,eAAe,IAAI,GAAG,CAAC;QAExE,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE;YACjE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;YACf,KAAK;SACN,CAAC,CAAmC,CAAC;QAEtC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE7C,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,OAAO;YACL,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;YAC3C,IAAI;YACJ,OAAO,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;SACjC,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,42 @@
1
+ export type Globals = {
2
+ uploadthingApiKey?: string;
3
+ defaultTtlMs?: number;
4
+ ttlByMimeType?: Record<string, number>;
5
+ ttlByFileType?: Record<string, number>;
6
+ deleteRemoteOnExpire?: boolean;
7
+ deleteBatchSize?: number;
8
+ };
9
+ export declare function loadGlobals(ctx: {
10
+ db: any;
11
+ }): Promise<Globals>;
12
+ export declare const getGlobalsInternal: import("convex/server").RegisteredQuery<"internal", {}, Promise<Globals>>;
13
+ export declare function computeExpiresAt(params: {
14
+ now: number;
15
+ mimeType?: string;
16
+ fileType?: string;
17
+ expiresAt?: number;
18
+ ttlMs?: number;
19
+ globals?: Globals;
20
+ }): number | undefined;
21
+ export declare const setConfig: import("convex/server").RegisteredMutation<"public", {
22
+ replace?: boolean | undefined;
23
+ config: {
24
+ uploadthingApiKey?: string | undefined;
25
+ defaultTtlMs?: number | undefined;
26
+ ttlByMimeType?: Record<string, number> | undefined;
27
+ ttlByFileType?: Record<string, number> | undefined;
28
+ deleteRemoteOnExpire?: boolean | undefined;
29
+ deleteBatchSize?: number | undefined;
30
+ };
31
+ }, Promise<{
32
+ created: boolean;
33
+ }>>;
34
+ export declare const getConfig: import("convex/server").RegisteredQuery<"public", {}, Promise<{
35
+ defaultTtlMs: number | undefined;
36
+ ttlByMimeType: Record<string, number> | undefined;
37
+ ttlByFileType: Record<string, number> | undefined;
38
+ deleteRemoteOnExpire: boolean | undefined;
39
+ deleteBatchSize: number | undefined;
40
+ hasApiKey: boolean;
41
+ }>>;
42
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/component/config.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,OAAO,GAAG;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAoBF,wBAAsB,WAAW,CAAC,GAAG,EAAE;IAAE,EAAE,EAAE,GAAG,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAEpE;AAED,eAAO,MAAM,kBAAkB,2EAK7B,CAAC;AAEH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,MAAM,GAAG,SAAS,CAoBrB;AAED,eAAO,MAAM,SAAS;;;;;;;;;;;;GA0BpB,CAAC;AAEH,eAAO,MAAM,SAAS;;;;;;;GAapB,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { internalQuery, mutation, query } from "./_generated/server";
2
+ import { v } from "convex/values";
3
+ import { configUpdateValidator } from "./types";
4
+ const GLOBALS_ID = "globals";
5
+ async function readGlobals(db) {
6
+ const record = await db
7
+ .query("globals")
8
+ .withIndex("by_singleton", (q) => q.eq("singleton", GLOBALS_ID))
9
+ .unique();
10
+ if (!record)
11
+ return {};
12
+ return {
13
+ uploadthingApiKey: record.uploadthingApiKey,
14
+ defaultTtlMs: record.defaultTtlMs,
15
+ ttlByMimeType: record.ttlByMimeType,
16
+ ttlByFileType: record.ttlByFileType,
17
+ deleteRemoteOnExpire: record.deleteRemoteOnExpire,
18
+ deleteBatchSize: record.deleteBatchSize,
19
+ };
20
+ }
21
+ export async function loadGlobals(ctx) {
22
+ return await readGlobals(ctx.db);
23
+ }
24
+ export const getGlobalsInternal = internalQuery({
25
+ args: {},
26
+ handler: async (ctx) => {
27
+ return await readGlobals(ctx.db);
28
+ },
29
+ });
30
+ export function computeExpiresAt(params) {
31
+ if (params.expiresAt !== undefined)
32
+ return params.expiresAt;
33
+ if (params.ttlMs !== undefined)
34
+ return params.now + params.ttlMs;
35
+ const globals = params.globals;
36
+ if (!globals)
37
+ return undefined;
38
+ if (params.fileType && globals.ttlByFileType?.[params.fileType] !== undefined) {
39
+ return params.now + globals.ttlByFileType[params.fileType];
40
+ }
41
+ if (params.mimeType && globals.ttlByMimeType?.[params.mimeType] !== undefined) {
42
+ return params.now + globals.ttlByMimeType[params.mimeType];
43
+ }
44
+ if (globals.defaultTtlMs !== undefined) {
45
+ return params.now + globals.defaultTtlMs;
46
+ }
47
+ return undefined;
48
+ }
49
+ export const setConfig = mutation({
50
+ args: {
51
+ config: configUpdateValidator,
52
+ replace: v.optional(v.boolean()),
53
+ },
54
+ handler: async (ctx, args) => {
55
+ const existing = await ctx.db
56
+ .query("globals")
57
+ .withIndex("by_singleton", (q) => q.eq("singleton", GLOBALS_ID))
58
+ .unique();
59
+ if (!existing) {
60
+ const record = { singleton: GLOBALS_ID, ...args.config };
61
+ await ctx.db.insert("globals", record);
62
+ return { created: true };
63
+ }
64
+ const update = args.replace
65
+ ? { singleton: GLOBALS_ID, ...args.config }
66
+ : { ...existing, ...args.config };
67
+ delete update._id;
68
+ delete update._creationTime;
69
+ await ctx.db.patch(existing._id, update);
70
+ return { created: false };
71
+ },
72
+ });
73
+ export const getConfig = query({
74
+ args: {},
75
+ handler: async (ctx) => {
76
+ const globals = await loadGlobals(ctx);
77
+ return {
78
+ defaultTtlMs: globals.defaultTtlMs,
79
+ ttlByMimeType: globals.ttlByMimeType,
80
+ ttlByFileType: globals.ttlByFileType,
81
+ deleteRemoteOnExpire: globals.deleteRemoteOnExpire,
82
+ deleteBatchSize: globals.deleteBatchSize,
83
+ hasApiKey: Boolean(globals.uploadthingApiKey),
84
+ };
85
+ },
86
+ });
87
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/component/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("convex/server").ComponentDefinition<any>;
2
+ export default _default;
3
+ //# sourceMappingURL=convex.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":";AACA,wBAA2D"}
@@ -0,0 +1,3 @@
1
+ import { defineComponent } from "convex/server";
2
+ export default defineComponent("uploadthing_file_tracker");
3
+ //# sourceMappingURL=convex.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,eAAe,eAAe,CAAC,0BAA0B,CAAC,CAAC"}
@@ -0,0 +1,72 @@
1
+ export declare const upsertFile: import("convex/server").RegisteredMutation<"public", {
2
+ options?: {
3
+ access?: {
4
+ allowUserIds?: string[] | undefined;
5
+ denyUserIds?: string[] | undefined;
6
+ visibility: "public" | "private" | "restricted";
7
+ } | undefined;
8
+ folder?: string | undefined;
9
+ fileType?: string | undefined;
10
+ tags?: string[] | undefined;
11
+ metadata?: any;
12
+ expiresAt?: number | undefined;
13
+ ttlMs?: number | undefined;
14
+ } | undefined;
15
+ file: {
16
+ uploadedAt?: number | undefined;
17
+ fileType?: string | undefined;
18
+ customId?: string | undefined;
19
+ key: string;
20
+ mimeType: string;
21
+ url: string;
22
+ name: string;
23
+ size: number;
24
+ };
25
+ userId: string;
26
+ }, Promise<any>>;
27
+ export declare const internalUpsertFile: import("convex/server").RegisteredMutation<"internal", {
28
+ options?: {
29
+ access?: {
30
+ allowUserIds?: string[] | undefined;
31
+ denyUserIds?: string[] | undefined;
32
+ visibility: "public" | "private" | "restricted";
33
+ } | undefined;
34
+ folder?: string | undefined;
35
+ fileType?: string | undefined;
36
+ tags?: string[] | undefined;
37
+ metadata?: any;
38
+ expiresAt?: number | undefined;
39
+ ttlMs?: number | undefined;
40
+ } | undefined;
41
+ file: {
42
+ uploadedAt?: number | undefined;
43
+ fileType?: string | undefined;
44
+ customId?: string | undefined;
45
+ key: string;
46
+ mimeType: string;
47
+ url: string;
48
+ name: string;
49
+ size: number;
50
+ };
51
+ userId: string;
52
+ }, Promise<any>>;
53
+ export declare const setFileAccess: import("convex/server").RegisteredMutation<"public", {
54
+ access?: {
55
+ allowUserIds?: string[] | undefined;
56
+ denyUserIds?: string[] | undefined;
57
+ visibility: "public" | "private" | "restricted";
58
+ } | null | undefined;
59
+ key: string;
60
+ }, Promise<import("convex/values").GenericId<"files"> | null>>;
61
+ export declare const setFolderAccess: import("convex/server").RegisteredMutation<"public", {
62
+ access?: {
63
+ allowUserIds?: string[] | undefined;
64
+ denyUserIds?: string[] | undefined;
65
+ visibility: "public" | "private" | "restricted";
66
+ } | null | undefined;
67
+ folder: string;
68
+ }, Promise<import("convex/values").GenericId<"folderRules"> | null>>;
69
+ export declare const deleteFilesByKey: import("convex/server").RegisteredMutation<"internal", {
70
+ keys: string[];
71
+ }, Promise<void>>;
72
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/component/files.ts"],"names":[],"mappings":"AA8EA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;gBAarB,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;gBAa7B,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;8DAmBxB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;oEAuC1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;iBAe3B,CAAC"}
@@ -0,0 +1,153 @@
1
+ import { mutation, internalMutation } from "./_generated/server";
2
+ import { v } from "convex/values";
3
+ import { computeExpiresAt, loadGlobals } from "./config";
4
+ import { accessRuleValidator, fileInfoValidator, fileUpsertOptionsValidator } from "./types";
5
+ import { sanitizeAccessRule } from "./access";
6
+ async function upsertFileRecord(ctx, params) {
7
+ const now = Date.now();
8
+ const globals = await loadGlobals(ctx);
9
+ const uploadedAt = params.file.uploadedAt ?? now;
10
+ const fileType = params.options?.fileType ?? params.file.fileType;
11
+ const expiresAt = computeExpiresAt({
12
+ now: uploadedAt,
13
+ mimeType: params.file.mimeType,
14
+ fileType,
15
+ expiresAt: params.options?.expiresAt,
16
+ ttlMs: params.options?.ttlMs,
17
+ globals,
18
+ });
19
+ const existing = await ctx.db
20
+ .query("files")
21
+ .withIndex("by_key", (q) => q.eq("key", params.file.key))
22
+ .unique();
23
+ const patch = {
24
+ url: params.file.url,
25
+ name: params.file.name,
26
+ size: params.file.size,
27
+ mimeType: params.file.mimeType,
28
+ uploadedAt,
29
+ userId: params.userId,
30
+ };
31
+ if (params.file.customId !== undefined)
32
+ patch.customId = params.file.customId;
33
+ if (fileType !== undefined)
34
+ patch.fileType = fileType;
35
+ if (params.options?.tags !== undefined)
36
+ patch.tags = params.options.tags;
37
+ if (params.options?.folder !== undefined)
38
+ patch.folder = params.options.folder;
39
+ if (params.options?.metadata !== undefined)
40
+ patch.metadata = params.options.metadata;
41
+ if (params.options?.access !== undefined) {
42
+ patch.access = sanitizeAccessRule(params.options.access);
43
+ }
44
+ if (expiresAt !== undefined)
45
+ patch.expiresAt = expiresAt;
46
+ if (existing) {
47
+ patch.replacedAt = now;
48
+ await ctx.db.patch(existing._id, patch);
49
+ return existing._id;
50
+ }
51
+ return await ctx.db.insert("files", {
52
+ key: params.file.key,
53
+ ...patch,
54
+ });
55
+ }
56
+ export const upsertFile = mutation({
57
+ args: {
58
+ file: fileInfoValidator,
59
+ userId: v.string(),
60
+ options: v.optional(fileUpsertOptionsValidator),
61
+ },
62
+ handler: async (ctx, args) => {
63
+ return await upsertFileRecord(ctx, {
64
+ file: args.file,
65
+ userId: args.userId,
66
+ options: args.options,
67
+ });
68
+ },
69
+ });
70
+ export const internalUpsertFile = internalMutation({
71
+ args: {
72
+ file: fileInfoValidator,
73
+ userId: v.string(),
74
+ options: v.optional(fileUpsertOptionsValidator),
75
+ },
76
+ handler: async (ctx, args) => {
77
+ return await upsertFileRecord(ctx, {
78
+ file: args.file,
79
+ userId: args.userId,
80
+ options: args.options,
81
+ });
82
+ },
83
+ });
84
+ export const setFileAccess = mutation({
85
+ args: {
86
+ key: v.string(),
87
+ access: v.optional(v.union(accessRuleValidator, v.null())),
88
+ },
89
+ handler: async (ctx, args) => {
90
+ const existing = await ctx.db
91
+ .query("files")
92
+ .withIndex("by_key", (q) => q.eq("key", args.key))
93
+ .unique();
94
+ if (!existing)
95
+ return null;
96
+ await ctx.db.patch(existing._id, {
97
+ access: args.access === null ? undefined : sanitizeAccessRule(args.access),
98
+ });
99
+ return existing._id;
100
+ },
101
+ });
102
+ export const setFolderAccess = mutation({
103
+ args: {
104
+ folder: v.string(),
105
+ access: v.optional(v.union(accessRuleValidator, v.null())),
106
+ },
107
+ handler: async (ctx, args) => {
108
+ const existing = await ctx.db
109
+ .query("folderRules")
110
+ .withIndex("by_folder", (q) => q.eq("folder", args.folder))
111
+ .unique();
112
+ if (args.access === null) {
113
+ if (existing) {
114
+ await ctx.db.delete(existing._id);
115
+ }
116
+ return null;
117
+ }
118
+ const access = sanitizeAccessRule(args.access);
119
+ if (!access) {
120
+ return null;
121
+ }
122
+ const updatedAt = Date.now();
123
+ if (existing) {
124
+ await ctx.db.patch(existing._id, {
125
+ access,
126
+ updatedAt,
127
+ });
128
+ return existing._id;
129
+ }
130
+ return await ctx.db.insert("folderRules", {
131
+ folder: args.folder,
132
+ access,
133
+ updatedAt,
134
+ });
135
+ },
136
+ });
137
+ export const deleteFilesByKey = internalMutation({
138
+ args: {
139
+ keys: v.array(v.string()),
140
+ },
141
+ handler: async (ctx, args) => {
142
+ for (const key of args.keys) {
143
+ const existing = await ctx.db
144
+ .query("files")
145
+ .withIndex("by_key", (q) => q.eq("key", key))
146
+ .unique();
147
+ if (existing) {
148
+ await ctx.db.delete(existing._id);
149
+ }
150
+ }
151
+ },
152
+ });
153
+ //# sourceMappingURL=files.js.map