@proteinjs/db-file 1.10.16 → 1.11.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/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +5 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/src/routes/FileResponder.d.ts +25 -0
- package/dist/src/routes/FileResponder.d.ts.map +1 -1
- package/dist/src/routes/FileResponder.js +88 -29
- package/dist/src/routes/FileResponder.js.map +1 -1
- package/dist/src/routes/getFileBytes.d.ts +14 -0
- package/dist/src/routes/getFileBytes.d.ts.map +1 -0
- package/dist/src/routes/getFileBytes.js +90 -0
- package/dist/src/routes/getFileBytes.js.map +1 -0
- package/dist/src/routes/getFileRoute.d.ts +14 -0
- package/dist/src/routes/getFileRoute.d.ts.map +1 -1
- package/dist/src/routes/getFileRoute.js +15 -1
- package/dist/src/routes/getFileRoute.js.map +1 -1
- package/dist/src/routes/getFileVariantBytes.d.ts +11 -0
- package/dist/src/routes/getFileVariantBytes.d.ts.map +1 -0
- package/dist/src/routes/getFileVariantBytes.js +103 -0
- package/dist/src/routes/getFileVariantBytes.js.map +1 -0
- package/dist/src/routes/getFileVariantRoute.d.ts +5 -0
- package/dist/src/routes/getFileVariantRoute.d.ts.map +1 -1
- package/dist/src/routes/getFileVariantRoute.js +7 -1
- package/dist/src/routes/getFileVariantRoute.js.map +1 -1
- package/dist/test/GetFileBytesRoute.test.d.ts +2 -0
- package/dist/test/GetFileBytesRoute.test.d.ts.map +1 -0
- package/dist/test/GetFileBytesRoute.test.js +488 -0
- package/dist/test/GetFileBytesRoute.test.js.map +1 -0
- package/generated/index.ts +5 -1
- package/package.json +4 -4
- package/src/routes/FileResponder.ts +62 -18
- package/src/routes/getFileBytes.ts +38 -0
- package/src/routes/getFileRoute.ts +16 -0
- package/src/routes/getFileVariantBytes.ts +42 -0
- package/src/routes/getFileVariantRoute.ts +7 -0
- package/test/GetFileBytesRoute.test.ts +316 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
var reflection_1 = require("@proteinjs/reflection");
|
|
40
|
+
var user_1 = require("@proteinjs/user");
|
|
41
|
+
var FileStorage_1 = require("../src/FileStorage");
|
|
42
|
+
var getFile_1 = require("../src/routes/getFile");
|
|
43
|
+
var getFileBytes_1 = require("../src/routes/getFileBytes");
|
|
44
|
+
var getFileVariantBytes_1 = require("../src/routes/getFileVariantBytes");
|
|
45
|
+
var getFileRoute_1 = require("../src/routes/getFileRoute");
|
|
46
|
+
var getFileVariantRoute_1 = require("../src/routes/getFileVariantRoute");
|
|
47
|
+
var FileTestEnvironment_1 = require("./FileTestEnvironment");
|
|
48
|
+
/** Every byte value once — a payload that corrupts under any text/base64 mix-up. */
|
|
49
|
+
var rawBytes = Buffer.from(Array.from({ length: 256 }, function (_, i) { return i; }));
|
|
50
|
+
/** The copy for others — the original's first eight bytes behind a mark, byte-distinct from it. */
|
|
51
|
+
var COPY_MARK = Buffer.from('COPY-FOR-OTHERS');
|
|
52
|
+
var copyOf = function (bytes) { return Buffer.concat([COPY_MARK, bytes.subarray(0, 8)]); };
|
|
53
|
+
/** A variant — the kind's name over the original's first eight bytes. */
|
|
54
|
+
var variantOf = function (kind, bytes) {
|
|
55
|
+
return Buffer.concat([Buffer.from("".concat(kind, ":")), bytes.subarray(0, 8)]);
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* A driver WITH an external URL space (the signed-URL serving case): `GET /file/:id` answers a 302 into
|
|
59
|
+
* it. The bytes doors must never redirect — they serve the bytes themselves, whatever the driver's URL
|
|
60
|
+
* space; so a signed URL minted by this driver is the proof that the door skipped it.
|
|
61
|
+
*/
|
|
62
|
+
var TestSignedUrlFileStorageDriver = /** @class */ (function () {
|
|
63
|
+
function TestSignedUrlFileStorageDriver() {
|
|
64
|
+
this.store = new Map();
|
|
65
|
+
this.signedUrlsMinted = 0;
|
|
66
|
+
}
|
|
67
|
+
TestSignedUrlFileStorageDriver.prototype.createFile = function (file, fileData) {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
69
|
+
return __generator(this, function (_a) {
|
|
70
|
+
this.store.set(file.id, Buffer.from(fileData, 'base64'));
|
|
71
|
+
return [2 /*return*/];
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
TestSignedUrlFileStorageDriver.prototype.getFileData = function (fileId) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
77
|
+
var data;
|
|
78
|
+
return __generator(this, function (_a) {
|
|
79
|
+
data = this.store.get(fileId);
|
|
80
|
+
if (data === undefined) {
|
|
81
|
+
throw new Error("No such object: ".concat(fileId));
|
|
82
|
+
}
|
|
83
|
+
return [2 /*return*/, data.toString('base64')];
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
TestSignedUrlFileStorageDriver.prototype.updateFileData = function (fileId, data) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
89
|
+
return __generator(this, function (_a) {
|
|
90
|
+
this.store.set(fileId, Buffer.from(data, 'base64'));
|
|
91
|
+
return [2 /*return*/];
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
TestSignedUrlFileStorageDriver.prototype.getSignedUrl = function (fileId) {
|
|
96
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
97
|
+
return __generator(this, function (_a) {
|
|
98
|
+
this.signedUrlsMinted++;
|
|
99
|
+
return [2 /*return*/, "https://blobs.example/".concat(fileId, "?sig=test")];
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
TestSignedUrlFileStorageDriver.prototype.deleteFile = function (fileId) {
|
|
104
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
105
|
+
return __generator(this, function (_a) {
|
|
106
|
+
this.store.delete(fileId);
|
|
107
|
+
return [2 /*return*/];
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
return TestSignedUrlFileStorageDriver;
|
|
112
|
+
}());
|
|
113
|
+
/** Minimal express Response recorder — the routes only touch these members. */
|
|
114
|
+
var ResponseRecorder = /** @class */ (function () {
|
|
115
|
+
function ResponseRecorder() {
|
|
116
|
+
this.headers = {};
|
|
117
|
+
}
|
|
118
|
+
ResponseRecorder.prototype.status = function (code) {
|
|
119
|
+
this.statusCode = code;
|
|
120
|
+
return this;
|
|
121
|
+
};
|
|
122
|
+
ResponseRecorder.prototype.send = function (body) {
|
|
123
|
+
this.body = body;
|
|
124
|
+
return this;
|
|
125
|
+
};
|
|
126
|
+
ResponseRecorder.prototype.setHeader = function (name, value) {
|
|
127
|
+
this.headers[name.toLowerCase()] = value;
|
|
128
|
+
};
|
|
129
|
+
ResponseRecorder.prototype.redirect = function (status, url) {
|
|
130
|
+
this.statusCode = status;
|
|
131
|
+
this.redirectUrl = url;
|
|
132
|
+
};
|
|
133
|
+
return ResponseRecorder;
|
|
134
|
+
}());
|
|
135
|
+
var invokeBytes = function (fileId, headers) {
|
|
136
|
+
if (headers === void 0) { headers = {}; }
|
|
137
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
138
|
+
var response;
|
|
139
|
+
return __generator(this, function (_a) {
|
|
140
|
+
switch (_a.label) {
|
|
141
|
+
case 0:
|
|
142
|
+
response = new ResponseRecorder();
|
|
143
|
+
return [4 /*yield*/, getFileBytes_1.getFileBytes.onRequest({ params: { id: fileId }, headers: headers }, response)];
|
|
144
|
+
case 1:
|
|
145
|
+
_a.sent();
|
|
146
|
+
return [2 /*return*/, response];
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
var invokeVariantBytes = function (fileId, kind, headers) {
|
|
152
|
+
if (headers === void 0) { headers = {}; }
|
|
153
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
154
|
+
var response;
|
|
155
|
+
return __generator(this, function (_a) {
|
|
156
|
+
switch (_a.label) {
|
|
157
|
+
case 0:
|
|
158
|
+
response = new ResponseRecorder();
|
|
159
|
+
return [4 /*yield*/, getFileVariantBytes_1.getFileVariantBytes.onRequest({ params: { id: fileId, kind: kind }, headers: headers }, response)];
|
|
160
|
+
case 1:
|
|
161
|
+
_a.sent();
|
|
162
|
+
return [2 /*return*/, response];
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
var invokeFile = function (fileId) { return __awaiter(void 0, void 0, void 0, function () {
|
|
168
|
+
var response;
|
|
169
|
+
return __generator(this, function (_a) {
|
|
170
|
+
switch (_a.label) {
|
|
171
|
+
case 0:
|
|
172
|
+
response = new ResponseRecorder();
|
|
173
|
+
return [4 /*yield*/, getFile_1.getFile.onRequest({ params: { id: fileId }, headers: {} }, response)];
|
|
174
|
+
case 1:
|
|
175
|
+
_a.sent();
|
|
176
|
+
return [2 /*return*/, response];
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}); };
|
|
180
|
+
var testEnv = new FileTestEnvironment_1.FileTestEnvironment();
|
|
181
|
+
var objectCache = function () { return reflection_1.SourceRepository.get().objectCache; };
|
|
182
|
+
var driver = new TestSignedUrlFileStorageDriver();
|
|
183
|
+
var owner;
|
|
184
|
+
var recipient;
|
|
185
|
+
var stranger;
|
|
186
|
+
/** File ids the stub resolver reports as reachable for the current user (the shared-content leg). */
|
|
187
|
+
var reachableFileIds = new Set();
|
|
188
|
+
beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
189
|
+
return __generator(this, function (_a) {
|
|
190
|
+
switch (_a.label) {
|
|
191
|
+
case 0: return [4 /*yield*/, testEnv.beforeAll()];
|
|
192
|
+
case 1:
|
|
193
|
+
_a.sent();
|
|
194
|
+
// The routes gate on UserAuth.isLoggedIn(), which resolves its user repo from the source repository —
|
|
195
|
+
// seed it (tests don't load the generated source graph) and reset the static cache.
|
|
196
|
+
objectCache()['@proteinjs/user-auth/AuthenticatedUserRepo'] = [new user_1.UserRepo()];
|
|
197
|
+
user_1.UserAuth.userRepo = undefined;
|
|
198
|
+
objectCache()['@proteinjs/db-file/FileReachabilityResolver'] = [
|
|
199
|
+
{ canReadViaReference: function (fileId) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
200
|
+
return [2 /*return*/, reachableFileIds.has(fileId)];
|
|
201
|
+
}); }); } },
|
|
202
|
+
];
|
|
203
|
+
testEnv.setDriver(driver);
|
|
204
|
+
return [4 /*yield*/, testEnv.createUser({ name: 'File owner', email: 'bytes-owner@test.local' })];
|
|
205
|
+
case 2:
|
|
206
|
+
owner = _a.sent();
|
|
207
|
+
return [4 /*yield*/, testEnv.createUser({ name: 'Share recipient', email: 'bytes-recipient@test.local' })];
|
|
208
|
+
case 3:
|
|
209
|
+
recipient = _a.sent();
|
|
210
|
+
return [4 /*yield*/, testEnv.createUser({ name: 'Stranger', email: 'bytes-stranger@test.local' })];
|
|
211
|
+
case 4:
|
|
212
|
+
stranger = _a.sent();
|
|
213
|
+
testEnv.actAs(owner);
|
|
214
|
+
return [2 /*return*/];
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}); });
|
|
218
|
+
afterAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
219
|
+
return __generator(this, function (_a) {
|
|
220
|
+
switch (_a.label) {
|
|
221
|
+
case 0:
|
|
222
|
+
user_1.UserAuth.userRepo = undefined;
|
|
223
|
+
delete objectCache()['@proteinjs/db-file/FileReachabilityResolver'];
|
|
224
|
+
return [4 /*yield*/, testEnv.afterAll()];
|
|
225
|
+
case 1:
|
|
226
|
+
_a.sent();
|
|
227
|
+
return [2 /*return*/];
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}); });
|
|
231
|
+
beforeEach(function () {
|
|
232
|
+
reachableFileIds.clear();
|
|
233
|
+
objectCache()['@proteinjs/db-file/FileCopyForOthers'] = [];
|
|
234
|
+
objectCache()['@proteinjs/db-file/FileVariantMaker'] = [];
|
|
235
|
+
driver.signedUrlsMinted = 0;
|
|
236
|
+
testEnv.actAs(owner);
|
|
237
|
+
});
|
|
238
|
+
var createOwnerFile = function (name, type) {
|
|
239
|
+
if (type === void 0) { type = 'image/png'; }
|
|
240
|
+
return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
241
|
+
switch (_a.label) {
|
|
242
|
+
case 0: return [4 /*yield*/, new FileStorage_1.FileStorage().createFile({ name: name, type: type, size: rawBytes.length }, rawBytes.toString('base64'))];
|
|
243
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
244
|
+
}
|
|
245
|
+
}); });
|
|
246
|
+
};
|
|
247
|
+
describe('the route paths — the bytes door is the file URL plus /bytes, one rule for both doors', function () {
|
|
248
|
+
it('names the doors from the app URLs the surfaces already use', function () {
|
|
249
|
+
expect(getFileRoute_1.getFileBytesRoute.path('f1')).toEqual("".concat(getFileRoute_1.getFileRoute.path('f1'), "/bytes"));
|
|
250
|
+
expect(getFileVariantRoute_1.getFileVariantBytesRoute.path('f1', 'preview')).toEqual("".concat(getFileVariantRoute_1.getFileVariantRoute.path('f1', 'preview'), "/bytes"));
|
|
251
|
+
expect(getFileBytes_1.getFileBytes.path).toEqual('/file/:id/bytes');
|
|
252
|
+
expect(getFileVariantBytes_1.getFileVariantBytes.path).toEqual('/file/:id/variant/:kind/bytes');
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
describe('GET /file/:id/bytes — the same-origin bytes door', function () {
|
|
256
|
+
it('serves the true bytes itself and never a redirect, though the driver has a URL space (the proxy branch)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
257
|
+
var file, asFile, response;
|
|
258
|
+
return __generator(this, function (_a) {
|
|
259
|
+
switch (_a.label) {
|
|
260
|
+
case 0: return [4 /*yield*/, createOwnerFile('shot.png')];
|
|
261
|
+
case 1:
|
|
262
|
+
file = _a.sent();
|
|
263
|
+
return [4 /*yield*/, invokeFile(file.id)];
|
|
264
|
+
case 2:
|
|
265
|
+
asFile = _a.sent();
|
|
266
|
+
expect(asFile.statusCode).toEqual(302);
|
|
267
|
+
return [4 /*yield*/, invokeBytes(file.id)];
|
|
268
|
+
case 3:
|
|
269
|
+
response = _a.sent();
|
|
270
|
+
expect(response.redirectUrl).toBeUndefined();
|
|
271
|
+
expect(response.statusCode).not.toEqual(302);
|
|
272
|
+
expect(response.headers['content-type']).toEqual('image/png');
|
|
273
|
+
expect(Buffer.isBuffer(response.body)).toBe(true);
|
|
274
|
+
expect(Buffer.compare(response.body, rawBytes)).toBe(0);
|
|
275
|
+
// The signed URL the file route minted is the only one — the bytes door never asked for one.
|
|
276
|
+
expect(driver.signedUrlsMinted).toEqual(1);
|
|
277
|
+
return [2 /*return*/];
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}); });
|
|
281
|
+
it('says the browser may keep the bytes privately — never a shared cache (the bytes are per caller)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
282
|
+
var file, response;
|
|
283
|
+
return __generator(this, function (_a) {
|
|
284
|
+
switch (_a.label) {
|
|
285
|
+
case 0: return [4 /*yield*/, createOwnerFile('cached.png')];
|
|
286
|
+
case 1:
|
|
287
|
+
file = _a.sent();
|
|
288
|
+
return [4 /*yield*/, invokeBytes(file.id)];
|
|
289
|
+
case 2:
|
|
290
|
+
response = _a.sent();
|
|
291
|
+
expect(response.headers['cache-control']).toEqual('private, max-age=300');
|
|
292
|
+
expect(response.headers['cache-control']).not.toContain('public');
|
|
293
|
+
return [2 /*return*/];
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}); });
|
|
297
|
+
it('honors Range exactly as the proxy branch does (206 with the bytes asked)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
298
|
+
var file, response;
|
|
299
|
+
return __generator(this, function (_a) {
|
|
300
|
+
switch (_a.label) {
|
|
301
|
+
case 0: return [4 /*yield*/, createOwnerFile('clip.webm', 'video/webm')];
|
|
302
|
+
case 1:
|
|
303
|
+
file = _a.sent();
|
|
304
|
+
return [4 /*yield*/, invokeBytes(file.id, { range: 'bytes=10-19' })];
|
|
305
|
+
case 2:
|
|
306
|
+
response = _a.sent();
|
|
307
|
+
expect(response.statusCode).toEqual(206);
|
|
308
|
+
expect(response.headers['accept-ranges']).toEqual('bytes');
|
|
309
|
+
expect(response.headers['content-range']).toEqual('bytes 10-19/256');
|
|
310
|
+
expect(Buffer.compare(response.body, rawBytes.subarray(10, 20))).toBe(0);
|
|
311
|
+
return [2 /*return*/];
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}); });
|
|
315
|
+
it('serves a shared-content recipient the COPY for others, as every door does — the same egress owner', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
316
|
+
var file, response, own;
|
|
317
|
+
return __generator(this, function (_a) {
|
|
318
|
+
switch (_a.label) {
|
|
319
|
+
case 0: return [4 /*yield*/, createOwnerFile('shared.jpg', 'image/jpeg')];
|
|
320
|
+
case 1:
|
|
321
|
+
file = _a.sent();
|
|
322
|
+
reachableFileIds.add(file.id);
|
|
323
|
+
objectCache()['@proteinjs/db-file/FileCopyForOthers'] = [
|
|
324
|
+
{ appliesTo: function () { return true; }, make: function (_file, bytes) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
325
|
+
return [2 /*return*/, copyOf(bytes)];
|
|
326
|
+
}); }); } },
|
|
327
|
+
];
|
|
328
|
+
testEnv.actAs(recipient);
|
|
329
|
+
return [4 /*yield*/, invokeBytes(file.id)];
|
|
330
|
+
case 2:
|
|
331
|
+
response = _a.sent();
|
|
332
|
+
expect(response.statusCode).not.toEqual(404);
|
|
333
|
+
expect(Buffer.compare(response.body, copyOf(rawBytes))).toBe(0);
|
|
334
|
+
expect(Buffer.compare(response.body, rawBytes)).not.toBe(0);
|
|
335
|
+
// The owner's own read through the same door is the original.
|
|
336
|
+
testEnv.actAs(owner);
|
|
337
|
+
return [4 /*yield*/, invokeBytes(file.id)];
|
|
338
|
+
case 3:
|
|
339
|
+
own = _a.sent();
|
|
340
|
+
expect(Buffer.compare(own.body, rawBytes)).toBe(0);
|
|
341
|
+
return [2 /*return*/];
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}); });
|
|
345
|
+
it("THE ERROR SURFACE: a stranger holding only the id is answered 404 'File not found' — the row read decides, no new permission", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
346
|
+
var file, response;
|
|
347
|
+
return __generator(this, function (_a) {
|
|
348
|
+
switch (_a.label) {
|
|
349
|
+
case 0: return [4 /*yield*/, createOwnerFile('private.png')];
|
|
350
|
+
case 1:
|
|
351
|
+
file = _a.sent();
|
|
352
|
+
testEnv.actAs(stranger);
|
|
353
|
+
return [4 /*yield*/, invokeBytes(file.id)];
|
|
354
|
+
case 2:
|
|
355
|
+
response = _a.sent();
|
|
356
|
+
expect(response.statusCode).toEqual(404);
|
|
357
|
+
expect(response.body).toEqual('File not found');
|
|
358
|
+
expect(response.redirectUrl).toBeUndefined();
|
|
359
|
+
return [2 /*return*/];
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}); });
|
|
363
|
+
it('404s on a missing id without touching the driver', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
364
|
+
var response;
|
|
365
|
+
return __generator(this, function (_a) {
|
|
366
|
+
switch (_a.label) {
|
|
367
|
+
case 0: return [4 /*yield*/, invokeBytes('00000000-0000-0000-0000-000000000000')];
|
|
368
|
+
case 1:
|
|
369
|
+
response = _a.sent();
|
|
370
|
+
expect(response.statusCode).toEqual(404);
|
|
371
|
+
expect(response.body).toEqual('File not found');
|
|
372
|
+
expect(driver.signedUrlsMinted).toEqual(0);
|
|
373
|
+
return [2 /*return*/];
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}); });
|
|
377
|
+
it('401s when not logged in', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
378
|
+
var file, response;
|
|
379
|
+
return __generator(this, function (_a) {
|
|
380
|
+
switch (_a.label) {
|
|
381
|
+
case 0: return [4 /*yield*/, createOwnerFile('clip.png')];
|
|
382
|
+
case 1:
|
|
383
|
+
file = _a.sent();
|
|
384
|
+
testEnv.actAs(user_1.guestUser);
|
|
385
|
+
_a.label = 2;
|
|
386
|
+
case 2:
|
|
387
|
+
_a.trys.push([2, , 4, 5]);
|
|
388
|
+
return [4 /*yield*/, invokeBytes(file.id)];
|
|
389
|
+
case 3:
|
|
390
|
+
response = _a.sent();
|
|
391
|
+
expect(response.statusCode).toEqual(401);
|
|
392
|
+
expect(response.body).toBeDefined();
|
|
393
|
+
expect(Buffer.isBuffer(response.body)).toBe(false);
|
|
394
|
+
return [3 /*break*/, 5];
|
|
395
|
+
case 4:
|
|
396
|
+
testEnv.actAs(owner);
|
|
397
|
+
return [7 /*endfinally*/];
|
|
398
|
+
case 5: return [2 /*return*/];
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}); });
|
|
402
|
+
});
|
|
403
|
+
describe('GET /file/:id/variant/:kind/bytes — the variant through the same door', function () {
|
|
404
|
+
it("serves the variant's bytes itself, never a redirect (the maker's bytes, not the original's)", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
405
|
+
var file, response;
|
|
406
|
+
return __generator(this, function (_a) {
|
|
407
|
+
switch (_a.label) {
|
|
408
|
+
case 0:
|
|
409
|
+
objectCache()['@proteinjs/db-file/FileVariantMaker'] = [
|
|
410
|
+
{
|
|
411
|
+
appliesTo: function (file, kind) { return file.type.startsWith('image/') && kind === 'preview'; },
|
|
412
|
+
make: function (_file, bytes, kind) { return __awaiter(void 0, void 0, void 0, function () {
|
|
413
|
+
return __generator(this, function (_a) {
|
|
414
|
+
return [2 /*return*/, ({
|
|
415
|
+
bytes: variantOf(kind, bytes),
|
|
416
|
+
type: 'image/webp',
|
|
417
|
+
width: 8,
|
|
418
|
+
height: 4,
|
|
419
|
+
})];
|
|
420
|
+
});
|
|
421
|
+
}); },
|
|
422
|
+
},
|
|
423
|
+
];
|
|
424
|
+
return [4 /*yield*/, createOwnerFile('photo.jpg', 'image/jpeg')];
|
|
425
|
+
case 1:
|
|
426
|
+
file = _a.sent();
|
|
427
|
+
return [4 /*yield*/, invokeVariantBytes(file.id, 'preview')];
|
|
428
|
+
case 2:
|
|
429
|
+
response = _a.sent();
|
|
430
|
+
expect(response.redirectUrl).toBeUndefined();
|
|
431
|
+
expect(response.headers['content-type']).toEqual('image/webp');
|
|
432
|
+
expect(response.headers['cache-control']).toEqual('private, max-age=300');
|
|
433
|
+
expect(Buffer.compare(response.body, variantOf('preview', rawBytes))).toBe(0);
|
|
434
|
+
return [2 /*return*/];
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
}); });
|
|
438
|
+
it('serves the file itself when no variant applies (the consumer never branches)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
439
|
+
var file, response;
|
|
440
|
+
return __generator(this, function (_a) {
|
|
441
|
+
switch (_a.label) {
|
|
442
|
+
case 0: return [4 /*yield*/, createOwnerFile('tiny.png')];
|
|
443
|
+
case 1:
|
|
444
|
+
file = _a.sent();
|
|
445
|
+
return [4 /*yield*/, invokeVariantBytes(file.id, 'preview')];
|
|
446
|
+
case 2:
|
|
447
|
+
response = _a.sent();
|
|
448
|
+
expect(response.redirectUrl).toBeUndefined();
|
|
449
|
+
expect(Buffer.compare(response.body, rawBytes)).toBe(0);
|
|
450
|
+
return [2 /*return*/];
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}); });
|
|
454
|
+
it('404s a kind the library does not know, as a file that is not there', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
455
|
+
var file, response;
|
|
456
|
+
return __generator(this, function (_a) {
|
|
457
|
+
switch (_a.label) {
|
|
458
|
+
case 0: return [4 /*yield*/, createOwnerFile('any.png')];
|
|
459
|
+
case 1:
|
|
460
|
+
file = _a.sent();
|
|
461
|
+
return [4 /*yield*/, invokeVariantBytes(file.id, 'poster')];
|
|
462
|
+
case 2:
|
|
463
|
+
response = _a.sent();
|
|
464
|
+
expect(response.statusCode).toEqual(404);
|
|
465
|
+
expect(response.body).toEqual('File not found');
|
|
466
|
+
return [2 /*return*/];
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}); });
|
|
470
|
+
it("THE ERROR SURFACE: a stranger is answered 404 for a variant too — the original's read decides", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
471
|
+
var file, response;
|
|
472
|
+
return __generator(this, function (_a) {
|
|
473
|
+
switch (_a.label) {
|
|
474
|
+
case 0: return [4 /*yield*/, createOwnerFile('private.jpg', 'image/jpeg')];
|
|
475
|
+
case 1:
|
|
476
|
+
file = _a.sent();
|
|
477
|
+
testEnv.actAs(stranger);
|
|
478
|
+
return [4 /*yield*/, invokeVariantBytes(file.id, 'preview')];
|
|
479
|
+
case 2:
|
|
480
|
+
response = _a.sent();
|
|
481
|
+
expect(response.statusCode).toEqual(404);
|
|
482
|
+
expect(response.body).toEqual('File not found');
|
|
483
|
+
return [2 /*return*/];
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}); });
|
|
487
|
+
});
|
|
488
|
+
//# sourceMappingURL=GetFileBytesRoute.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GetFileBytesRoute.test.js","sourceRoot":"","sources":["../../test/GetFileBytesRoute.test.ts"],"names":[],"mappings":"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"}
|