secure-upload-fastify-sdk 1.0.5 → 1.0.6
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 +152 -0
- package/dist/index.js +103 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +103 -4
- package/dist/index.mjs.map +1 -1
- package/dist/server/fastify-plugin.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +103 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +103 -4
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/routes/multipart.d.ts.map +1 -1
- package/dist/server/types.d.ts +71 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/repository/index.js +51 -1
- package/dist/server/utils/repository/index.js.map +1 -1
- package/dist/server/utils/repository/index.mjs +51 -1
- package/dist/server/utils/repository/index.mjs.map +1 -1
- package/dist/server/utils/repository/memory.d.ts +7 -1
- package/dist/server/utils/repository/memory.d.ts.map +1 -1
- package/dist/server/utils/repository/prisma.d.ts +8 -1
- package/dist/server/utils/repository/prisma.d.ts.map +1 -1
- package/dist/server/utils/repository/types.d.ts +38 -2
- package/dist/server/utils/repository/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastify-plugin.d.ts","sourceRoot":"","sources":["../../src/server/fastify-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAC5F,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAQhG,OAAO,KAAK,EACV,mBAAmB,EAIpB,MAAM,SAAS,CAAC;AAmDjB,iBAAe,sBAAsB,CACnC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"fastify-plugin.d.ts","sourceRoot":"","sources":["../../src/server/fastify-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAC5F,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAQhG,OAAO,KAAK,EACV,mBAAmB,EAIpB,MAAM,SAAS,CAAC;AAmDjB,iBAAe,sBAAsB,CACnC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAsHf;;AAED,wBAGG;AAKH,OAAO,EACL,YAAY,EACZ,eAAe,EACf,yBAAyB,EACzB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,GACvB,CAAC"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
* import { chooseStrategy, suggestConcurrency, DEFAULT_STRATEGY_CONFIG } from 'secure-upload-fastify-sdk/strategy';
|
|
13
13
|
*/
|
|
14
14
|
export { default, default as secureUploadPlugin, LocalStorage, InMemoryStorage, PrismaMultipartRepository, PrismaFileRepository, InMemoryMultipartRepository, InMemoryFileRepository, } from './fastify-plugin';
|
|
15
|
-
export type { SecureUploadOptions, SecureUploadRuntime, SecureMode, RepositoryConfig, StorageConfig, PathPrefixConfig, SessionValidator, InitResponse, PartResponse, CompleteResponse, AbortResponse, StatusResponse, MultipartRecord, FileRecord, } from './types';
|
|
15
|
+
export type { SecureUploadOptions, SecureUploadRuntime, SecureMode, RepositoryConfig, StorageConfig, PathPrefixConfig, SessionValidator, InitResponse, PartResponse, CompleteResponse, AbortResponse, StatusResponse, MultipartRecord, FileRecord, OnCompleteHook, OnCompleteContext, OnCompletePatch, } from './types';
|
|
16
16
|
export { ErrorCode, SecureError, getHttpStatus, } from './errors';
|
|
17
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,OAAO,EACP,OAAO,IAAI,kBAAkB,EAC7B,YAAY,EACZ,eAAe,EACf,yBAAyB,EACzB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,EACf,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,OAAO,EACP,OAAO,IAAI,kBAAkB,EAC7B,YAAY,EACZ,eAAe,EACf,yBAAyB,EACzB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,EACf,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,SAAS,EACT,WAAW,EACX,aAAa,GACd,MAAM,UAAU,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -346,7 +346,12 @@ function toFileRecord(row) {
|
|
|
346
346
|
meta: row.meta && typeof row.meta === "object" ? row.meta : null,
|
|
347
347
|
createdAt: row.createdAt,
|
|
348
348
|
updatedAt: row.updatedAt,
|
|
349
|
-
deletedAt: row.deletedAt
|
|
349
|
+
deletedAt: row.deletedAt,
|
|
350
|
+
// v1.5.1+:水印溯源字段(可选;旧 schema/老库里 row 可能没有,需要兜底)
|
|
351
|
+
watermarked: row.watermarked ?? false,
|
|
352
|
+
isOriginal: row.isOriginal ?? true,
|
|
353
|
+
parentId: row.parentId ?? null,
|
|
354
|
+
retentionExpiresAt: row.retentionExpiresAt ?? null
|
|
350
355
|
};
|
|
351
356
|
}
|
|
352
357
|
var PrismaMultipartRepository = class {
|
|
@@ -512,6 +517,27 @@ var PrismaFileRepository = class {
|
|
|
512
517
|
});
|
|
513
518
|
return toFileRecord(row);
|
|
514
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* v1.5.1: 钩子 onComplete 返回 patch 后 SDK 调这个方法
|
|
522
|
+
* - 只更新传入的字段;其他字段保留原值
|
|
523
|
+
* - 文件不存在 → 抛错(由调用方决定是否捕获)
|
|
524
|
+
* - 业务字段 watermarked/isOriginal/parentId/retentionExpiresAt 需要 schema v1.4.0+
|
|
525
|
+
*/
|
|
526
|
+
async update(id, patch) {
|
|
527
|
+
const data = {};
|
|
528
|
+
if (patch.storageKey !== void 0) data.storageKey = patch.storageKey;
|
|
529
|
+
if (patch.size !== void 0) data.size = BigInt(patch.size);
|
|
530
|
+
if (patch.contentType !== void 0) data.contentType = patch.contentType;
|
|
531
|
+
if (patch.meta !== void 0) data.meta = patch.meta;
|
|
532
|
+
if (patch.watermarked !== void 0) data.watermarked = patch.watermarked;
|
|
533
|
+
if (patch.isOriginal !== void 0) data.isOriginal = patch.isOriginal;
|
|
534
|
+
if (patch.parentId !== void 0) data.parentId = patch.parentId;
|
|
535
|
+
if (patch.retentionExpiresAt !== void 0) {
|
|
536
|
+
data.retentionExpiresAt = patch.retentionExpiresAt;
|
|
537
|
+
}
|
|
538
|
+
const row = await this.prisma.file.update({ where: { id }, data });
|
|
539
|
+
return toFileRecord(row);
|
|
540
|
+
}
|
|
515
541
|
async softDelete(id, metaPatch) {
|
|
516
542
|
if (metaPatch) {
|
|
517
543
|
const cur = await this.prisma.file.findUnique({ where: { id } });
|
|
@@ -738,6 +764,30 @@ var InMemoryFileRepository = class {
|
|
|
738
764
|
};
|
|
739
765
|
this.store.set(id, next);
|
|
740
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* v1.5.1: 钩子 onComplete 返回 patch 后 SDK 调这个方法
|
|
769
|
+
* - 只更新传入的字段;其他字段保留原值
|
|
770
|
+
* - 文件不存在 → 抛错(由调用方决定是否捕获)
|
|
771
|
+
*/
|
|
772
|
+
async update(id, patch) {
|
|
773
|
+
const rec = this.store.get(id);
|
|
774
|
+
if (!rec) throw new Error("NOT_FOUND");
|
|
775
|
+
const now = /* @__PURE__ */ new Date();
|
|
776
|
+
const next = {
|
|
777
|
+
...rec,
|
|
778
|
+
storageKey: patch.storageKey ?? rec.storageKey,
|
|
779
|
+
size: patch.size !== void 0 ? BigInt(patch.size) : rec.size,
|
|
780
|
+
contentType: patch.contentType ?? rec.contentType,
|
|
781
|
+
meta: patch.meta === void 0 ? rec.meta : patch.meta,
|
|
782
|
+
watermarked: patch.watermarked ?? rec.watermarked,
|
|
783
|
+
isOriginal: patch.isOriginal ?? rec.isOriginal,
|
|
784
|
+
parentId: patch.parentId === void 0 ? rec.parentId : patch.parentId,
|
|
785
|
+
retentionExpiresAt: patch.retentionExpiresAt === void 0 ? rec.retentionExpiresAt : patch.retentionExpiresAt,
|
|
786
|
+
updatedAt: now
|
|
787
|
+
};
|
|
788
|
+
this.store.set(id, next);
|
|
789
|
+
return next;
|
|
790
|
+
}
|
|
741
791
|
async listOrphans(opts) {
|
|
742
792
|
const sinceMs = opts.sinceDays * 24 * 3600 * 1e3;
|
|
743
793
|
const list = [];
|
|
@@ -1185,6 +1235,47 @@ async function lock(rt, key, ttlMs, fn, retries = 0) {
|
|
|
1185
1235
|
}
|
|
1186
1236
|
return await memoryWithLock(key, fn, retries);
|
|
1187
1237
|
}
|
|
1238
|
+
async function streamToBuffer(stream) {
|
|
1239
|
+
const chunks = [];
|
|
1240
|
+
for await (const chunk of stream) {
|
|
1241
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
1242
|
+
}
|
|
1243
|
+
return Buffer.concat(chunks);
|
|
1244
|
+
}
|
|
1245
|
+
async function runOnComplete(rt, file, log) {
|
|
1246
|
+
if (!rt.onComplete) return file;
|
|
1247
|
+
try {
|
|
1248
|
+
const patch = await rt.onComplete({
|
|
1249
|
+
file,
|
|
1250
|
+
storage: rt.storage,
|
|
1251
|
+
log,
|
|
1252
|
+
getOriginalBuffer: async () => {
|
|
1253
|
+
const stream = await Promise.resolve(rt.storage.get(file.storageKey));
|
|
1254
|
+
return streamToBuffer(stream);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
if (!patch || Object.keys(patch).length === 0) {
|
|
1258
|
+
return file;
|
|
1259
|
+
}
|
|
1260
|
+
const updated = await rt.fileRepo.update(file.id, patch);
|
|
1261
|
+
log.info(
|
|
1262
|
+
{
|
|
1263
|
+
fileId: file.id,
|
|
1264
|
+
patchKeys: Object.keys(patch),
|
|
1265
|
+
newStorageKey: patch.storageKey,
|
|
1266
|
+
watermarked: patch.watermarked
|
|
1267
|
+
},
|
|
1268
|
+
"\u2705 onComplete \u94A9\u5B50\u5DF2\u5E94\u7528 patch"
|
|
1269
|
+
);
|
|
1270
|
+
return updated;
|
|
1271
|
+
} catch (err) {
|
|
1272
|
+
log.warn(
|
|
1273
|
+
{ err, fileId: file.id, uploadId: file.uploadId },
|
|
1274
|
+
"\u26A0\uFE0F onComplete \u94A9\u5B50\u6267\u884C\u5931\u8D25,\u5DF2\u8DF3\u8FC7\u4E1A\u52A1\u540E\u5904\u7406(file record \u4FDD\u6301\u539F\u72B6)"
|
|
1275
|
+
);
|
|
1276
|
+
return file;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1188
1279
|
async function registerMultipartRoutes(app, rt) {
|
|
1189
1280
|
if (!rt.config.enabled) {
|
|
1190
1281
|
app.log.warn("\u26A0\uFE0F multipart \u4E0A\u4F20\u5DF2\u7981\u7528 (enabled=false)");
|
|
@@ -1490,8 +1581,14 @@ async function registerMultipartRoutes(app, rt) {
|
|
|
1490
1581
|
contentHash: finalHash,
|
|
1491
1582
|
meta: rec.meta ?? void 0
|
|
1492
1583
|
});
|
|
1584
|
+
const finalFile = await runOnComplete(rt, file2, req.log);
|
|
1493
1585
|
await rt.multipartRepo.markCompleted(rec.id, fileId);
|
|
1494
|
-
return {
|
|
1586
|
+
return {
|
|
1587
|
+
idempotent: false,
|
|
1588
|
+
fileId: finalFile.id,
|
|
1589
|
+
size: Number(finalFile.size),
|
|
1590
|
+
finalHash
|
|
1591
|
+
};
|
|
1495
1592
|
}
|
|
1496
1593
|
);
|
|
1497
1594
|
if (!result.idempotent) {
|
|
@@ -1504,7 +1601,8 @@ async function registerMultipartRoutes(app, rt) {
|
|
|
1504
1601
|
uploadId: body.uploadId,
|
|
1505
1602
|
fileId: result.fileId,
|
|
1506
1603
|
size: result.size,
|
|
1507
|
-
userId: rec.userId
|
|
1604
|
+
userId: rec.userId,
|
|
1605
|
+
watermarkHook: rt.onComplete ? "on" : "off"
|
|
1508
1606
|
},
|
|
1509
1607
|
"\u2705 multipart: complete"
|
|
1510
1608
|
);
|
|
@@ -1980,7 +2078,8 @@ async function secureUploadPluginImpl(app, options) {
|
|
|
1980
2078
|
...options.strategy ?? {}
|
|
1981
2079
|
}
|
|
1982
2080
|
},
|
|
1983
|
-
validateSession: options.sessionValidator ?? defaultSessionValidator
|
|
2081
|
+
validateSession: options.sessionValidator ?? defaultSessionValidator,
|
|
2082
|
+
onComplete: options.onComplete ?? null
|
|
1984
2083
|
};
|
|
1985
2084
|
app.decorate("secureUpload", runtime);
|
|
1986
2085
|
await registerMultipartRoutes(app, runtime);
|