@edgebasejs/core 0.1.7 → 0.1.9
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/README.md +15 -0
- package/dist/core/src/access-rules/column-security.d.ts +80 -0
- package/dist/core/src/access-rules/column-security.d.ts.map +1 -0
- package/dist/core/src/access-rules/column-security.js +191 -0
- package/dist/core/src/access-rules/column-security.js.map +1 -0
- package/dist/core/src/access-rules/engine.d.ts.map +1 -1
- package/dist/core/src/access-rules/engine.js +2 -1
- package/dist/core/src/access-rules/engine.js.map +1 -1
- package/dist/core/src/audit/audit-manager.d.ts +108 -0
- package/dist/core/src/audit/audit-manager.d.ts.map +1 -0
- package/dist/core/src/audit/audit-manager.js +265 -0
- package/dist/core/src/audit/audit-manager.js.map +1 -0
- package/dist/core/src/encryption/encryption-manager.d.ts +97 -0
- package/dist/core/src/encryption/encryption-manager.d.ts.map +1 -0
- package/dist/core/src/encryption/encryption-manager.js +224 -0
- package/dist/core/src/encryption/encryption-manager.js.map +1 -0
- package/dist/core/src/index.d.ts +12 -0
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/index.js +12 -0
- package/dist/core/src/index.js.map +1 -1
- package/dist/core/src/realtime/change-notifier.d.ts +50 -0
- package/dist/core/src/realtime/change-notifier.d.ts.map +1 -0
- package/dist/core/src/realtime/change-notifier.js +145 -0
- package/dist/core/src/realtime/change-notifier.js.map +1 -0
- package/dist/core/src/realtime/message-types.d.ts +39 -0
- package/dist/core/src/realtime/message-types.d.ts.map +1 -0
- package/dist/core/src/realtime/message-types.js +5 -0
- package/dist/core/src/realtime/message-types.js.map +1 -0
- package/dist/core/src/realtime/subscription-manager.d.ts +67 -0
- package/dist/core/src/realtime/subscription-manager.d.ts.map +1 -0
- package/dist/core/src/realtime/subscription-manager.js +229 -0
- package/dist/core/src/realtime/subscription-manager.js.map +1 -0
- package/dist/core/src/search/search-manager.d.ts +93 -0
- package/dist/core/src/search/search-manager.d.ts.map +1 -0
- package/dist/core/src/search/search-manager.js +258 -0
- package/dist/core/src/search/search-manager.js.map +1 -0
- package/dist/core/src/storage/file-manager.d.ts +138 -0
- package/dist/core/src/storage/file-manager.d.ts.map +1 -0
- package/dist/core/src/storage/file-manager.js +224 -0
- package/dist/core/src/storage/file-manager.js.map +1 -0
- package/dist/core/src/sync/batch-processor.d.ts +97 -0
- package/dist/core/src/sync/batch-processor.d.ts.map +1 -0
- package/dist/core/src/sync/batch-processor.js +313 -0
- package/dist/core/src/sync/batch-processor.js.map +1 -0
- package/dist/core/src/sync/csv-processor.d.ts +66 -0
- package/dist/core/src/sync/csv-processor.d.ts.map +1 -0
- package/dist/core/src/sync/csv-processor.js +223 -0
- package/dist/core/src/sync/csv-processor.js.map +1 -0
- package/dist/core/src/sync/sync-engine.d.ts +22 -0
- package/dist/core/src/sync/sync-engine.d.ts.map +1 -1
- package/dist/core/src/sync/sync-engine.js +123 -10
- package/dist/core/src/sync/sync-engine.js.map +1 -1
- package/dist/core/src/sync/transaction-manager.d.ts +83 -0
- package/dist/core/src/sync/transaction-manager.d.ts.map +1 -0
- package/dist/core/src/sync/transaction-manager.js +227 -0
- package/dist/core/src/sync/transaction-manager.js.map +1 -0
- package/dist/core/src/webhooks/webhook-manager.d.ts +137 -0
- package/dist/core/src/webhooks/webhook-manager.d.ts.map +1 -0
- package/dist/core/src/webhooks/webhook-manager.js +334 -0
- package/dist/core/src/webhooks/webhook-manager.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-manager.d.ts","sourceRoot":"","sources":["../../../../src/search/search-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,OAAO,CAAuC;gBAE1C,EAAE,EAAE,cAAc;IAI9B;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAIvC;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyCtD;;OAEG;YACW,kBAAkB;IAuChC;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBnD;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IA0FzD;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAetD;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAC3C,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAoBF;;OAEG;IACH,oBAAoB,IAAI,WAAW,EAAE;IAIrC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAGlC"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-text search manager using SQLite FTS5
|
|
3
|
+
* Handles search index creation, updating, and querying
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Search manager for full-text search
|
|
7
|
+
*/
|
|
8
|
+
export class SearchManager {
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.indexes = new Map();
|
|
11
|
+
this.db = db;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Register a search index for an entity
|
|
15
|
+
*/
|
|
16
|
+
registerIndex(index) {
|
|
17
|
+
this.indexes.set(index.entity, index);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create FTS5 virtual table for an entity
|
|
21
|
+
*/
|
|
22
|
+
async createSearchIndex(entity) {
|
|
23
|
+
const index = this.indexes.get(entity);
|
|
24
|
+
if (!index) {
|
|
25
|
+
throw new Error(`No search index configured for entity: ${entity}`);
|
|
26
|
+
}
|
|
27
|
+
const ftsTableName = `${entity}_fts`;
|
|
28
|
+
// Build FTS5 options
|
|
29
|
+
const ftsOptions = [];
|
|
30
|
+
// Add columns
|
|
31
|
+
ftsOptions.push(...index.columns);
|
|
32
|
+
// Add tokenizer
|
|
33
|
+
if (index.tokenize) {
|
|
34
|
+
ftsOptions.push(`tokenize='${index.tokenize}'`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
ftsOptions.push(`tokenize='porter unicode61'`); // Default: porter stemming + unicode
|
|
38
|
+
}
|
|
39
|
+
// Add prefix indexes for autocomplete
|
|
40
|
+
if (index.prefix && index.prefix.length > 0) {
|
|
41
|
+
ftsOptions.push(`prefix='${index.prefix.join(' ')}'`);
|
|
42
|
+
}
|
|
43
|
+
// Contentless option (smaller index, no snippet support)
|
|
44
|
+
if (index.contentless) {
|
|
45
|
+
ftsOptions.push(`content=''`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
ftsOptions.push(`content='${entity}'`);
|
|
49
|
+
}
|
|
50
|
+
// Create FTS5 virtual table
|
|
51
|
+
const createSQL = `CREATE VIRTUAL TABLE IF NOT EXISTS ${ftsTableName} USING fts5(${ftsOptions.join(', ')})`;
|
|
52
|
+
await this.db.run(createSQL, []);
|
|
53
|
+
// Create triggers to keep FTS index in sync with main table
|
|
54
|
+
await this.createSyncTriggers(entity, index);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create triggers to automatically update FTS index
|
|
58
|
+
*/
|
|
59
|
+
async createSyncTriggers(entity, index) {
|
|
60
|
+
const ftsTableName = `${entity}_fts`;
|
|
61
|
+
const columns = index.columns.join(', ');
|
|
62
|
+
const columnValues = index.columns.map((col) => `NEW.${col}`).join(', ');
|
|
63
|
+
// Insert trigger
|
|
64
|
+
await this.db.run(`CREATE TRIGGER IF NOT EXISTS ${entity}_fts_insert
|
|
65
|
+
AFTER INSERT ON ${entity}
|
|
66
|
+
BEGIN
|
|
67
|
+
INSERT INTO ${ftsTableName} (rowid, ${columns})
|
|
68
|
+
VALUES (NEW.rowid, ${columnValues});
|
|
69
|
+
END`, []);
|
|
70
|
+
// Update trigger
|
|
71
|
+
await this.db.run(`CREATE TRIGGER IF NOT EXISTS ${entity}_fts_update
|
|
72
|
+
AFTER UPDATE ON ${entity}
|
|
73
|
+
BEGIN
|
|
74
|
+
UPDATE ${ftsTableName}
|
|
75
|
+
SET ${index.columns.map((col) => `${col} = NEW.${col}`).join(', ')}
|
|
76
|
+
WHERE rowid = NEW.rowid;
|
|
77
|
+
END`, []);
|
|
78
|
+
// Delete trigger
|
|
79
|
+
await this.db.run(`CREATE TRIGGER IF NOT EXISTS ${entity}_fts_delete
|
|
80
|
+
AFTER DELETE ON ${entity}
|
|
81
|
+
BEGIN
|
|
82
|
+
DELETE FROM ${ftsTableName} WHERE rowid = OLD.rowid;
|
|
83
|
+
END`, []);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Rebuild search index from scratch
|
|
87
|
+
*/
|
|
88
|
+
async rebuildIndex(entity) {
|
|
89
|
+
const index = this.indexes.get(entity);
|
|
90
|
+
if (!index) {
|
|
91
|
+
throw new Error(`No search index configured for entity: ${entity}`);
|
|
92
|
+
}
|
|
93
|
+
const ftsTableName = `${entity}_fts`;
|
|
94
|
+
// Clear existing FTS data
|
|
95
|
+
await this.db.run(`DELETE FROM ${ftsTableName}`, []);
|
|
96
|
+
// Rebuild from main table
|
|
97
|
+
const columns = index.columns.join(', ');
|
|
98
|
+
const insertSQL = `
|
|
99
|
+
INSERT INTO ${ftsTableName} (rowid, ${columns})
|
|
100
|
+
SELECT rowid, ${columns} FROM ${entity}
|
|
101
|
+
`;
|
|
102
|
+
await this.db.run(insertSQL, []);
|
|
103
|
+
// Get count
|
|
104
|
+
const result = await this.db.getOne(`SELECT COUNT(*) as count FROM ${ftsTableName}`, []);
|
|
105
|
+
return result?.count || 0;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Search for records
|
|
109
|
+
*/
|
|
110
|
+
async search(query) {
|
|
111
|
+
const index = this.indexes.get(query.entity);
|
|
112
|
+
if (!index) {
|
|
113
|
+
throw new Error(`No search index configured for entity: ${query.entity}`);
|
|
114
|
+
}
|
|
115
|
+
const ftsTableName = `${query.entity}_fts`;
|
|
116
|
+
const limit = query.limit || 20;
|
|
117
|
+
const offset = query.offset || 0;
|
|
118
|
+
// Build FTS5 query
|
|
119
|
+
const ftsQuery = this.buildFTSQuery(query.query, query.columns);
|
|
120
|
+
// Build SQL with optional ranking and highlighting
|
|
121
|
+
let sql = `
|
|
122
|
+
SELECT
|
|
123
|
+
${query.entity}.id,
|
|
124
|
+
${query.entity}.*
|
|
125
|
+
`;
|
|
126
|
+
if (query.rank) {
|
|
127
|
+
sql += `, ${ftsTableName}.rank as _rank`;
|
|
128
|
+
}
|
|
129
|
+
if (query.highlight) {
|
|
130
|
+
// Use snippet function for highlighting
|
|
131
|
+
const snippetColumns = (query.columns || index.columns).map((col) => {
|
|
132
|
+
return `snippet(${ftsTableName}, ${index.columns.indexOf(col)}, '<mark>', '</mark>', '...', 32)`;
|
|
133
|
+
});
|
|
134
|
+
sql += `, ${snippetColumns[0]} as _snippet`;
|
|
135
|
+
}
|
|
136
|
+
sql += `
|
|
137
|
+
FROM ${ftsTableName}
|
|
138
|
+
JOIN ${query.entity} ON ${ftsTableName}.rowid = ${query.entity}.rowid
|
|
139
|
+
WHERE ${ftsTableName} MATCH ?
|
|
140
|
+
`;
|
|
141
|
+
if (query.rank) {
|
|
142
|
+
sql += ` ORDER BY ${ftsTableName}.rank`;
|
|
143
|
+
}
|
|
144
|
+
sql += ` LIMIT ? OFFSET ?`;
|
|
145
|
+
const results = await this.db.getAll(sql, [ftsQuery, limit + 1, offset]);
|
|
146
|
+
// Check if there are more results
|
|
147
|
+
const hasMore = results.length > limit;
|
|
148
|
+
const trimmedResults = hasMore ? results.slice(0, limit) : results;
|
|
149
|
+
// Get total count
|
|
150
|
+
const countSQL = `
|
|
151
|
+
SELECT COUNT(*) as total
|
|
152
|
+
FROM ${ftsTableName}
|
|
153
|
+
WHERE ${ftsTableName} MATCH ?
|
|
154
|
+
`;
|
|
155
|
+
const countResult = await this.db.getOne(countSQL, [ftsQuery]);
|
|
156
|
+
const total = countResult?.total || 0;
|
|
157
|
+
// Format results
|
|
158
|
+
const formattedResults = trimmedResults.map((row) => {
|
|
159
|
+
const result = {
|
|
160
|
+
id: row.id,
|
|
161
|
+
};
|
|
162
|
+
if (query.rank) {
|
|
163
|
+
result.rank = row._rank;
|
|
164
|
+
}
|
|
165
|
+
if (query.highlight) {
|
|
166
|
+
result.snippet = row._snippet;
|
|
167
|
+
}
|
|
168
|
+
// Add all other columns
|
|
169
|
+
for (const key in row) {
|
|
170
|
+
if (key !== '_rank' && key !== '_snippet' && key !== 'rowid') {
|
|
171
|
+
result[key] = row[key];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
results: formattedResults,
|
|
178
|
+
total,
|
|
179
|
+
hasMore,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Build FTS5 query string from user query
|
|
184
|
+
*/
|
|
185
|
+
buildFTSQuery(query, columns) {
|
|
186
|
+
// Escape special FTS5 characters
|
|
187
|
+
let escapedQuery = query.replace(/["']/g, '');
|
|
188
|
+
// If specific columns are specified, use column filter
|
|
189
|
+
if (columns && columns.length > 0) {
|
|
190
|
+
const columnQueries = columns.map((col) => `${col}:${escapedQuery}`);
|
|
191
|
+
return columnQueries.join(' OR ');
|
|
192
|
+
}
|
|
193
|
+
// Support phrase queries (quoted strings)
|
|
194
|
+
if (query.includes('"')) {
|
|
195
|
+
return query;
|
|
196
|
+
}
|
|
197
|
+
// Support prefix matching with *
|
|
198
|
+
if (!query.endsWith('*')) {
|
|
199
|
+
const words = escapedQuery.split(/\s+/).filter((w) => w.length > 0);
|
|
200
|
+
// Add prefix matching to last word for autocomplete
|
|
201
|
+
if (words.length > 0) {
|
|
202
|
+
words[words.length - 1] += '*';
|
|
203
|
+
}
|
|
204
|
+
return words.join(' ');
|
|
205
|
+
}
|
|
206
|
+
return escapedQuery;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Delete search index
|
|
210
|
+
*/
|
|
211
|
+
async deleteSearchIndex(entity) {
|
|
212
|
+
const ftsTableName = `${entity}_fts`;
|
|
213
|
+
// Drop triggers
|
|
214
|
+
await this.db.run(`DROP TRIGGER IF EXISTS ${entity}_fts_insert`, []);
|
|
215
|
+
await this.db.run(`DROP TRIGGER IF EXISTS ${entity}_fts_update`, []);
|
|
216
|
+
await this.db.run(`DROP TRIGGER IF EXISTS ${entity}_fts_delete`, []);
|
|
217
|
+
// Drop FTS table
|
|
218
|
+
await this.db.run(`DROP TABLE IF EXISTS ${ftsTableName}`, []);
|
|
219
|
+
// Remove from registered indexes
|
|
220
|
+
this.indexes.delete(entity);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Optimize search index (FTS5 optimize)
|
|
224
|
+
*/
|
|
225
|
+
async optimizeIndex(entity) {
|
|
226
|
+
const ftsTableName = `${entity}_fts`;
|
|
227
|
+
await this.db.run(`INSERT INTO ${ftsTableName}(${ftsTableName}) VALUES('optimize')`, []);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get search index statistics
|
|
231
|
+
*/
|
|
232
|
+
async getIndexStats(entity) {
|
|
233
|
+
const ftsTableName = `${entity}_fts`;
|
|
234
|
+
const countResult = await this.db.getOne(`SELECT COUNT(*) as count FROM ${ftsTableName}`, []);
|
|
235
|
+
const documentCount = countResult?.count || 0;
|
|
236
|
+
// Get index size (approximate)
|
|
237
|
+
const sizeResult = await this.db.getOne(`SELECT page_count * page_size as size FROM pragma_page_count('${ftsTableName}'), pragma_page_size()`, []);
|
|
238
|
+
const indexSize = sizeResult?.size || 0;
|
|
239
|
+
return {
|
|
240
|
+
entity,
|
|
241
|
+
documentCount,
|
|
242
|
+
indexSize,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get all registered indexes
|
|
247
|
+
*/
|
|
248
|
+
getRegisteredIndexes() {
|
|
249
|
+
return Array.from(this.indexes.values());
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Check if an entity has a search index
|
|
253
|
+
*/
|
|
254
|
+
hasIndex(entity) {
|
|
255
|
+
return this.indexes.has(entity);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=search-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-manager.js","sourceRoot":"","sources":["../../../../src/search/search-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuCH;;GAEG;AACH,MAAM,OAAO,aAAa;IAIxB,YAAY,EAAkB;QAFtB,YAAO,GAA6B,IAAI,GAAG,EAAE,CAAC;QAGpD,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,KAAkB;QAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,MAAc;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,YAAY,GAAG,GAAG,MAAM,MAAM,CAAC;QAErC,qBAAqB;QACrB,MAAM,UAAU,GAAa,EAAE,CAAC;QAEhC,cAAc;QACd,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QAElC,gBAAgB;QAChB,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,UAAU,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC,qCAAqC;QACvF,CAAC;QAED,sCAAsC;QACtC,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,UAAU,CAAC,IAAI,CAAC,WAAW,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxD,CAAC;QAED,yDAAyD;QACzD,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACtB,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,YAAY,MAAM,GAAG,CAAC,CAAC;QACzC,CAAC;QAED,4BAA4B;QAC5B,MAAM,SAAS,GAAG,sCAAsC,YAAY,eAAe,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAC5G,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEjC,4DAA4D;QAC5D,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,kBAAkB,CAAC,MAAc,EAAE,KAAkB;QACjE,MAAM,YAAY,GAAG,GAAG,MAAM,MAAM,CAAC;QACrC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEzE,iBAAiB;QACjB,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CACf,gCAAgC,MAAM;yBACnB,MAAM;;uBAER,YAAY,YAAY,OAAO;8BACxB,YAAY;WAC/B,EACL,EAAE,CACH,CAAC;QAEF,iBAAiB;QACjB,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CACf,gCAAgC,MAAM;yBACnB,MAAM;;kBAEb,YAAY;eACf,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,UAAU,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;WAEhE,EACL,EAAE,CACH,CAAC;QAEF,iBAAiB;QACjB,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CACf,gCAAgC,MAAM;yBACnB,MAAM;;uBAER,YAAY;WACxB,EACL,EAAE,CACH,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,MAAc;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,YAAY,GAAG,GAAG,MAAM,MAAM,CAAC;QAErC,0BAA0B;QAC1B,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QAErD,0BAA0B;QAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG;oBACF,YAAY,YAAY,OAAO;sBAC7B,OAAO,SAAS,MAAM;KACvC,CAAC;QACF,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEjC,YAAY;QACZ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,iCAAiC,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QACzF,OAAO,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,KAAkB;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,YAAY,GAAG,GAAG,KAAK,CAAC,MAAM,MAAM,CAAC;QAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAEjC,mBAAmB;QACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAEhE,mDAAmD;QACnD,IAAI,GAAG,GAAG;;UAEJ,KAAK,CAAC,MAAM;UACZ,KAAK,CAAC,MAAM;KACjB,CAAC;QAEF,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,GAAG,IAAI,KAAK,YAAY,gBAAgB,CAAC;QAC3C,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,wCAAwC;YACxC,MAAM,cAAc,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBAClE,OAAO,WAAW,YAAY,KAAK,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC;YACnG,CAAC,CAAC,CAAC;YACH,GAAG,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC;QAC9C,CAAC;QAED,GAAG,IAAI;aACE,YAAY;aACZ,KAAK,CAAC,MAAM,OAAO,YAAY,YAAY,KAAK,CAAC,MAAM;cACtD,YAAY;KACrB,CAAC;QAEF,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,GAAG,IAAI,aAAa,YAAY,OAAO,CAAC;QAC1C,CAAC;QAED,GAAG,IAAI,mBAAmB,CAAC;QAE3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAEzE,kCAAkC;QAClC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;QACvC,MAAM,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAEnE,kBAAkB;QAClB,MAAM,QAAQ,GAAG;;aAER,YAAY;cACX,YAAY;KACrB,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC;QAEtC,iBAAiB;QACjB,MAAM,gBAAgB,GAAmB,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YAClE,MAAM,MAAM,GAAiB;gBAC3B,EAAE,EAAE,GAAG,CAAC,EAAE;aACX,CAAC;YAEF,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC;YAC1B,CAAC;YAED,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACpB,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;YAChC,CAAC;YAED,wBAAwB;YACxB,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;gBACtB,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;oBAC7D,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;gBACzB,CAAC;YACH,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EAAE,gBAAgB;YACzB,KAAK;YACL,OAAO;SACR,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,KAAa,EAAE,OAAkB;QACrD,iCAAiC;QACjC,IAAI,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAE9C,uDAAuD;QACvD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,YAAY,EAAE,CAAC,CAAC;YACrE,OAAO,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;QAED,0CAA0C;QAC1C,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,iCAAiC;QACjC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACpE,oDAAoD;YACpD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC;YACjC,CAAC;YACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,MAAc;QACpC,MAAM,YAAY,GAAG,GAAG,MAAM,MAAM,CAAC;QAErC,gBAAgB;QAChB,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,0BAA0B,MAAM,aAAa,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,0BAA0B,MAAM,aAAa,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,0BAA0B,MAAM,aAAa,EAAE,EAAE,CAAC,CAAC;QAErE,iBAAiB;QACjB,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,wBAAwB,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QAE9D,iCAAiC;QACjC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,MAAc;QAChC,MAAM,YAAY,GAAG,GAAG,MAAM,MAAM,CAAC;QACrC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,YAAY,IAAI,YAAY,sBAAsB,EAAE,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,MAAc;QAKhC,MAAM,YAAY,GAAG,GAAG,MAAM,MAAM,CAAC;QAErC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,iCAAiC,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9F,MAAM,aAAa,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC;QAE9C,+BAA+B;QAC/B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CACrC,iEAAiE,YAAY,wBAAwB,EACrG,EAAE,CACH,CAAC;QACF,MAAM,SAAS,GAAG,UAAU,EAAE,IAAI,IAAI,CAAC,CAAC;QAExC,OAAO;YACL,MAAM;YACN,aAAa;YACb,SAAS;SACV,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,oBAAoB;QAClB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,MAAc;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;CACF"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File storage manager for Cloudflare R2
|
|
3
|
+
* Handles upload, download, and file metadata management
|
|
4
|
+
*/
|
|
5
|
+
import type { User } from '@edgebasejs/types';
|
|
6
|
+
export interface R2Bucket {
|
|
7
|
+
put(key: string, value: ReadableStream | ArrayBuffer | string, options?: R2PutOptions): Promise<R2Object>;
|
|
8
|
+
get(key: string, options?: R2GetOptions): Promise<R2ObjectBody | null>;
|
|
9
|
+
delete(key: string): Promise<void>;
|
|
10
|
+
head(key: string): Promise<R2Object | null>;
|
|
11
|
+
list(options?: R2ListOptions): Promise<R2Objects>;
|
|
12
|
+
}
|
|
13
|
+
export interface R2PutOptions {
|
|
14
|
+
httpMetadata?: R2HTTPMetadata;
|
|
15
|
+
customMetadata?: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export interface R2GetOptions {
|
|
18
|
+
range?: {
|
|
19
|
+
offset?: number;
|
|
20
|
+
length?: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface R2ListOptions {
|
|
24
|
+
prefix?: string;
|
|
25
|
+
limit?: number;
|
|
26
|
+
cursor?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface R2HTTPMetadata {
|
|
29
|
+
contentType?: string;
|
|
30
|
+
contentLanguage?: string;
|
|
31
|
+
contentDisposition?: string;
|
|
32
|
+
contentEncoding?: string;
|
|
33
|
+
cacheControl?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface R2Object {
|
|
36
|
+
key: string;
|
|
37
|
+
version: string;
|
|
38
|
+
size: number;
|
|
39
|
+
etag: string;
|
|
40
|
+
httpMetadata?: R2HTTPMetadata;
|
|
41
|
+
customMetadata?: Record<string, string>;
|
|
42
|
+
uploaded: Date;
|
|
43
|
+
}
|
|
44
|
+
export interface R2ObjectBody extends R2Object {
|
|
45
|
+
body: ReadableStream;
|
|
46
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
47
|
+
text(): Promise<string>;
|
|
48
|
+
json<T>(): Promise<T>;
|
|
49
|
+
blob(): Promise<Blob>;
|
|
50
|
+
}
|
|
51
|
+
export interface R2Objects {
|
|
52
|
+
objects: R2Object[];
|
|
53
|
+
truncated: boolean;
|
|
54
|
+
cursor?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface FileMetadata {
|
|
57
|
+
id: string;
|
|
58
|
+
key: string;
|
|
59
|
+
fileName: string;
|
|
60
|
+
mimeType: string;
|
|
61
|
+
size: number;
|
|
62
|
+
userId: string;
|
|
63
|
+
bucket: string;
|
|
64
|
+
entityType?: string;
|
|
65
|
+
entityId?: string;
|
|
66
|
+
isPublic: boolean;
|
|
67
|
+
uploadedAt: number;
|
|
68
|
+
expiresAt?: number;
|
|
69
|
+
checksum?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface UploadOptions {
|
|
72
|
+
fileName: string;
|
|
73
|
+
mimeType: string;
|
|
74
|
+
size: number;
|
|
75
|
+
entityType?: string;
|
|
76
|
+
entityId?: string;
|
|
77
|
+
isPublic?: boolean;
|
|
78
|
+
expiresIn?: number;
|
|
79
|
+
maxSize?: number;
|
|
80
|
+
}
|
|
81
|
+
export interface FileDatabase {
|
|
82
|
+
run(sql: string, params: any[]): Promise<any>;
|
|
83
|
+
getOne(sql: string, params: any[]): Promise<any>;
|
|
84
|
+
getAll(sql: string, params: any[]): Promise<any[]>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* File storage manager
|
|
88
|
+
*/
|
|
89
|
+
export declare class FileStorageManager {
|
|
90
|
+
private bucket;
|
|
91
|
+
private db;
|
|
92
|
+
private maxFileSize;
|
|
93
|
+
private allowedMimeTypes?;
|
|
94
|
+
constructor(bucket: R2Bucket, db: FileDatabase, options?: {
|
|
95
|
+
maxFileSize?: number;
|
|
96
|
+
allowedMimeTypes?: string[];
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* Upload a file to R2
|
|
100
|
+
*/
|
|
101
|
+
uploadFile(user: User, file: ReadableStream | ArrayBuffer | string, options: UploadOptions): Promise<FileMetadata>;
|
|
102
|
+
/**
|
|
103
|
+
* Download a file from R2
|
|
104
|
+
*/
|
|
105
|
+
downloadFile(fileId: string, user: User): Promise<R2ObjectBody | null>;
|
|
106
|
+
/**
|
|
107
|
+
* Delete a file
|
|
108
|
+
*/
|
|
109
|
+
deleteFile(fileId: string, user: User): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Get file metadata
|
|
112
|
+
*/
|
|
113
|
+
getFileMetadata(fileId: string): Promise<FileMetadata | null>;
|
|
114
|
+
/**
|
|
115
|
+
* List files for a user
|
|
116
|
+
*/
|
|
117
|
+
listFiles(user: User, options?: {
|
|
118
|
+
entityType?: string;
|
|
119
|
+
entityId?: string;
|
|
120
|
+
limit?: number;
|
|
121
|
+
offset?: number;
|
|
122
|
+
}): Promise<FileMetadata[]>;
|
|
123
|
+
/**
|
|
124
|
+
* Generate a signed URL for temporary access
|
|
125
|
+
* Note: R2 doesn't support presigned URLs natively yet, so this returns a direct URL
|
|
126
|
+
* In production, you'd implement token-based access or use Workers to proxy requests
|
|
127
|
+
*/
|
|
128
|
+
generateSignedUrl(fileId: string, user: User, expiresIn?: number): Promise<string>;
|
|
129
|
+
/**
|
|
130
|
+
* Verify a signed URL token
|
|
131
|
+
*/
|
|
132
|
+
verifyToken(fileId: string, token: string): Promise<boolean>;
|
|
133
|
+
/**
|
|
134
|
+
* Clean up expired files
|
|
135
|
+
*/
|
|
136
|
+
cleanupExpiredFiles(): Promise<number>;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=file-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-manager.d.ts","sourceRoot":"","sources":["../../../../src/storage/file-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File storage manager for Cloudflare R2
|
|
3
|
+
* Handles upload, download, and file metadata management
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* File storage manager
|
|
7
|
+
*/
|
|
8
|
+
export class FileStorageManager {
|
|
9
|
+
constructor(bucket, db, options) {
|
|
10
|
+
this.bucket = bucket;
|
|
11
|
+
this.db = db;
|
|
12
|
+
this.maxFileSize = options?.maxFileSize || 100 * 1024 * 1024; // 100MB default
|
|
13
|
+
this.allowedMimeTypes = options?.allowedMimeTypes;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Upload a file to R2
|
|
17
|
+
*/
|
|
18
|
+
async uploadFile(user, file, options) {
|
|
19
|
+
// Validate file size
|
|
20
|
+
if (options.size > (options.maxSize || this.maxFileSize)) {
|
|
21
|
+
throw new Error(`File size (${options.size} bytes) exceeds maximum allowed size (${options.maxSize || this.maxFileSize} bytes)`);
|
|
22
|
+
}
|
|
23
|
+
// Validate MIME type
|
|
24
|
+
if (this.allowedMimeTypes && !this.allowedMimeTypes.includes(options.mimeType)) {
|
|
25
|
+
throw new Error(`File type ${options.mimeType} is not allowed`);
|
|
26
|
+
}
|
|
27
|
+
const fileId = `${user.id}_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
|
|
28
|
+
const key = `${user.id}/${fileId}/${options.fileName}`;
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
// Upload to R2
|
|
31
|
+
await this.bucket.put(key, file, {
|
|
32
|
+
httpMetadata: {
|
|
33
|
+
contentType: options.mimeType,
|
|
34
|
+
contentDisposition: `attachment; filename="${options.fileName}"`,
|
|
35
|
+
},
|
|
36
|
+
customMetadata: {
|
|
37
|
+
userId: user.id,
|
|
38
|
+
uploadedAt: now.toString(),
|
|
39
|
+
...(options.entityType ? { entityType: options.entityType } : {}),
|
|
40
|
+
...(options.entityId ? { entityId: options.entityId } : {}),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
// Save metadata to database
|
|
44
|
+
const metadata = {
|
|
45
|
+
id: fileId,
|
|
46
|
+
key,
|
|
47
|
+
fileName: options.fileName,
|
|
48
|
+
mimeType: options.mimeType,
|
|
49
|
+
size: options.size,
|
|
50
|
+
userId: user.id,
|
|
51
|
+
bucket: 'default',
|
|
52
|
+
entityType: options.entityType,
|
|
53
|
+
entityId: options.entityId,
|
|
54
|
+
isPublic: options.isPublic || false,
|
|
55
|
+
uploadedAt: now,
|
|
56
|
+
expiresAt: options.expiresIn ? now + options.expiresIn * 1000 : undefined,
|
|
57
|
+
};
|
|
58
|
+
await this.db.run(`INSERT INTO files (id, key, file_name, mime_type, size, user_id, bucket, entity_type, entity_id, is_public, uploaded_at, expires_at)
|
|
59
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
60
|
+
metadata.id,
|
|
61
|
+
metadata.key,
|
|
62
|
+
metadata.fileName,
|
|
63
|
+
metadata.mimeType,
|
|
64
|
+
metadata.size,
|
|
65
|
+
metadata.userId,
|
|
66
|
+
metadata.bucket,
|
|
67
|
+
metadata.entityType || null,
|
|
68
|
+
metadata.entityId || null,
|
|
69
|
+
metadata.isPublic ? 1 : 0,
|
|
70
|
+
metadata.uploadedAt,
|
|
71
|
+
metadata.expiresAt || null,
|
|
72
|
+
]);
|
|
73
|
+
return metadata;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Download a file from R2
|
|
77
|
+
*/
|
|
78
|
+
async downloadFile(fileId, user) {
|
|
79
|
+
// Get file metadata
|
|
80
|
+
const metadata = await this.getFileMetadata(fileId);
|
|
81
|
+
if (!metadata) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
// Check permissions
|
|
85
|
+
if (!metadata.isPublic && metadata.userId !== user.id) {
|
|
86
|
+
throw new Error('Access denied');
|
|
87
|
+
}
|
|
88
|
+
// Check if expired
|
|
89
|
+
if (metadata.expiresAt && metadata.expiresAt < Date.now()) {
|
|
90
|
+
throw new Error('File has expired');
|
|
91
|
+
}
|
|
92
|
+
// Download from R2
|
|
93
|
+
return await this.bucket.get(metadata.key);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Delete a file
|
|
97
|
+
*/
|
|
98
|
+
async deleteFile(fileId, user) {
|
|
99
|
+
// Get file metadata
|
|
100
|
+
const metadata = await this.getFileMetadata(fileId);
|
|
101
|
+
if (!metadata) {
|
|
102
|
+
throw new Error('File not found');
|
|
103
|
+
}
|
|
104
|
+
// Check permissions
|
|
105
|
+
if (metadata.userId !== user.id) {
|
|
106
|
+
throw new Error('Access denied');
|
|
107
|
+
}
|
|
108
|
+
// Delete from R2
|
|
109
|
+
await this.bucket.delete(metadata.key);
|
|
110
|
+
// Delete metadata from database
|
|
111
|
+
await this.db.run('DELETE FROM files WHERE id = ?', [fileId]);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get file metadata
|
|
115
|
+
*/
|
|
116
|
+
async getFileMetadata(fileId) {
|
|
117
|
+
const row = await this.db.getOne('SELECT * FROM files WHERE id = ?', [fileId]);
|
|
118
|
+
if (!row) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
id: row.id,
|
|
123
|
+
key: row.key,
|
|
124
|
+
fileName: row.file_name,
|
|
125
|
+
mimeType: row.mime_type,
|
|
126
|
+
size: row.size,
|
|
127
|
+
userId: row.user_id,
|
|
128
|
+
bucket: row.bucket,
|
|
129
|
+
entityType: row.entity_type,
|
|
130
|
+
entityId: row.entity_id,
|
|
131
|
+
isPublic: row.is_public === 1,
|
|
132
|
+
uploadedAt: row.uploaded_at,
|
|
133
|
+
expiresAt: row.expires_at,
|
|
134
|
+
checksum: row.checksum,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* List files for a user
|
|
139
|
+
*/
|
|
140
|
+
async listFiles(user, options) {
|
|
141
|
+
let query = 'SELECT * FROM files WHERE user_id = ?';
|
|
142
|
+
const params = [user.id];
|
|
143
|
+
if (options?.entityType) {
|
|
144
|
+
query += ' AND entity_type = ?';
|
|
145
|
+
params.push(options.entityType);
|
|
146
|
+
}
|
|
147
|
+
if (options?.entityId) {
|
|
148
|
+
query += ' AND entity_id = ?';
|
|
149
|
+
params.push(options.entityId);
|
|
150
|
+
}
|
|
151
|
+
query += ' ORDER BY uploaded_at DESC';
|
|
152
|
+
if (options?.limit) {
|
|
153
|
+
query += ' LIMIT ?';
|
|
154
|
+
params.push(options.limit);
|
|
155
|
+
}
|
|
156
|
+
if (options?.offset) {
|
|
157
|
+
query += ' OFFSET ?';
|
|
158
|
+
params.push(options.offset);
|
|
159
|
+
}
|
|
160
|
+
const rows = await this.db.getAll(query, params);
|
|
161
|
+
return rows.map((row) => ({
|
|
162
|
+
id: row.id,
|
|
163
|
+
key: row.key,
|
|
164
|
+
fileName: row.file_name,
|
|
165
|
+
mimeType: row.mime_type,
|
|
166
|
+
size: row.size,
|
|
167
|
+
userId: row.user_id,
|
|
168
|
+
bucket: row.bucket,
|
|
169
|
+
entityType: row.entity_type,
|
|
170
|
+
entityId: row.entity_id,
|
|
171
|
+
isPublic: row.is_public === 1,
|
|
172
|
+
uploadedAt: row.uploaded_at,
|
|
173
|
+
expiresAt: row.expires_at,
|
|
174
|
+
checksum: row.checksum,
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Generate a signed URL for temporary access
|
|
179
|
+
* Note: R2 doesn't support presigned URLs natively yet, so this returns a direct URL
|
|
180
|
+
* In production, you'd implement token-based access or use Workers to proxy requests
|
|
181
|
+
*/
|
|
182
|
+
async generateSignedUrl(fileId, user, expiresIn = 3600) {
|
|
183
|
+
const metadata = await this.getFileMetadata(fileId);
|
|
184
|
+
if (!metadata) {
|
|
185
|
+
throw new Error('File not found');
|
|
186
|
+
}
|
|
187
|
+
// Check permissions
|
|
188
|
+
if (!metadata.isPublic && metadata.userId !== user.id) {
|
|
189
|
+
throw new Error('Access denied');
|
|
190
|
+
}
|
|
191
|
+
// Generate a temporary access token (stored in database)
|
|
192
|
+
const token = `${fileId}_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
|
|
193
|
+
const expiresAt = Date.now() + expiresIn * 1000;
|
|
194
|
+
await this.db.run('INSERT INTO file_access_tokens (token, file_id, user_id, expires_at, created_at) VALUES (?, ?, ?, ?, ?)', [token, fileId, user.id, expiresAt, Date.now()]);
|
|
195
|
+
// Return URL with token
|
|
196
|
+
return `/files/download/${fileId}?token=${token}`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Verify a signed URL token
|
|
200
|
+
*/
|
|
201
|
+
async verifyToken(fileId, token) {
|
|
202
|
+
const row = await this.db.getOne('SELECT * FROM file_access_tokens WHERE token = ? AND file_id = ? AND expires_at > ?', [token, fileId, Date.now()]);
|
|
203
|
+
return !!row;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Clean up expired files
|
|
207
|
+
*/
|
|
208
|
+
async cleanupExpiredFiles() {
|
|
209
|
+
const expiredFiles = await this.db.getAll('SELECT * FROM files WHERE expires_at IS NOT NULL AND expires_at < ?', [
|
|
210
|
+
Date.now(),
|
|
211
|
+
]);
|
|
212
|
+
for (const file of expiredFiles) {
|
|
213
|
+
try {
|
|
214
|
+
await this.bucket.delete(file.key);
|
|
215
|
+
await this.db.run('DELETE FROM files WHERE id = ?', [file.id]);
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
console.error(`Failed to delete expired file ${file.id}:`, error);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return expiredFiles.length;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=file-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-manager.js","sourceRoot":"","sources":["../../../../src/storage/file-manager.ts"],"names":[],"mappings":"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"}
|