@proteinjs/db-file 1.6.0 → 1.7.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.
- package/CHANGELOG.md +19 -0
- package/dist/src/routes/byteRange.d.ts +17 -0
- package/dist/src/routes/byteRange.d.ts.map +1 -0
- package/dist/src/routes/byteRange.js +51 -0
- package/dist/src/routes/byteRange.js.map +1 -0
- package/dist/src/routes/getFile.d.ts +3 -1
- package/dist/src/routes/getFile.d.ts.map +1 -1
- package/dist/src/routes/getFile.js +28 -11
- package/dist/src/routes/getFile.js.map +1 -1
- package/dist/test/GetFileRoute.test.js +138 -12
- package/dist/test/GetFileRoute.test.js.map +1 -1
- package/package.json +5 -5
- package/src/routes/byteRange.ts +49 -0
- package/src/routes/getFile.ts +18 -2
- package/test/GetFileRoute.test.ts +68 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.7.0](https://github.com/proteinjs/db/compare/@proteinjs/db-file@1.6.1...@proteinjs/db-file@1.7.0) (2026-08-26)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* HTTP Range on the file proxy route — video seek parity with signed-URL serving ([0a95a3f](https://github.com/proteinjs/db/commit/0a95a3f355411b68a20b5cfbc7d7614962b3cd40))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [1.6.1](https://github.com/proteinjs/db/compare/@proteinjs/db-file@1.6.0...@proteinjs/db-file@1.6.1) (2026-08-24)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @proteinjs/db-file
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
# [1.6.0](https://github.com/proteinjs/db/compare/@proteinjs/db-file@1.5.3...@proteinjs/db-file@1.6.0) (2026-08-23)
|
|
7
26
|
|
|
8
27
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The proxy route's Range arithmetic (RFC 7233 single byte-range), separated pure so the
|
|
3
|
+
* route's seek behavior is hermetically testable:
|
|
4
|
+
*
|
|
5
|
+
* - `undefined` — no (usable) range was asked: serve the whole body as 200. Covers a missing
|
|
6
|
+
* header, a non-`bytes` unit, malformed specs, and multi-range requests (a `<video>` never
|
|
7
|
+
* sends them; the RFC lets a server ignore an unusable Range header entirely).
|
|
8
|
+
* - `'unsatisfiable'` — a syntactically-valid range that selects nothing (start past EOF, an
|
|
9
|
+
* empty suffix, or any range of a zero-byte body): respond 416 with a `Content-Range` naming the size (`bytes * / size`).
|
|
10
|
+
* - `{ start, end }` — serve 206 with `bytes start-end/size` and exactly those bytes
|
|
11
|
+
* (inclusive). An over-long end clamps to the last byte.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveByteRange(rangeHeader: string | undefined, size: number): {
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
} | 'unsatisfiable' | undefined;
|
|
17
|
+
//# sourceMappingURL=byteRange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byteRange.d.ts","sourceRoot":"","sources":["../../../src/routes/byteRange.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,eAAe,GAAG,SAAS,CAiC9D"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveByteRange = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The proxy route's Range arithmetic (RFC 7233 single byte-range), separated pure so the
|
|
6
|
+
* route's seek behavior is hermetically testable:
|
|
7
|
+
*
|
|
8
|
+
* - `undefined` — no (usable) range was asked: serve the whole body as 200. Covers a missing
|
|
9
|
+
* header, a non-`bytes` unit, malformed specs, and multi-range requests (a `<video>` never
|
|
10
|
+
* sends them; the RFC lets a server ignore an unusable Range header entirely).
|
|
11
|
+
* - `'unsatisfiable'` — a syntactically-valid range that selects nothing (start past EOF, an
|
|
12
|
+
* empty suffix, or any range of a zero-byte body): respond 416 with a `Content-Range` naming the size (`bytes * / size`).
|
|
13
|
+
* - `{ start, end }` — serve 206 with `bytes start-end/size` and exactly those bytes
|
|
14
|
+
* (inclusive). An over-long end clamps to the last byte.
|
|
15
|
+
*/
|
|
16
|
+
function resolveByteRange(rangeHeader, size) {
|
|
17
|
+
if (!rangeHeader || !rangeHeader.startsWith('bytes=')) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
var spec = rangeHeader.slice('bytes='.length).trim();
|
|
21
|
+
if (spec.includes(',')) {
|
|
22
|
+
return undefined; // multi-range — serve whole (no player sends these)
|
|
23
|
+
}
|
|
24
|
+
var match = /^(\d*)-(\d*)$/.exec(spec);
|
|
25
|
+
if (!match || (match[1] === '' && match[2] === '')) {
|
|
26
|
+
return undefined; // malformed — ignore the header per the RFC
|
|
27
|
+
}
|
|
28
|
+
var fromText = match[1], toText = match[2];
|
|
29
|
+
if (fromText === '') {
|
|
30
|
+
// Suffix range: the last N bytes.
|
|
31
|
+
var suffix = Number(toText);
|
|
32
|
+
if (suffix === 0 || size === 0) {
|
|
33
|
+
return 'unsatisfiable';
|
|
34
|
+
}
|
|
35
|
+
return { start: Math.max(0, size - suffix), end: size - 1 };
|
|
36
|
+
}
|
|
37
|
+
var start = Number(fromText);
|
|
38
|
+
if (start >= size) {
|
|
39
|
+
return 'unsatisfiable';
|
|
40
|
+
}
|
|
41
|
+
if (toText === '') {
|
|
42
|
+
return { start: start, end: size - 1 };
|
|
43
|
+
}
|
|
44
|
+
var end = Number(toText);
|
|
45
|
+
if (end < start) {
|
|
46
|
+
return undefined; // malformed (last < first) — ignore the header per the RFC
|
|
47
|
+
}
|
|
48
|
+
return { start: start, end: Math.min(end, size - 1) };
|
|
49
|
+
}
|
|
50
|
+
exports.resolveByteRange = resolveByteRange;
|
|
51
|
+
//# sourceMappingURL=byteRange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byteRange.js","sourceRoot":"","sources":["../../../src/routes/byteRange.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,SAAgB,gBAAgB,CAC9B,WAA+B,EAC/B,IAAY;IAEZ,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QACrD,OAAO,SAAS,CAAC;KAClB;IACD,IAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACtB,OAAO,SAAS,CAAC,CAAC,oDAAoD;KACvE;IACD,IAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE;QAClD,OAAO,SAAS,CAAC,CAAC,4CAA4C;KAC/D;IACQ,IAAA,QAAQ,GAAY,KAAK,GAAjB,EAAE,MAAM,GAAI,KAAK,GAAT,CAAU;IACnC,IAAI,QAAQ,KAAK,EAAE,EAAE;QACnB,kCAAkC;QAClC,IAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE;YAC9B,OAAO,eAAe,CAAC;SACxB;QACD,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;KAC7D;IACD,IAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,KAAK,IAAI,IAAI,EAAE;QACjB,OAAO,eAAe,CAAC;KACxB;IACD,IAAI,MAAM,KAAK,EAAE,EAAE;QACjB,OAAO,EAAE,KAAK,OAAA,EAAE,GAAG,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;KACjC;IACD,IAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,GAAG,GAAG,KAAK,EAAE;QACf,OAAO,SAAS,CAAC,CAAC,2DAA2D;KAC9E;IACD,OAAO,EAAE,KAAK,OAAA,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;AACjD,CAAC;AApCD,4CAoCC"}
|
|
@@ -8,7 +8,9 @@ import { Route } from '@proteinjs/server-api';
|
|
|
8
8
|
* seeking, real caching, bytes never transit the app server. The redirect itself is cached
|
|
9
9
|
* briefly (client-private, well under the signed TTL) so repeated loads reuse one URL.
|
|
10
10
|
* - **Proxy** for drivers with no external URL space (`DbFileStorageDriver`): the interface's
|
|
11
|
-
* base64 is decoded so every mime — binary or text — serves its true bytes
|
|
11
|
+
* base64 is decoded so every mime — binary or text — serves its true bytes, and HTTP Range
|
|
12
|
+
* is honored (206/416 via {@link resolveByteRange}) so a `<video>` can SEEK against a
|
|
13
|
+
* proxy-served blob — streaming parity with the signed-URL path instead of download-then-watch.
|
|
12
14
|
*/
|
|
13
15
|
export declare const getFile: Route;
|
|
14
16
|
//# sourceMappingURL=getFile.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../../../src/routes/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../../../src/routes/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAK9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,EAAE,KAiDrB,CAAC"}
|
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.getFile = void 0;
|
|
40
40
|
var FileStorage_1 = require("../FileStorage");
|
|
41
41
|
var user_1 = require("@proteinjs/user");
|
|
42
|
+
var byteRange_1 = require("./byteRange");
|
|
42
43
|
/**
|
|
43
44
|
* Serves a file's bytes. Auth first (logged-in + scoped row read), then one of two paths:
|
|
44
45
|
*
|
|
@@ -48,15 +49,18 @@ var user_1 = require("@proteinjs/user");
|
|
|
48
49
|
* seeking, real caching, bytes never transit the app server. The redirect itself is cached
|
|
49
50
|
* briefly (client-private, well under the signed TTL) so repeated loads reuse one URL.
|
|
50
51
|
* - **Proxy** for drivers with no external URL space (`DbFileStorageDriver`): the interface's
|
|
51
|
-
* base64 is decoded so every mime — binary or text — serves its true bytes
|
|
52
|
+
* base64 is decoded so every mime — binary or text — serves its true bytes, and HTTP Range
|
|
53
|
+
* is honored (206/416 via {@link resolveByteRange}) so a `<video>` can SEEK against a
|
|
54
|
+
* proxy-served blob — streaming parity with the signed-URL path instead of download-then-watch.
|
|
52
55
|
*/
|
|
53
56
|
exports.getFile = {
|
|
54
57
|
path: '/file/:id',
|
|
55
58
|
method: 'get',
|
|
56
59
|
onRequest: function (request, response) { return __awaiter(void 0, void 0, void 0, function () {
|
|
57
|
-
var fileId, fileStorage, file, signedUrl, fileDataBase64, safeFilename, error_1;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
var fileId, fileStorage, file, signedUrl, fileDataBase64, bytes, safeFilename, range, error_1;
|
|
61
|
+
var _a;
|
|
62
|
+
return __generator(this, function (_b) {
|
|
63
|
+
switch (_b.label) {
|
|
60
64
|
case 0:
|
|
61
65
|
if (!user_1.UserAuth.isLoggedIn()) {
|
|
62
66
|
response.status(401).send('User not logged in');
|
|
@@ -64,19 +68,19 @@ exports.getFile = {
|
|
|
64
68
|
}
|
|
65
69
|
fileId = request.params.id;
|
|
66
70
|
fileStorage = (0, FileStorage_1.getFileStorage)();
|
|
67
|
-
|
|
71
|
+
_b.label = 1;
|
|
68
72
|
case 1:
|
|
69
|
-
|
|
73
|
+
_b.trys.push([1, 5, , 6]);
|
|
70
74
|
return [4 /*yield*/, fileStorage.getFile(fileId)];
|
|
71
75
|
case 2:
|
|
72
|
-
file =
|
|
76
|
+
file = _b.sent();
|
|
73
77
|
if (!file) {
|
|
74
78
|
response.status(404).send('File not found');
|
|
75
79
|
return [2 /*return*/];
|
|
76
80
|
}
|
|
77
81
|
return [4 /*yield*/, fileStorage.getSignedUrl(fileId)];
|
|
78
82
|
case 3:
|
|
79
|
-
signedUrl =
|
|
83
|
+
signedUrl = _b.sent();
|
|
80
84
|
if (signedUrl) {
|
|
81
85
|
response.setHeader('Cache-Control', 'private, max-age=300');
|
|
82
86
|
response.redirect(302, signedUrl);
|
|
@@ -84,14 +88,27 @@ exports.getFile = {
|
|
|
84
88
|
}
|
|
85
89
|
return [4 /*yield*/, fileStorage.getFileData(fileId)];
|
|
86
90
|
case 4:
|
|
87
|
-
fileDataBase64 =
|
|
91
|
+
fileDataBase64 = _b.sent();
|
|
92
|
+
bytes = Buffer.from(fileDataBase64, 'base64');
|
|
88
93
|
safeFilename = encodeURIComponent(file.name);
|
|
89
94
|
response.setHeader('Content-Disposition', "inline; filename=\"".concat(safeFilename, "\""));
|
|
90
95
|
response.setHeader('Content-Type', file.type);
|
|
91
|
-
response.
|
|
96
|
+
response.setHeader('Accept-Ranges', 'bytes');
|
|
97
|
+
range = (0, byteRange_1.resolveByteRange)((_a = request.headers) === null || _a === void 0 ? void 0 : _a.range, bytes.length);
|
|
98
|
+
if (range === 'unsatisfiable') {
|
|
99
|
+
response.setHeader('Content-Range', "bytes */".concat(bytes.length));
|
|
100
|
+
response.status(416).send('Range Not Satisfiable');
|
|
101
|
+
return [2 /*return*/];
|
|
102
|
+
}
|
|
103
|
+
if (range) {
|
|
104
|
+
response.setHeader('Content-Range', "bytes ".concat(range.start, "-").concat(range.end, "/").concat(bytes.length));
|
|
105
|
+
response.status(206).send(bytes.subarray(range.start, range.end + 1));
|
|
106
|
+
return [2 /*return*/];
|
|
107
|
+
}
|
|
108
|
+
response.send(bytes);
|
|
92
109
|
return [3 /*break*/, 6];
|
|
93
110
|
case 5:
|
|
94
|
-
error_1 =
|
|
111
|
+
error_1 = _b.sent();
|
|
95
112
|
console.error("Error fetching file (".concat(fileId, "):"), error_1);
|
|
96
113
|
response.status(500).send('Internal Server Error');
|
|
97
114
|
return [3 /*break*/, 6];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFile.js","sourceRoot":"","sources":["../../../src/routes/getFile.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,8CAAgD;AAChD,wCAA2C;
|
|
1
|
+
{"version":3,"file":"getFile.js","sourceRoot":"","sources":["../../../src/routes/getFile.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,8CAAgD;AAChD,wCAA2C;AAC3C,yCAA+C;AAE/C;;;;;;;;;;;;GAYG;AACU,QAAA,OAAO,GAAU;IAC5B,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,KAAK;IACb,SAAS,EAAE,UAAO,OAAO,EAAE,QAAQ;;;;;;oBACjC,IAAI,CAAC,eAAQ,CAAC,UAAU,EAAE,EAAE;wBAC1B,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;wBAChD,sBAAO;qBACR;oBAEK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC3B,WAAW,GAAG,IAAA,4BAAc,GAAE,CAAC;;;;oBAGtB,qBAAM,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,EAAA;;oBAAxC,IAAI,GAAG,SAAiC;oBAC9C,IAAI,CAAC,IAAI,EAAE;wBACT,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;wBAC5C,sBAAO;qBACR;oBAEiB,qBAAM,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,EAAA;;oBAAlD,SAAS,GAAG,SAAsC;oBACxD,IAAI,SAAS,EAAE;wBACb,QAAQ,CAAC,SAAS,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAAC;wBAC5D,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;wBAClC,sBAAO;qBACR;oBAEsB,qBAAM,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,EAAA;;oBAAtD,cAAc,GAAG,SAAqC;oBACtD,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;oBAC9C,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnD,QAAQ,CAAC,SAAS,CAAC,qBAAqB,EAAE,6BAAqB,YAAY,OAAG,CAAC,CAAC;oBAChF,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC9C,QAAQ,CAAC,SAAS,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;oBACvC,KAAK,GAAG,IAAA,4BAAgB,EAAC,MAAA,OAAO,CAAC,OAAO,0CAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBACrE,IAAI,KAAK,KAAK,eAAe,EAAE;wBAC7B,QAAQ,CAAC,SAAS,CAAC,eAAe,EAAE,kBAAW,KAAK,CAAC,MAAM,CAAE,CAAC,CAAC;wBAC/D,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;wBACnD,sBAAO;qBACR;oBACD,IAAI,KAAK,EAAE;wBACT,QAAQ,CAAC,SAAS,CAAC,eAAe,EAAE,gBAAS,KAAK,CAAC,KAAK,cAAI,KAAK,CAAC,GAAG,cAAI,KAAK,CAAC,MAAM,CAAE,CAAC,CAAC;wBACzF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;wBACtE,sBAAO;qBACR;oBACD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;;;oBAErB,OAAO,CAAC,KAAK,CAAC,+BAAwB,MAAM,OAAI,EAAE,OAAK,CAAC,CAAC;oBACzD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;;;;;SAEtD;CACF,CAAC"}
|
|
@@ -241,19 +241,22 @@ var ResponseRecorder = /** @class */ (function () {
|
|
|
241
241
|
};
|
|
242
242
|
return ResponseRecorder;
|
|
243
243
|
}());
|
|
244
|
-
var invokeRoute = function (fileId
|
|
245
|
-
|
|
246
|
-
return
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
244
|
+
var invokeRoute = function (fileId, headers) {
|
|
245
|
+
if (headers === void 0) { headers = {}; }
|
|
246
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
247
|
+
var response;
|
|
248
|
+
return __generator(this, function (_a) {
|
|
249
|
+
switch (_a.label) {
|
|
250
|
+
case 0:
|
|
251
|
+
response = new ResponseRecorder();
|
|
252
|
+
return [4 /*yield*/, getFile_1.getFile.onRequest({ params: { id: fileId }, headers: headers }, response)];
|
|
253
|
+
case 1:
|
|
254
|
+
_a.sent();
|
|
255
|
+
return [2 /*return*/, response];
|
|
256
|
+
}
|
|
257
|
+
});
|
|
255
258
|
});
|
|
256
|
-
}
|
|
259
|
+
};
|
|
257
260
|
var testEnv = new FileTestEnvironment_1.FileTestEnvironment();
|
|
258
261
|
var user;
|
|
259
262
|
beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -325,6 +328,129 @@ describe('GET /file/:id — proxy serving (driver without signed URLs)', functio
|
|
|
325
328
|
}
|
|
326
329
|
});
|
|
327
330
|
}); });
|
|
331
|
+
// ── HTTP Range on the proxy path — video streaming parity with the signed-URL path, so a
|
|
332
|
+
// <video> can seek against a proxy-served blob instead of downloading it whole. ──────────
|
|
333
|
+
describe('Range requests (video seek parity with the signed-URL path)', function () {
|
|
334
|
+
var videoFileId;
|
|
335
|
+
beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
336
|
+
var file;
|
|
337
|
+
return __generator(this, function (_a) {
|
|
338
|
+
switch (_a.label) {
|
|
339
|
+
case 0: return [4 /*yield*/, new FileStorage_1.FileStorage().createFile({ name: 'smoke.webm', type: 'video/webm', size: rawBytes.length }, rawBytes.toString('base64'))];
|
|
340
|
+
case 1:
|
|
341
|
+
file = _a.sent();
|
|
342
|
+
videoFileId = file.id;
|
|
343
|
+
return [2 /*return*/];
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}); });
|
|
347
|
+
it('advertises Accept-Ranges and serves the whole file when no Range is asked', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
348
|
+
var response;
|
|
349
|
+
return __generator(this, function (_a) {
|
|
350
|
+
switch (_a.label) {
|
|
351
|
+
case 0: return [4 /*yield*/, invokeRoute(videoFileId)];
|
|
352
|
+
case 1:
|
|
353
|
+
response = _a.sent();
|
|
354
|
+
expect(response.headers['accept-ranges']).toEqual('bytes');
|
|
355
|
+
expect(Buffer.compare(response.body, rawBytes)).toBe(0);
|
|
356
|
+
return [2 /*return*/];
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}); });
|
|
360
|
+
it('serves a bounded range as 206 with Content-Range and exactly those bytes', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
361
|
+
var response;
|
|
362
|
+
return __generator(this, function (_a) {
|
|
363
|
+
switch (_a.label) {
|
|
364
|
+
case 0: return [4 /*yield*/, invokeRoute(videoFileId, { range: 'bytes=10-19' })];
|
|
365
|
+
case 1:
|
|
366
|
+
response = _a.sent();
|
|
367
|
+
expect(response.statusCode).toEqual(206);
|
|
368
|
+
expect(response.headers['content-range']).toEqual('bytes 10-19/256');
|
|
369
|
+
expect(response.headers['accept-ranges']).toEqual('bytes');
|
|
370
|
+
expect(Buffer.compare(response.body, rawBytes.subarray(10, 20))).toBe(0);
|
|
371
|
+
return [2 /*return*/];
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}); });
|
|
375
|
+
it('serves an open-ended range to EOF (the video-seek shape)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
376
|
+
var response;
|
|
377
|
+
return __generator(this, function (_a) {
|
|
378
|
+
switch (_a.label) {
|
|
379
|
+
case 0: return [4 /*yield*/, invokeRoute(videoFileId, { range: 'bytes=200-' })];
|
|
380
|
+
case 1:
|
|
381
|
+
response = _a.sent();
|
|
382
|
+
expect(response.statusCode).toEqual(206);
|
|
383
|
+
expect(response.headers['content-range']).toEqual('bytes 200-255/256');
|
|
384
|
+
expect(Buffer.compare(response.body, rawBytes.subarray(200))).toBe(0);
|
|
385
|
+
return [2 /*return*/];
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}); });
|
|
389
|
+
it('serves a suffix range (last N bytes)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
390
|
+
var response;
|
|
391
|
+
return __generator(this, function (_a) {
|
|
392
|
+
switch (_a.label) {
|
|
393
|
+
case 0: return [4 /*yield*/, invokeRoute(videoFileId, { range: 'bytes=-16' })];
|
|
394
|
+
case 1:
|
|
395
|
+
response = _a.sent();
|
|
396
|
+
expect(response.statusCode).toEqual(206);
|
|
397
|
+
expect(response.headers['content-range']).toEqual('bytes 240-255/256');
|
|
398
|
+
expect(Buffer.compare(response.body, rawBytes.subarray(240))).toBe(0);
|
|
399
|
+
return [2 /*return*/];
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
}); });
|
|
403
|
+
it('clamps an over-long range to the end of the file', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
404
|
+
var response;
|
|
405
|
+
return __generator(this, function (_a) {
|
|
406
|
+
switch (_a.label) {
|
|
407
|
+
case 0: return [4 /*yield*/, invokeRoute(videoFileId, { range: 'bytes=250-999' })];
|
|
408
|
+
case 1:
|
|
409
|
+
response = _a.sent();
|
|
410
|
+
expect(response.statusCode).toEqual(206);
|
|
411
|
+
expect(response.headers['content-range']).toEqual('bytes 250-255/256');
|
|
412
|
+
expect(Buffer.compare(response.body, rawBytes.subarray(250))).toBe(0);
|
|
413
|
+
return [2 /*return*/];
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
}); });
|
|
417
|
+
it('416s an unsatisfiable range, naming the size', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
418
|
+
var response;
|
|
419
|
+
return __generator(this, function (_a) {
|
|
420
|
+
switch (_a.label) {
|
|
421
|
+
case 0: return [4 /*yield*/, invokeRoute(videoFileId, { range: 'bytes=999-' })];
|
|
422
|
+
case 1:
|
|
423
|
+
response = _a.sent();
|
|
424
|
+
expect(response.statusCode).toEqual(416);
|
|
425
|
+
expect(response.headers['content-range']).toEqual('bytes */256');
|
|
426
|
+
return [2 /*return*/];
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}); });
|
|
430
|
+
it('ignores malformed and multi-range headers — the whole file serves', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
431
|
+
var _i, _a, range, response;
|
|
432
|
+
return __generator(this, function (_b) {
|
|
433
|
+
switch (_b.label) {
|
|
434
|
+
case 0:
|
|
435
|
+
_i = 0, _a = ['bytes=5-2', 'apples=1-2', 'bytes=0-1,3-4', 'bytes=x-y'];
|
|
436
|
+
_b.label = 1;
|
|
437
|
+
case 1:
|
|
438
|
+
if (!(_i < _a.length)) return [3 /*break*/, 4];
|
|
439
|
+
range = _a[_i];
|
|
440
|
+
return [4 /*yield*/, invokeRoute(videoFileId, { range: range })];
|
|
441
|
+
case 2:
|
|
442
|
+
response = _b.sent();
|
|
443
|
+
expect(response.statusCode).not.toEqual(206);
|
|
444
|
+
expect(Buffer.compare(response.body, rawBytes)).toBe(0);
|
|
445
|
+
_b.label = 3;
|
|
446
|
+
case 3:
|
|
447
|
+
_i++;
|
|
448
|
+
return [3 /*break*/, 1];
|
|
449
|
+
case 4: return [2 /*return*/];
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}); });
|
|
453
|
+
});
|
|
328
454
|
});
|
|
329
455
|
describe('GET /file/:id — signed-URL serving (302 redirect)', function () {
|
|
330
456
|
var driver = new TestSignedUrlFileStorageDriver();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GetFileRoute.test.js","sourceRoot":"","sources":["../../test/GetFileRoute.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GetFileRoute.test.js","sourceRoot":"","sources":["../../test/GetFileRoute.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/db-file",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "File storage in the db",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
"watch": "reflection-watch"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@proteinjs/db": "^1.
|
|
25
|
+
"@proteinjs/db": "^1.35.0",
|
|
26
26
|
"@proteinjs/logger": "^1.0.21",
|
|
27
|
-
"@proteinjs/reflection": "^1.
|
|
27
|
+
"@proteinjs/reflection": "^1.2.0",
|
|
28
28
|
"@proteinjs/server-api": "^3.0.11",
|
|
29
29
|
"@proteinjs/service": "^1.5.1",
|
|
30
30
|
"@proteinjs/user": "^1.7.1",
|
|
31
31
|
"moment": "2.29.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@proteinjs/db-driver-spanner": "^1.23.
|
|
34
|
+
"@proteinjs/db-driver-spanner": "^1.23.3",
|
|
35
35
|
"@proteinjs/reflection-build": "^2.0.1",
|
|
36
36
|
"@types/jest": "29.5.5",
|
|
37
37
|
"@types/node": "14.0.27",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
},
|
|
47
47
|
"main": "./dist/generated/index.js",
|
|
48
48
|
"types": "./dist/generated/index.d.ts",
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "521d5d1ca2f86f0ed2bef0309b0dde30506f132b"
|
|
50
50
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The proxy route's Range arithmetic (RFC 7233 single byte-range), separated pure so the
|
|
3
|
+
* route's seek behavior is hermetically testable:
|
|
4
|
+
*
|
|
5
|
+
* - `undefined` — no (usable) range was asked: serve the whole body as 200. Covers a missing
|
|
6
|
+
* header, a non-`bytes` unit, malformed specs, and multi-range requests (a `<video>` never
|
|
7
|
+
* sends them; the RFC lets a server ignore an unusable Range header entirely).
|
|
8
|
+
* - `'unsatisfiable'` — a syntactically-valid range that selects nothing (start past EOF, an
|
|
9
|
+
* empty suffix, or any range of a zero-byte body): respond 416 with a `Content-Range` naming the size (`bytes * / size`).
|
|
10
|
+
* - `{ start, end }` — serve 206 with `bytes start-end/size` and exactly those bytes
|
|
11
|
+
* (inclusive). An over-long end clamps to the last byte.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveByteRange(
|
|
14
|
+
rangeHeader: string | undefined,
|
|
15
|
+
size: number
|
|
16
|
+
): { start: number; end: number } | 'unsatisfiable' | undefined {
|
|
17
|
+
if (!rangeHeader || !rangeHeader.startsWith('bytes=')) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const spec = rangeHeader.slice('bytes='.length).trim();
|
|
21
|
+
if (spec.includes(',')) {
|
|
22
|
+
return undefined; // multi-range — serve whole (no player sends these)
|
|
23
|
+
}
|
|
24
|
+
const match = /^(\d*)-(\d*)$/.exec(spec);
|
|
25
|
+
if (!match || (match[1] === '' && match[2] === '')) {
|
|
26
|
+
return undefined; // malformed — ignore the header per the RFC
|
|
27
|
+
}
|
|
28
|
+
const [, fromText, toText] = match;
|
|
29
|
+
if (fromText === '') {
|
|
30
|
+
// Suffix range: the last N bytes.
|
|
31
|
+
const suffix = Number(toText);
|
|
32
|
+
if (suffix === 0 || size === 0) {
|
|
33
|
+
return 'unsatisfiable';
|
|
34
|
+
}
|
|
35
|
+
return { start: Math.max(0, size - suffix), end: size - 1 };
|
|
36
|
+
}
|
|
37
|
+
const start = Number(fromText);
|
|
38
|
+
if (start >= size) {
|
|
39
|
+
return 'unsatisfiable';
|
|
40
|
+
}
|
|
41
|
+
if (toText === '') {
|
|
42
|
+
return { start, end: size - 1 };
|
|
43
|
+
}
|
|
44
|
+
const end = Number(toText);
|
|
45
|
+
if (end < start) {
|
|
46
|
+
return undefined; // malformed (last < first) — ignore the header per the RFC
|
|
47
|
+
}
|
|
48
|
+
return { start, end: Math.min(end, size - 1) };
|
|
49
|
+
}
|
package/src/routes/getFile.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Route } from '@proteinjs/server-api';
|
|
2
2
|
import { getFileStorage } from '../FileStorage';
|
|
3
3
|
import { UserAuth } from '@proteinjs/user';
|
|
4
|
+
import { resolveByteRange } from './byteRange';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Serves a file's bytes. Auth first (logged-in + scoped row read), then one of two paths:
|
|
@@ -11,7 +12,9 @@ import { UserAuth } from '@proteinjs/user';
|
|
|
11
12
|
* seeking, real caching, bytes never transit the app server. The redirect itself is cached
|
|
12
13
|
* briefly (client-private, well under the signed TTL) so repeated loads reuse one URL.
|
|
13
14
|
* - **Proxy** for drivers with no external URL space (`DbFileStorageDriver`): the interface's
|
|
14
|
-
* base64 is decoded so every mime — binary or text — serves its true bytes
|
|
15
|
+
* base64 is decoded so every mime — binary or text — serves its true bytes, and HTTP Range
|
|
16
|
+
* is honored (206/416 via {@link resolveByteRange}) so a `<video>` can SEEK against a
|
|
17
|
+
* proxy-served blob — streaming parity with the signed-URL path instead of download-then-watch.
|
|
15
18
|
*/
|
|
16
19
|
export const getFile: Route = {
|
|
17
20
|
path: '/file/:id',
|
|
@@ -40,10 +43,23 @@ export const getFile: Route = {
|
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
const fileDataBase64 = await fileStorage.getFileData(fileId);
|
|
46
|
+
const bytes = Buffer.from(fileDataBase64, 'base64');
|
|
43
47
|
const safeFilename = encodeURIComponent(file.name);
|
|
44
48
|
response.setHeader('Content-Disposition', `inline; filename="${safeFilename}"`);
|
|
45
49
|
response.setHeader('Content-Type', file.type);
|
|
46
|
-
response.
|
|
50
|
+
response.setHeader('Accept-Ranges', 'bytes');
|
|
51
|
+
const range = resolveByteRange(request.headers?.range, bytes.length);
|
|
52
|
+
if (range === 'unsatisfiable') {
|
|
53
|
+
response.setHeader('Content-Range', `bytes */${bytes.length}`);
|
|
54
|
+
response.status(416).send('Range Not Satisfiable');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (range) {
|
|
58
|
+
response.setHeader('Content-Range', `bytes ${range.start}-${range.end}/${bytes.length}`);
|
|
59
|
+
response.status(206).send(bytes.subarray(range.start, range.end + 1));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
response.send(bytes);
|
|
47
63
|
} catch (error) {
|
|
48
64
|
console.error(`Error fetching file (${fileId}):`, error);
|
|
49
65
|
response.status(500).send('Internal Server Error');
|
|
@@ -131,9 +131,12 @@ class ResponseRecorder {
|
|
|
131
131
|
type RouteRequest = Parameters<typeof getFile.onRequest>[0];
|
|
132
132
|
type RouteResponse = Parameters<typeof getFile.onRequest>[1];
|
|
133
133
|
|
|
134
|
-
const invokeRoute = async (fileId: string): Promise<ResponseRecorder> => {
|
|
134
|
+
const invokeRoute = async (fileId: string, headers: Record<string, string> = {}): Promise<ResponseRecorder> => {
|
|
135
135
|
const response = new ResponseRecorder();
|
|
136
|
-
await getFile.onRequest(
|
|
136
|
+
await getFile.onRequest(
|
|
137
|
+
{ params: { id: fileId }, headers } as unknown as RouteRequest,
|
|
138
|
+
response as unknown as RouteResponse
|
|
139
|
+
);
|
|
137
140
|
return response;
|
|
138
141
|
};
|
|
139
142
|
|
|
@@ -189,6 +192,69 @@ describe('GET /file/:id — proxy serving (driver without signed URLs)', () => {
|
|
|
189
192
|
expect(Buffer.isBuffer(response.body)).toBe(true);
|
|
190
193
|
expect(Buffer.compare(response.body as Buffer, rawBytes)).toBe(0);
|
|
191
194
|
});
|
|
195
|
+
|
|
196
|
+
// ── HTTP Range on the proxy path — video streaming parity with the signed-URL path, so a
|
|
197
|
+
// <video> can seek against a proxy-served blob instead of downloading it whole. ──────────
|
|
198
|
+
describe('Range requests (video seek parity with the signed-URL path)', () => {
|
|
199
|
+
let videoFileId: string;
|
|
200
|
+
|
|
201
|
+
beforeAll(async () => {
|
|
202
|
+
const file = await new FileStorage().createFile(
|
|
203
|
+
{ name: 'smoke.webm', type: 'video/webm', size: rawBytes.length } as File,
|
|
204
|
+
rawBytes.toString('base64')
|
|
205
|
+
);
|
|
206
|
+
videoFileId = file.id;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('advertises Accept-Ranges and serves the whole file when no Range is asked', async () => {
|
|
210
|
+
const response = await invokeRoute(videoFileId);
|
|
211
|
+
expect(response.headers['accept-ranges']).toEqual('bytes');
|
|
212
|
+
expect(Buffer.compare(response.body as Buffer, rawBytes)).toBe(0);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('serves a bounded range as 206 with Content-Range and exactly those bytes', async () => {
|
|
216
|
+
const response = await invokeRoute(videoFileId, { range: 'bytes=10-19' });
|
|
217
|
+
expect(response.statusCode).toEqual(206);
|
|
218
|
+
expect(response.headers['content-range']).toEqual('bytes 10-19/256');
|
|
219
|
+
expect(response.headers['accept-ranges']).toEqual('bytes');
|
|
220
|
+
expect(Buffer.compare(response.body as Buffer, rawBytes.subarray(10, 20))).toBe(0);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('serves an open-ended range to EOF (the video-seek shape)', async () => {
|
|
224
|
+
const response = await invokeRoute(videoFileId, { range: 'bytes=200-' });
|
|
225
|
+
expect(response.statusCode).toEqual(206);
|
|
226
|
+
expect(response.headers['content-range']).toEqual('bytes 200-255/256');
|
|
227
|
+
expect(Buffer.compare(response.body as Buffer, rawBytes.subarray(200))).toBe(0);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('serves a suffix range (last N bytes)', async () => {
|
|
231
|
+
const response = await invokeRoute(videoFileId, { range: 'bytes=-16' });
|
|
232
|
+
expect(response.statusCode).toEqual(206);
|
|
233
|
+
expect(response.headers['content-range']).toEqual('bytes 240-255/256');
|
|
234
|
+
expect(Buffer.compare(response.body as Buffer, rawBytes.subarray(240))).toBe(0);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('clamps an over-long range to the end of the file', async () => {
|
|
238
|
+
const response = await invokeRoute(videoFileId, { range: 'bytes=250-999' });
|
|
239
|
+
expect(response.statusCode).toEqual(206);
|
|
240
|
+
expect(response.headers['content-range']).toEqual('bytes 250-255/256');
|
|
241
|
+
expect(Buffer.compare(response.body as Buffer, rawBytes.subarray(250))).toBe(0);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('416s an unsatisfiable range, naming the size', async () => {
|
|
245
|
+
const response = await invokeRoute(videoFileId, { range: 'bytes=999-' });
|
|
246
|
+
expect(response.statusCode).toEqual(416);
|
|
247
|
+
expect(response.headers['content-range']).toEqual('bytes */256');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('ignores malformed and multi-range headers — the whole file serves', async () => {
|
|
251
|
+
for (const range of ['bytes=5-2', 'apples=1-2', 'bytes=0-1,3-4', 'bytes=x-y']) {
|
|
252
|
+
const response = await invokeRoute(videoFileId, { range });
|
|
253
|
+
expect(response.statusCode).not.toEqual(206);
|
|
254
|
+
expect(Buffer.compare(response.body as Buffer, rawBytes)).toBe(0);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
192
258
|
});
|
|
193
259
|
|
|
194
260
|
describe('GET /file/:id — signed-URL serving (302 redirect)', () => {
|