@dudousxd/nestjs-media-dashboard 0.16.2 → 0.18.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/dist/client/index.d.ts +1 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -4
- package/dist/client/index.js.map +1 -1
- package/dist/client/media-console-client.d.ts +14 -23
- package/dist/client/media-console-client.d.ts.map +1 -1
- package/dist/client/media-console-client.js +53 -46
- package/dist/client/media-console-client.js.map +1 -1
- package/dist/client/types.d.ts +10 -6
- package/dist/client/types.d.ts.map +1 -1
- package/dist/server/index.cjs +805 -1235
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +362 -239
- package/dist/server/index.d.ts +362 -239
- package/dist/server/index.js +790 -1235
- package/dist/server/index.js.map +1 -1
- package/dist/spa/assets/{ArchivePreview-h5wYCDJT.js → ArchivePreview-DacpA4QQ.js} +1 -1
- package/dist/spa/assets/CertificatePreview-CEfJ-ORW.js +127 -0
- package/dist/spa/assets/MarkdownPreview-47FMx48l.js +74 -0
- package/dist/spa/assets/ParquetPreview-DHvs5kV-.js +973 -0
- package/dist/spa/assets/{SheetPreview-Bfq7dI0a.js → SheetPreview-AT23RGVb.js} +1 -1
- package/dist/spa/assets/{SqlitePreview-JTQDjJJ3.js → SqlitePreview-D7KZuyZD.js} +1 -1
- package/dist/spa/assets/{index-Cjax4szq.js → index-BmejPQYi.js} +1 -1
- package/dist/spa/assets/{preview-BjGG6sSu.js → preview-DVKz8AwF.js} +2 -2
- package/dist/spa/assets/styles-CG_mruF7.css +1 -0
- package/dist/spa/assets/styles-CTvG1yXb.js +61 -0
- package/dist/spa/index.html +3 -3
- package/dist/spa/preview.html +3 -3
- package/package.json +25 -30
- package/dist/client/console-session.d.ts +0 -69
- package/dist/client/console-session.d.ts.map +0 -1
- package/dist/client/console-session.js +0 -120
- package/dist/client/console-session.js.map +0 -1
- package/dist/react/index.d.ts +0 -4
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js +0 -15
- package/dist/react/index.js.map +0 -1
- package/dist/react/open-console-button.d.ts +0 -26
- package/dist/react/open-console-button.d.ts.map +0 -1
- package/dist/react/open-console-button.js +0 -29
- package/dist/react/open-console-button.js.map +0 -1
- package/dist/react/use-open-console.d.ts +0 -45
- package/dist/react/use-open-console.d.ts.map +0 -1
- package/dist/react/use-open-console.js +0 -75
- package/dist/react/use-open-console.js.map +0 -1
- package/dist/spa/assets/CertificatePreview-Bm4NkGaO.js +0 -127
- package/dist/spa/assets/MarkdownPreview-Cai2BrTH.js +0 -70
- package/dist/spa/assets/ParquetPreview-DfDTL_6f.js +0 -973
- package/dist/spa/assets/styles-Cui6Yy9Y.js +0 -61
- package/dist/spa/assets/styles-FRkIQ83o.css +0 -1
package/dist/server/index.cjs
CHANGED
|
@@ -21,338 +21,46 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/server/index.ts
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
-
|
|
24
|
+
CONSOLE_AUTHORIZE_DECIDE: () => CONSOLE_AUTHORIZE_DECIDE,
|
|
25
|
+
CONSOLE_AUTHORIZE_MISSING_WARNING: () => CONSOLE_AUTHORIZE_MISSING_WARNING,
|
|
26
|
+
CONSOLE_GATE: () => CONSOLE_GATE,
|
|
27
|
+
CONSOLE_PAGE_METADATA: () => CONSOLE_PAGE_METADATA,
|
|
28
|
+
ConsoleAccessDenied: () => ConsoleAccessDenied,
|
|
29
|
+
ConsoleAuthorizeFilter: () => ConsoleAuthorizeFilter,
|
|
30
|
+
ConsoleAuthorizeGuard: () => ConsoleAuthorizeGuard,
|
|
31
|
+
ConsolePage: () => ConsolePage,
|
|
32
|
+
MEDIA_MULTIPART: () => MEDIA_MULTIPART,
|
|
25
33
|
MEDIA_STORE: () => MEDIA_STORE,
|
|
26
34
|
MEDIA_UPLOAD_SESSIONS: () => MEDIA_UPLOAD_SESSIONS,
|
|
35
|
+
MediaConsoleActionsController: () => MediaConsoleActionsController,
|
|
27
36
|
MediaConsoleApiModule: () => MediaConsoleApiModule,
|
|
37
|
+
MediaConsoleReadController: () => MediaConsoleReadController,
|
|
28
38
|
MediaConsoleService: () => MediaConsoleService,
|
|
29
|
-
MediaDashboardModule: () => MediaDashboardModule
|
|
39
|
+
MediaDashboardModule: () => MediaDashboardModule,
|
|
40
|
+
MediaDashboardUiController: () => MediaDashboardUiController,
|
|
41
|
+
UseConsoleAuthorize: () => UseConsoleAuthorize,
|
|
42
|
+
authorizeByToken: () => authorizeByToken,
|
|
43
|
+
normalizeConsoleResult: () => normalizeConsoleResult,
|
|
44
|
+
resolveConsoleAuthorize: () => resolveConsoleAuthorize
|
|
30
45
|
});
|
|
31
46
|
module.exports = __toCommonJS(index_exports);
|
|
32
47
|
|
|
33
|
-
// ../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.16.
|
|
48
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.16.21_jiti@1.21.7_postcss@8.5.32_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js
|
|
34
49
|
var getImportMetaUrl = /* @__PURE__ */ __name(() => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href, "getImportMetaUrl");
|
|
35
50
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
36
51
|
|
|
37
52
|
// src/server/media-dashboard.module.ts
|
|
38
|
-
var
|
|
39
|
-
var
|
|
53
|
+
var import_common7 = require("@nestjs/common");
|
|
54
|
+
var import_core2 = require("@nestjs/core");
|
|
40
55
|
|
|
41
|
-
// src/server/
|
|
42
|
-
var
|
|
43
|
-
var DURATION_UNITS = {
|
|
44
|
-
s: 1e3,
|
|
45
|
-
m: 60 * 1e3,
|
|
46
|
-
h: 60 * 60 * 1e3,
|
|
47
|
-
d: 24 * 60 * 60 * 1e3
|
|
48
|
-
};
|
|
49
|
-
function durationToMs(ttl) {
|
|
50
|
-
if (ttl === void 0) return DEFAULT_TTL_MS;
|
|
51
|
-
const match = /^(\d+)([smhd])$/.exec(ttl.trim());
|
|
52
|
-
if (!match) return DEFAULT_TTL_MS;
|
|
53
|
-
const unit = DURATION_UNITS[match[2] ?? ""];
|
|
54
|
-
if (unit === void 0) return DEFAULT_TTL_MS;
|
|
55
|
-
return Number(match[1]) * unit;
|
|
56
|
-
}
|
|
57
|
-
__name(durationToMs, "durationToMs");
|
|
58
|
-
function resolveConsoleAuth(options) {
|
|
59
|
-
if (!options) return null;
|
|
60
|
-
if (!options.secret) {
|
|
61
|
-
throw new Error("MediaDashboardModule: auth.secret is required when `auth` is configured.");
|
|
62
|
-
}
|
|
63
|
-
const modes = [];
|
|
64
|
-
if (options.session) modes.push("session");
|
|
65
|
-
if (options.login) modes.push("login");
|
|
66
|
-
if (modes.length === 0) {
|
|
67
|
-
throw new Error("MediaDashboardModule: auth needs a `login` and/or `session` hook.");
|
|
68
|
-
}
|
|
69
|
-
if (options.unauthenticatedPage !== void 0 && typeof options.unauthenticatedPage !== "function") {
|
|
70
|
-
throw new Error("MediaDashboardModule: auth.unauthenticatedPage must be a function.");
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
secret: options.secret,
|
|
74
|
-
ttlMs: durationToMs(options.ttl),
|
|
75
|
-
modes,
|
|
76
|
-
...options.session ? {
|
|
77
|
-
session: options.session
|
|
78
|
-
} : {},
|
|
79
|
-
...options.login ? {
|
|
80
|
-
login: options.login
|
|
81
|
-
} : {},
|
|
82
|
-
...options.revalidate ? {
|
|
83
|
-
revalidate: options.revalidate
|
|
84
|
-
} : {},
|
|
85
|
-
...options.unauthenticatedPage ? {
|
|
86
|
-
unauthenticatedPage: options.unauthenticatedPage
|
|
87
|
-
} : {}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
__name(resolveConsoleAuth, "resolveConsoleAuth");
|
|
56
|
+
// src/server/controller-decorators.ts
|
|
57
|
+
var import_reflect_metadata2 = require("reflect-metadata");
|
|
91
58
|
|
|
92
|
-
// src/server/
|
|
59
|
+
// src/server/console-authorize.ts
|
|
93
60
|
var import_reflect_metadata = require("reflect-metadata");
|
|
94
|
-
var GUARDS_METADATA = "__guards__";
|
|
95
|
-
function isGuardClass(guard) {
|
|
96
|
-
return typeof guard === "function";
|
|
97
|
-
}
|
|
98
|
-
__name(isGuardClass, "isGuardClass");
|
|
99
|
-
function baseGuards(controller) {
|
|
100
|
-
return Reflect.getOwnMetadata(GUARDS_METADATA, controller) ?? [];
|
|
101
|
-
}
|
|
102
|
-
__name(baseGuards, "baseGuards");
|
|
103
|
-
function stampGuards(guards, entries) {
|
|
104
|
-
if (guards === void 0 || guards.length === 0) return;
|
|
105
|
-
for (const [controller, base] of entries) {
|
|
106
|
-
Reflect.defineMetadata(GUARDS_METADATA, [
|
|
107
|
-
...base,
|
|
108
|
-
...guards
|
|
109
|
-
], controller);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
__name(stampGuards, "stampGuards");
|
|
113
|
-
|
|
114
|
-
// src/server/media-console-api.module.ts
|
|
115
|
-
var import_common6 = require("@nestjs/common");
|
|
116
|
-
|
|
117
|
-
// src/server/media-console-actions.controller.ts
|
|
118
|
-
var import_common3 = require("@nestjs/common");
|
|
119
|
-
|
|
120
|
-
// src/server/media-console.guard.ts
|
|
121
|
-
var import_common = require("@nestjs/common");
|
|
122
|
-
|
|
123
|
-
// src/server/auth/cookie-header.ts
|
|
124
|
-
function parseCookieHeader(header) {
|
|
125
|
-
const cookies = {};
|
|
126
|
-
if (typeof header !== "string" || header === "") return cookies;
|
|
127
|
-
for (const segment of header.split(";")) {
|
|
128
|
-
const eq = segment.indexOf("=");
|
|
129
|
-
if (eq <= 0) continue;
|
|
130
|
-
const name = segment.slice(0, eq).trim();
|
|
131
|
-
if (name === "") continue;
|
|
132
|
-
let rawValue = segment.slice(eq + 1).trim();
|
|
133
|
-
if (rawValue.length >= 2 && rawValue.startsWith('"') && rawValue.endsWith('"')) {
|
|
134
|
-
rawValue = rawValue.slice(1, -1);
|
|
135
|
-
}
|
|
136
|
-
if (name in cookies) continue;
|
|
137
|
-
try {
|
|
138
|
-
cookies[name] = decodeURIComponent(rawValue);
|
|
139
|
-
} catch {
|
|
140
|
-
cookies[name] = rawValue;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return cookies;
|
|
144
|
-
}
|
|
145
|
-
__name(parseCookieHeader, "parseCookieHeader");
|
|
146
|
-
function serializeSetCookie(name, value, options) {
|
|
147
|
-
const parts = [
|
|
148
|
-
`${name}=${options.clear ? "" : encodeURIComponent(value)}`
|
|
149
|
-
];
|
|
150
|
-
parts.push(`Path=${options.path}`);
|
|
151
|
-
parts.push("HttpOnly");
|
|
152
|
-
parts.push("SameSite=Lax");
|
|
153
|
-
if (options.secure) parts.push("Secure");
|
|
154
|
-
if (options.clear) {
|
|
155
|
-
parts.push("Max-Age=0");
|
|
156
|
-
parts.push("Expires=Thu, 01 Jan 1970 00:00:00 GMT");
|
|
157
|
-
} else {
|
|
158
|
-
parts.push(`Max-Age=${options.maxAgeSeconds}`);
|
|
159
|
-
}
|
|
160
|
-
return parts.join("; ");
|
|
161
|
-
}
|
|
162
|
-
__name(serializeSetCookie, "serializeSetCookie");
|
|
163
|
-
|
|
164
|
-
// src/server/auth/request.ts
|
|
165
|
-
function isRecord(value) {
|
|
166
|
-
return typeof value === "object" && value !== null;
|
|
167
|
-
}
|
|
168
|
-
__name(isRecord, "isRecord");
|
|
169
|
-
function readHeaders(request) {
|
|
170
|
-
if (!isRecord(request)) return {};
|
|
171
|
-
const headers = request.headers;
|
|
172
|
-
return isRecord(headers) ? headers : {};
|
|
173
|
-
}
|
|
174
|
-
__name(readHeaders, "readHeaders");
|
|
175
|
-
function headerToString(value) {
|
|
176
|
-
if (typeof value === "string") return value;
|
|
177
|
-
if (Array.isArray(value) && value.every((part) => typeof part === "string")) {
|
|
178
|
-
return value.join(",");
|
|
179
|
-
}
|
|
180
|
-
return void 0;
|
|
181
|
-
}
|
|
182
|
-
__name(headerToString, "headerToString");
|
|
183
|
-
function readCookieHeader(request) {
|
|
184
|
-
return headerToString(readHeaders(request).cookie);
|
|
185
|
-
}
|
|
186
|
-
__name(readCookieHeader, "readCookieHeader");
|
|
187
|
-
function isHttpsRequest(request) {
|
|
188
|
-
if (isRecord(request) && request.protocol === "https") return true;
|
|
189
|
-
const forwarded = headerToString(readHeaders(request)["x-forwarded-proto"]);
|
|
190
|
-
if (forwarded === void 0) return false;
|
|
191
|
-
return forwarded.split(",").map((proto) => proto.trim().toLowerCase()).includes("https");
|
|
192
|
-
}
|
|
193
|
-
__name(isHttpsRequest, "isHttpsRequest");
|
|
194
|
-
function attachSession(request, session) {
|
|
195
|
-
if (!isRecord(request)) return;
|
|
196
|
-
request.consoleSession = session;
|
|
197
|
-
}
|
|
198
|
-
__name(attachSession, "attachSession");
|
|
199
|
-
|
|
200
|
-
// src/server/auth/response.ts
|
|
201
|
-
function isRecord2(value) {
|
|
202
|
-
return typeof value === "object" && value !== null;
|
|
203
|
-
}
|
|
204
|
-
__name(isRecord2, "isRecord");
|
|
205
|
-
function isRawNodeResponse(value) {
|
|
206
|
-
return isRecord2(value) && typeof value.getHeader === "function" && typeof value.setHeader === "function";
|
|
207
|
-
}
|
|
208
|
-
__name(isRawNodeResponse, "isRawNodeResponse");
|
|
209
|
-
function resolveRawResponse(response) {
|
|
210
|
-
if (isRawNodeResponse(response)) return response;
|
|
211
|
-
if (isRecord2(response) && isRawNodeResponse(response.raw)) return response.raw;
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
__name(resolveRawResponse, "resolveRawResponse");
|
|
215
|
-
function existingSetCookies(response) {
|
|
216
|
-
const current = response.getHeader("set-cookie");
|
|
217
|
-
if (Array.isArray(current)) return current;
|
|
218
|
-
if (typeof current === "string") return [
|
|
219
|
-
current
|
|
220
|
-
];
|
|
221
|
-
return [];
|
|
222
|
-
}
|
|
223
|
-
__name(existingSetCookies, "existingSetCookies");
|
|
224
|
-
function appendSetCookie(response, cookie) {
|
|
225
|
-
const raw = resolveRawResponse(response);
|
|
226
|
-
if (!raw) return;
|
|
227
|
-
raw.setHeader("set-cookie", [
|
|
228
|
-
...existingSetCookies(raw),
|
|
229
|
-
cookie
|
|
230
|
-
]);
|
|
231
|
-
}
|
|
232
|
-
__name(appendSetCookie, "appendSetCookie");
|
|
233
|
-
function resolveEndableResponse(response) {
|
|
234
|
-
const isEndable = /* @__PURE__ */ __name((value) => isRawNodeResponse(value) && typeof value.end === "function", "isEndable");
|
|
235
|
-
if (isEndable(response)) return response;
|
|
236
|
-
if (isRecord2(response) && isEndable(response.raw)) return response.raw;
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
__name(resolveEndableResponse, "resolveEndableResponse");
|
|
240
|
-
function responseAlreadyWritten(response) {
|
|
241
|
-
return resolveEndableResponse(response)?.headersSent === true;
|
|
242
|
-
}
|
|
243
|
-
__name(responseAlreadyWritten, "responseAlreadyWritten");
|
|
244
|
-
function sendHtml(response, status, html) {
|
|
245
|
-
const raw = resolveEndableResponse(response);
|
|
246
|
-
if (!raw) return;
|
|
247
|
-
raw.statusCode = status;
|
|
248
|
-
raw.setHeader("content-type", "text/html; charset=utf-8");
|
|
249
|
-
raw.setHeader("cache-control", "no-store, must-revalidate");
|
|
250
|
-
raw.end(html);
|
|
251
|
-
}
|
|
252
|
-
__name(sendHtml, "sendHtml");
|
|
253
|
-
|
|
254
|
-
// src/server/auth/session-cookie.ts
|
|
255
61
|
var import_node_crypto = require("crypto");
|
|
256
|
-
var
|
|
257
|
-
|
|
258
|
-
return Buffer.from(value, "utf8").toString("base64url");
|
|
259
|
-
}
|
|
260
|
-
__name(base64urlEncode, "base64urlEncode");
|
|
261
|
-
function base64urlDecode(value) {
|
|
262
|
-
return Buffer.from(value, "base64url").toString("utf8");
|
|
263
|
-
}
|
|
264
|
-
__name(base64urlDecode, "base64urlDecode");
|
|
265
|
-
function hmac(secret, payload) {
|
|
266
|
-
return (0, import_node_crypto.createHmac)("sha256", secret).update(payload).digest("base64url");
|
|
267
|
-
}
|
|
268
|
-
__name(hmac, "hmac");
|
|
269
|
-
function signSessionCookie(user, options) {
|
|
270
|
-
const issuedAt = options.now ?? Date.now();
|
|
271
|
-
const session = {
|
|
272
|
-
sub: user.id,
|
|
273
|
-
...user.name !== void 0 ? {
|
|
274
|
-
name: user.name
|
|
275
|
-
} : {},
|
|
276
|
-
roles: user.roles ?? [],
|
|
277
|
-
iat: issuedAt,
|
|
278
|
-
exp: issuedAt + options.ttlMs
|
|
279
|
-
};
|
|
280
|
-
const encodedPayload = base64urlEncode(JSON.stringify(session));
|
|
281
|
-
return `${encodedPayload}.${hmac(options.secret, encodedPayload)}`;
|
|
282
|
-
}
|
|
283
|
-
__name(signSessionCookie, "signSessionCookie");
|
|
284
|
-
function isSessionPayload(value) {
|
|
285
|
-
if (typeof value !== "object" || value === null) return false;
|
|
286
|
-
const record = value;
|
|
287
|
-
if (typeof record.sub !== "string") return false;
|
|
288
|
-
if (record.name !== void 0 && typeof record.name !== "string") return false;
|
|
289
|
-
if (!Array.isArray(record.roles)) return false;
|
|
290
|
-
if (!record.roles.every((role) => typeof role === "string")) return false;
|
|
291
|
-
if (typeof record.iat !== "number" || !Number.isFinite(record.iat)) return false;
|
|
292
|
-
if (typeof record.exp !== "number" || !Number.isFinite(record.exp)) return false;
|
|
293
|
-
return true;
|
|
294
|
-
}
|
|
295
|
-
__name(isSessionPayload, "isSessionPayload");
|
|
296
|
-
function verifySessionCookie(value, options) {
|
|
297
|
-
try {
|
|
298
|
-
const dot = value.indexOf(".");
|
|
299
|
-
if (dot <= 0 || dot === value.length - 1) return null;
|
|
300
|
-
const encodedPayload = value.slice(0, dot);
|
|
301
|
-
const provided = Buffer.from(value.slice(dot + 1), "base64url");
|
|
302
|
-
const expected = Buffer.from(hmac(options.secret, encodedPayload), "base64url");
|
|
303
|
-
if (provided.length !== expected.length) return null;
|
|
304
|
-
if (!(0, import_node_crypto.timingSafeEqual)(provided, expected)) return null;
|
|
305
|
-
const decoded = JSON.parse(base64urlDecode(encodedPayload));
|
|
306
|
-
if (!isSessionPayload(decoded)) return null;
|
|
307
|
-
const now = options.now ?? Date.now();
|
|
308
|
-
if (now > decoded.exp + EXP_GRACE_MS) return null;
|
|
309
|
-
return decoded;
|
|
310
|
-
} catch {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
__name(verifySessionCookie, "verifySessionCookie");
|
|
315
|
-
|
|
316
|
-
// src/server/auth/session-cookie-io.ts
|
|
317
|
-
var SESSION_COOKIE_NAME = "media_console_session";
|
|
318
|
-
function issueSessionCookie(user, context) {
|
|
319
|
-
const value = signSessionCookie(user, {
|
|
320
|
-
secret: context.auth.secret,
|
|
321
|
-
ttlMs: context.auth.ttlMs,
|
|
322
|
-
...context.now !== void 0 ? {
|
|
323
|
-
now: context.now
|
|
324
|
-
} : {}
|
|
325
|
-
});
|
|
326
|
-
appendSetCookie(context.response, serializeSetCookie(SESSION_COOKIE_NAME, value, {
|
|
327
|
-
path: context.cookiePath,
|
|
328
|
-
maxAgeSeconds: Math.floor(context.auth.ttlMs / 1e3),
|
|
329
|
-
secure: isHttpsRequest(context.request)
|
|
330
|
-
}));
|
|
331
|
-
}
|
|
332
|
-
__name(issueSessionCookie, "issueSessionCookie");
|
|
333
|
-
function clearSessionCookie(context) {
|
|
334
|
-
appendSetCookie(context.response, serializeSetCookie(SESSION_COOKIE_NAME, "", {
|
|
335
|
-
path: context.cookiePath,
|
|
336
|
-
maxAgeSeconds: 0,
|
|
337
|
-
secure: isHttpsRequest(context.request),
|
|
338
|
-
clear: true
|
|
339
|
-
}));
|
|
340
|
-
}
|
|
341
|
-
__name(clearSessionCookie, "clearSessionCookie");
|
|
342
|
-
|
|
343
|
-
// src/server/tokens.ts
|
|
344
|
-
var MEDIA_STORE = /* @__PURE__ */ Symbol.for("nestjs-media:store");
|
|
345
|
-
var MEDIA_UPLOAD_SESSIONS = /* @__PURE__ */ Symbol.for("nestjs-media:upload-sessions");
|
|
346
|
-
var MEDIA_STORAGE_SHARED = /* @__PURE__ */ Symbol.for("nestjs-media:storage");
|
|
347
|
-
var MEDIA_DASHBOARD_BASE_PATH = /* @__PURE__ */ Symbol("MEDIA_DASHBOARD_BASE_PATH");
|
|
348
|
-
var MEDIA_DASHBOARD_API_PATH = /* @__PURE__ */ Symbol("MEDIA_DASHBOARD_API_PATH");
|
|
349
|
-
var MEDIA_DASHBOARD_ACTIONS = /* @__PURE__ */ Symbol("MEDIA_DASHBOARD_ACTIONS");
|
|
350
|
-
var MEDIA_CONSOLE_AUTH = /* @__PURE__ */ Symbol("MEDIA_CONSOLE_AUTH");
|
|
351
|
-
var MEDIA_CONSOLE_COOKIE_PATH = /* @__PURE__ */ Symbol("MEDIA_CONSOLE_COOKIE_PATH");
|
|
352
|
-
var MEDIA_CONSOLE_OBJECT_INSIGHTS = /* @__PURE__ */ Symbol("MEDIA_CONSOLE_OBJECT_INSIGHTS");
|
|
353
|
-
var MEDIA_CONSOLE_OBJECT_URLS = /* @__PURE__ */ Symbol("MEDIA_CONSOLE_OBJECT_URLS");
|
|
354
|
-
|
|
355
|
-
// src/server/media-console.guard.ts
|
|
62
|
+
var import_common = require("@nestjs/common");
|
|
63
|
+
var import_core = require("@nestjs/core");
|
|
356
64
|
function _ts_decorate(decorators, target, key, desc) {
|
|
357
65
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
358
66
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -379,94 +87,339 @@ function _ts_param(paramIndex, decorator) {
|
|
|
379
87
|
};
|
|
380
88
|
}
|
|
381
89
|
__name(_ts_param, "_ts_param");
|
|
382
|
-
var
|
|
383
|
-
|
|
384
|
-
|
|
90
|
+
var CONSOLE_GATE = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:console-gate");
|
|
91
|
+
var PACKAGE = "@dudousxd/nestjs-media-dashboard";
|
|
92
|
+
var MODULE = "MediaDashboardModule";
|
|
93
|
+
var CONSOLE_AUTHORIZE_MISSING_WARNING = consoleAuthorizeMissingWarning(`${MODULE}.forRoot()`);
|
|
94
|
+
function consoleAuthorizeMissingWarning(registration) {
|
|
95
|
+
return `[${PACKAGE}] console has no \`authorize\` configured: it is open outside production and denies every request in production. Set \`authorize\` in ${registration}.`;
|
|
96
|
+
}
|
|
97
|
+
__name(consoleAuthorizeMissingWarning, "consoleAuthorizeMissingWarning");
|
|
98
|
+
var allow = /* @__PURE__ */ __name(() => ({
|
|
99
|
+
allowed: true
|
|
100
|
+
}), "allow");
|
|
101
|
+
var forbidden = /* @__PURE__ */ __name(() => ({
|
|
102
|
+
allowed: false,
|
|
103
|
+
reason: "forbidden"
|
|
104
|
+
}), "forbidden");
|
|
105
|
+
var unauthenticated = /* @__PURE__ */ __name(() => ({
|
|
106
|
+
allowed: false,
|
|
107
|
+
reason: "unauthenticated"
|
|
108
|
+
}), "unauthenticated");
|
|
109
|
+
function isProduction() {
|
|
110
|
+
return (process.env.NODE_ENV ?? "").trim().toLowerCase() === "production";
|
|
111
|
+
}
|
|
112
|
+
__name(isProduction, "isProduction");
|
|
113
|
+
function isAuthorizerClass(value) {
|
|
114
|
+
if (typeof value !== "function") return false;
|
|
115
|
+
if (typeof value.prototype?.authorize === "function") {
|
|
116
|
+
return true;
|
|
385
117
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
118
|
+
return /^class[\s{]/.test(Function.prototype.toString.call(value));
|
|
119
|
+
}
|
|
120
|
+
__name(isAuthorizerClass, "isAuthorizerClass");
|
|
121
|
+
function isPolicyCheck(value) {
|
|
122
|
+
return typeof value === "object" && value !== null && typeof value.policy === "function" && typeof value.ability === "string";
|
|
123
|
+
}
|
|
124
|
+
__name(isPolicyCheck, "isPolicyCheck");
|
|
125
|
+
function isLazyModule(value) {
|
|
126
|
+
return typeof value === "object" && value !== null && isAuthorizerClass(value.default);
|
|
127
|
+
}
|
|
128
|
+
__name(isLazyModule, "isLazyModule");
|
|
129
|
+
function normalizeConsoleResult(result) {
|
|
130
|
+
if (result === true) return allow();
|
|
131
|
+
if (typeof result !== "object" || result === null) return forbidden();
|
|
132
|
+
const shape = result;
|
|
133
|
+
if (typeof shape.redirectTo === "string" && shape.allowed !== true) {
|
|
134
|
+
return {
|
|
135
|
+
allowed: false,
|
|
136
|
+
reason: "unauthenticated",
|
|
137
|
+
redirectTo: shape.redirectTo
|
|
138
|
+
};
|
|
391
139
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
140
|
+
if (shape.allowed === true) return allow();
|
|
141
|
+
return shape.reason === "unauthenticated" ? unauthenticated() : forbidden();
|
|
142
|
+
}
|
|
143
|
+
__name(normalizeConsoleResult, "normalizeConsoleResult");
|
|
144
|
+
function hasUser(user) {
|
|
145
|
+
return user !== null && user !== void 0 && user !== false;
|
|
146
|
+
}
|
|
147
|
+
__name(hasUser, "hasUser");
|
|
148
|
+
var importOptional = /* @__PURE__ */ __name((specifier) => import(
|
|
149
|
+
/* @vite-ignore */
|
|
150
|
+
specifier
|
|
151
|
+
), "importOptional");
|
|
152
|
+
async function policyDecider(check, moduleRef) {
|
|
153
|
+
let pkg;
|
|
154
|
+
try {
|
|
155
|
+
pkg = await importOptional("@nestjs/authorization");
|
|
156
|
+
} catch {
|
|
157
|
+
throw new Error("`authorize: { policy, ability }` needs @nestjs/authorization installed (npm i @nestjs/authorization); or pass a function, a class, or a gate name");
|
|
403
158
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
return verifySessionCookie(cookieValue, {
|
|
409
|
-
secret: this.auth.secret
|
|
159
|
+
let service;
|
|
160
|
+
try {
|
|
161
|
+
service = moduleRef.get(pkg.AuthorizationService, {
|
|
162
|
+
strict: false
|
|
410
163
|
});
|
|
164
|
+
} catch {
|
|
165
|
+
service = void 0;
|
|
411
166
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (
|
|
430
|
-
|
|
167
|
+
if (!service) {
|
|
168
|
+
throw new Error("`authorize: { policy, ability }` needs AuthorizationModule.forRoot() from @nestjs/authorization imported in the app");
|
|
169
|
+
}
|
|
170
|
+
const found = service;
|
|
171
|
+
const defaultGetUser = pkg.defaultGetUser;
|
|
172
|
+
let getUser = defaultGetUser;
|
|
173
|
+
try {
|
|
174
|
+
const options = moduleRef.get(pkg.AUTHORIZATION_MODULE_OPTIONS, {
|
|
175
|
+
strict: false
|
|
176
|
+
});
|
|
177
|
+
if (typeof options?.getUser === "function") getUser = options.getUser;
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
const { policy, ability } = check;
|
|
181
|
+
return async (context) => {
|
|
182
|
+
const user = await getUser(context);
|
|
183
|
+
const signedIn = hasUser(user);
|
|
184
|
+
if (await found.can(policy, ability, signedIn ? user : null)) return allow();
|
|
185
|
+
return signedIn ? forbidden() : unauthenticated();
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
__name(policyDecider, "policyDecider");
|
|
189
|
+
function gateDecider(name, moduleRef) {
|
|
190
|
+
let gate;
|
|
191
|
+
try {
|
|
192
|
+
gate = moduleRef.get(CONSOLE_GATE, {
|
|
193
|
+
strict: false
|
|
194
|
+
});
|
|
195
|
+
} catch {
|
|
196
|
+
gate = void 0;
|
|
197
|
+
}
|
|
198
|
+
if (!gate) {
|
|
199
|
+
throw new Error(`\`authorize: '${name}'\` needs @dudousxd/nestjs-authz's AuthzModule (it resolves gate names); or pass a function, a class, or { policy, ability }`);
|
|
200
|
+
}
|
|
201
|
+
const found = gate;
|
|
202
|
+
return async (context) => normalizeConsoleResult(await found.allows(name, context));
|
|
203
|
+
}
|
|
204
|
+
__name(gateDecider, "gateDecider");
|
|
205
|
+
function classDecider(moduleRef) {
|
|
206
|
+
const instances = /* @__PURE__ */ new Map();
|
|
207
|
+
const instanceOf = /* @__PURE__ */ __name((Class) => {
|
|
208
|
+
const cached = instances.get(Class);
|
|
209
|
+
if (cached) return cached;
|
|
210
|
+
const pending = (async () => {
|
|
431
211
|
try {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
allowed = false;
|
|
435
|
-
}
|
|
436
|
-
if (!allowed) {
|
|
437
|
-
clearSessionCookie({
|
|
438
|
-
cookiePath: this.cookiePath ?? "/",
|
|
439
|
-
request,
|
|
440
|
-
response
|
|
212
|
+
const registered = moduleRef.get(Class, {
|
|
213
|
+
strict: false
|
|
441
214
|
});
|
|
442
|
-
return
|
|
215
|
+
if (registered) return registered;
|
|
216
|
+
} catch {
|
|
443
217
|
}
|
|
218
|
+
return moduleRef.create(Class);
|
|
219
|
+
})();
|
|
220
|
+
pending.catch(() => instances.delete(Class));
|
|
221
|
+
instances.set(Class, pending);
|
|
222
|
+
return pending;
|
|
223
|
+
}, "instanceOf");
|
|
224
|
+
return (Class) => async (context) => normalizeConsoleResult(await (await instanceOf(Class)).authorize(context));
|
|
225
|
+
}
|
|
226
|
+
__name(classDecider, "classDecider");
|
|
227
|
+
async function resolveConsoleAuthorize(authorize, moduleRef, options = {}) {
|
|
228
|
+
const logger = options.logger ?? new import_common.Logger(MODULE);
|
|
229
|
+
if (authorize === void 0) {
|
|
230
|
+
if (options.warnIfMissing !== false) {
|
|
231
|
+
logger.warn(options.registration ? consoleAuthorizeMissingWarning(options.registration) : CONSOLE_AUTHORIZE_MISSING_WARNING);
|
|
444
232
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
233
|
+
const production = isProduction();
|
|
234
|
+
return async () => production ? forbidden() : allow();
|
|
235
|
+
}
|
|
236
|
+
let decide;
|
|
237
|
+
if (typeof authorize === "string") {
|
|
238
|
+
decide = gateDecider(authorize, moduleRef);
|
|
239
|
+
} else if (isPolicyCheck(authorize)) {
|
|
240
|
+
decide = await policyDecider(authorize, moduleRef);
|
|
241
|
+
} else if (isAuthorizerClass(authorize)) {
|
|
242
|
+
decide = classDecider(moduleRef)(authorize);
|
|
243
|
+
} else if (typeof authorize === "function") {
|
|
244
|
+
const fromClass = classDecider(moduleRef);
|
|
245
|
+
let lazy;
|
|
246
|
+
const fn = authorize;
|
|
247
|
+
decide = /* @__PURE__ */ __name(async (context) => {
|
|
248
|
+
if (lazy) return lazy(context);
|
|
249
|
+
const result = await fn(context);
|
|
250
|
+
if (!isLazyModule(result)) return normalizeConsoleResult(result);
|
|
251
|
+
lazy = fromClass(result.default);
|
|
252
|
+
return lazy(context);
|
|
253
|
+
}, "decide");
|
|
254
|
+
} else {
|
|
255
|
+
throw new Error(`${MODULE}: \`authorize\` must be a function, a class with authorize(), a lazy import, { policy, ability } or a gate name.`);
|
|
256
|
+
}
|
|
257
|
+
return async (context) => {
|
|
258
|
+
try {
|
|
259
|
+
return await decide(context);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
logger.warn(`console \`authorize\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`);
|
|
262
|
+
return forbidden();
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
__name(resolveConsoleAuthorize, "resolveConsoleAuthorize");
|
|
267
|
+
var sha256 = /* @__PURE__ */ __name((value) => (0, import_node_crypto.createHash)("sha256").update(value).digest(), "sha256");
|
|
268
|
+
function authorizeByToken(options) {
|
|
269
|
+
const token = options.token;
|
|
270
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
271
|
+
throw new Error("authorizeByToken: `token` is empty; set it (e.g. from an env var).");
|
|
272
|
+
}
|
|
273
|
+
const expected = sha256(token);
|
|
274
|
+
return (context) => {
|
|
275
|
+
const header = context.switchToHttp().getRequest()?.headers?.authorization;
|
|
276
|
+
const match = typeof header === "string" ? /^Bearer\s+(.+)$/i.exec(header.trim()) : null;
|
|
277
|
+
if (match?.[1] && (0, import_node_crypto.timingSafeEqual)(sha256(match[1]), expected)) return true;
|
|
278
|
+
return {
|
|
279
|
+
allowed: false,
|
|
280
|
+
reason: "unauthenticated"
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
__name(authorizeByToken, "authorizeByToken");
|
|
285
|
+
var CONSOLE_AUTHORIZE_DECIDE = /* @__PURE__ */ Symbol(`${PACKAGE}:console-authorize`);
|
|
286
|
+
var CONSOLE_PAGE_METADATA = `${PACKAGE}:console-page`;
|
|
287
|
+
var ConsolePage = /* @__PURE__ */ __name(() => (target) => {
|
|
288
|
+
Reflect.defineMetadata(CONSOLE_PAGE_METADATA, true, target);
|
|
289
|
+
}, "ConsolePage");
|
|
290
|
+
var ConsoleAccessDenied = class extends import_common.HttpException {
|
|
291
|
+
static {
|
|
292
|
+
__name(this, "ConsoleAccessDenied");
|
|
293
|
+
}
|
|
294
|
+
decision;
|
|
295
|
+
page;
|
|
296
|
+
constructor(decision, page2 = false) {
|
|
297
|
+
const isUnauthenticated = decision.reason === "unauthenticated";
|
|
298
|
+
super(isUnauthenticated ? {
|
|
299
|
+
error: "unauthenticated",
|
|
300
|
+
...decision.redirectTo ? {
|
|
301
|
+
redirectTo: decision.redirectTo
|
|
302
|
+
} : {}
|
|
303
|
+
} : {
|
|
304
|
+
error: "forbidden"
|
|
305
|
+
}, isUnauthenticated ? 401 : 403), this.decision = decision, this.page = page2;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
var ConsoleAuthorizeGuard = class {
|
|
309
|
+
static {
|
|
310
|
+
__name(this, "ConsoleAuthorizeGuard");
|
|
311
|
+
}
|
|
312
|
+
decide;
|
|
313
|
+
constructor(decide) {
|
|
314
|
+
this.decide = decide;
|
|
315
|
+
}
|
|
316
|
+
async canActivate(context) {
|
|
317
|
+
const decision = await this.decide(context);
|
|
318
|
+
if (decision.allowed) return true;
|
|
319
|
+
throw new ConsoleAccessDenied(decision, isPageNavigation(context));
|
|
453
320
|
}
|
|
454
321
|
};
|
|
455
|
-
|
|
322
|
+
ConsoleAuthorizeGuard = _ts_decorate([
|
|
456
323
|
(0, import_common.Injectable)(),
|
|
457
|
-
_ts_param(0, (0, import_common.
|
|
458
|
-
_ts_param(0, (0, import_common.Inject)(MEDIA_CONSOLE_AUTH)),
|
|
459
|
-
_ts_param(1, (0, import_common.Optional)()),
|
|
460
|
-
_ts_param(1, (0, import_common.Inject)(MEDIA_CONSOLE_COOKIE_PATH)),
|
|
324
|
+
_ts_param(0, (0, import_common.Inject)(CONSOLE_AUTHORIZE_DECIDE)),
|
|
461
325
|
_ts_metadata("design:type", Function),
|
|
462
326
|
_ts_metadata("design:paramtypes", [
|
|
463
|
-
Object
|
|
464
|
-
Object
|
|
327
|
+
typeof ConsoleDecide === "undefined" ? Object : ConsoleDecide
|
|
465
328
|
])
|
|
466
|
-
],
|
|
329
|
+
], ConsoleAuthorizeGuard);
|
|
330
|
+
function isPageNavigation(context) {
|
|
331
|
+
if (context.getType() !== "http") return false;
|
|
332
|
+
if (Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) !== true) return false;
|
|
333
|
+
const request = context.switchToHttp().getRequest();
|
|
334
|
+
const accept = request?.headers?.accept;
|
|
335
|
+
return request?.method === "GET" && typeof accept === "string" && accept.includes("text/html");
|
|
336
|
+
}
|
|
337
|
+
__name(isPageNavigation, "isPageNavigation");
|
|
338
|
+
var page = /* @__PURE__ */ __name((title, message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title></head><body style="font-family:system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0"><main style="text-align:center"><h1>${title}</h1><p>${message}</p></main></body></html>`, "page");
|
|
339
|
+
var DENIED_PAGE = page("Access denied", "You do not have access to this console.");
|
|
340
|
+
var SIGN_IN_PAGE = page("Sign in required", "Sign in to open this console.");
|
|
341
|
+
var ConsoleAuthorizeFilter = class {
|
|
342
|
+
static {
|
|
343
|
+
__name(this, "ConsoleAuthorizeFilter");
|
|
344
|
+
}
|
|
345
|
+
adapterHost;
|
|
346
|
+
constructor(adapterHost) {
|
|
347
|
+
this.adapterHost = adapterHost;
|
|
348
|
+
}
|
|
349
|
+
catch(exception, host) {
|
|
350
|
+
const adapter = this.adapterHost.httpAdapter;
|
|
351
|
+
const response = host.switchToHttp().getResponse();
|
|
352
|
+
const { decision, page: isPage } = exception;
|
|
353
|
+
if (isPage && decision.redirectTo) {
|
|
354
|
+
adapter.redirect(response, 302, decision.redirectTo);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (isPage) {
|
|
358
|
+
const unauthenticated2 = decision.reason === "unauthenticated";
|
|
359
|
+
adapter.setHeader(response, "Content-Type", "text/html; charset=utf-8");
|
|
360
|
+
adapter.reply(response, unauthenticated2 ? SIGN_IN_PAGE : DENIED_PAGE, unauthenticated2 ? 401 : 403);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
adapter.reply(response, exception.getResponse(), exception.getStatus());
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
ConsoleAuthorizeFilter = _ts_decorate([
|
|
367
|
+
(0, import_common.Catch)(ConsoleAccessDenied),
|
|
368
|
+
_ts_param(0, (0, import_common.Inject)(import_core.HttpAdapterHost)),
|
|
369
|
+
_ts_metadata("design:type", Function),
|
|
370
|
+
_ts_metadata("design:paramtypes", [
|
|
371
|
+
typeof import_core.HttpAdapterHost === "undefined" ? Object : import_core.HttpAdapterHost
|
|
372
|
+
])
|
|
373
|
+
], ConsoleAuthorizeFilter);
|
|
374
|
+
var UseConsoleAuthorize = /* @__PURE__ */ __name(() => (0, import_common.applyDecorators)((0, import_common.UseGuards)(ConsoleAuthorizeGuard), (0, import_common.UseFilters)(ConsoleAuthorizeFilter)), "UseConsoleAuthorize");
|
|
375
|
+
function consoleAuthorizeProvider(authorize, registration) {
|
|
376
|
+
return {
|
|
377
|
+
provide: CONSOLE_AUTHORIZE_DECIDE,
|
|
378
|
+
inject: [
|
|
379
|
+
import_core.ModuleRef
|
|
380
|
+
],
|
|
381
|
+
useFactory: /* @__PURE__ */ __name((moduleRef) => resolveConsoleAuthorize(authorize, moduleRef, {
|
|
382
|
+
registration
|
|
383
|
+
}), "useFactory")
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
__name(consoleAuthorizeProvider, "consoleAuthorizeProvider");
|
|
387
|
+
|
|
388
|
+
// src/server/controller-decorators.ts
|
|
389
|
+
var CAN_ANYONE_METADATA = "authorization:can-anyone";
|
|
390
|
+
function OpenToAuthorization() {
|
|
391
|
+
return (target) => {
|
|
392
|
+
Reflect.defineMetadata(CAN_ANYONE_METADATA, true, target);
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
__name(OpenToAuthorization, "OpenToAuthorization");
|
|
396
|
+
function consoleController(controller, decorators) {
|
|
397
|
+
const Decorated = class extends controller {
|
|
398
|
+
static {
|
|
399
|
+
__name(this, "Decorated");
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
Object.defineProperty(Decorated, "name", {
|
|
403
|
+
value: controller.name
|
|
404
|
+
});
|
|
405
|
+
if (decorators && decorators.length > 0) Reflect.decorate(decorators, Decorated);
|
|
406
|
+
Reflect.decorate([
|
|
407
|
+
UseConsoleAuthorize()
|
|
408
|
+
], Decorated);
|
|
409
|
+
return Decorated;
|
|
410
|
+
}
|
|
411
|
+
__name(consoleController, "consoleController");
|
|
412
|
+
|
|
413
|
+
// src/server/media-console-api.module.ts
|
|
414
|
+
var import_common5 = require("@nestjs/common");
|
|
415
|
+
|
|
416
|
+
// src/server/media-console-actions.controller.ts
|
|
417
|
+
var import_common3 = require("@nestjs/common");
|
|
467
418
|
|
|
468
419
|
// src/server/media-console.service.ts
|
|
420
|
+
var import_nestjs_media_core = require("@dudousxd/nestjs-media-core");
|
|
469
421
|
var import_common2 = require("@nestjs/common");
|
|
422
|
+
var import_storage = require("@nestjs/storage");
|
|
470
423
|
|
|
471
424
|
// src/server/object-insights.ts
|
|
472
425
|
var SAFE_LINK_SCHEMES = /* @__PURE__ */ new Set([
|
|
@@ -501,6 +454,16 @@ function objectProxyUrl(apiBasePath, route, disk, key) {
|
|
|
501
454
|
}
|
|
502
455
|
__name(objectProxyUrl, "objectProxyUrl");
|
|
503
456
|
|
|
457
|
+
// src/server/tokens.ts
|
|
458
|
+
var MEDIA_STORE = /* @__PURE__ */ Symbol.for("nestjs-media:store");
|
|
459
|
+
var MEDIA_UPLOAD_SESSIONS = /* @__PURE__ */ Symbol.for("nestjs-media:upload-sessions");
|
|
460
|
+
var MEDIA_MULTIPART = /* @__PURE__ */ Symbol.for("nestjs-media:multipart");
|
|
461
|
+
var MEDIA_DASHBOARD_BASE_PATH = /* @__PURE__ */ Symbol("MEDIA_DASHBOARD_BASE_PATH");
|
|
462
|
+
var MEDIA_DASHBOARD_API_PATH = /* @__PURE__ */ Symbol("MEDIA_DASHBOARD_API_PATH");
|
|
463
|
+
var MEDIA_DASHBOARD_ACTIONS = /* @__PURE__ */ Symbol("MEDIA_DASHBOARD_ACTIONS");
|
|
464
|
+
var MEDIA_CONSOLE_OBJECT_INSIGHTS = /* @__PURE__ */ Symbol("MEDIA_CONSOLE_OBJECT_INSIGHTS");
|
|
465
|
+
var MEDIA_CONSOLE_OBJECT_URLS = /* @__PURE__ */ Symbol("MEDIA_CONSOLE_OBJECT_URLS");
|
|
466
|
+
|
|
504
467
|
// src/server/media-console.service.ts
|
|
505
468
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
506
469
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -530,46 +493,57 @@ function _ts_param2(paramIndex, decorator) {
|
|
|
530
493
|
__name(_ts_param2, "_ts_param");
|
|
531
494
|
var URL_TTL_SECONDS = 300;
|
|
532
495
|
var DEFAULT_PAGE_LIMIT = 50;
|
|
533
|
-
var
|
|
534
|
-
var
|
|
535
|
-
var MAX_CROSS_DISK_BYTES = 100 * 1024 * 1024;
|
|
496
|
+
var SWEEP_LIMIT = 1e3;
|
|
497
|
+
var FOLDER_KEEP_FILE = ".keep";
|
|
536
498
|
function lastSegment(prefix) {
|
|
537
499
|
const trimmed = prefix.replace(/\/+$/, "");
|
|
538
500
|
const slash = trimmed.lastIndexOf("/");
|
|
539
501
|
return slash === -1 ? trimmed : trimmed.slice(slash + 1);
|
|
540
502
|
}
|
|
541
503
|
__name(lastSegment, "lastSegment");
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
504
|
+
function clientFolder(prefix, what) {
|
|
505
|
+
const trimmed = prefix.trim().replace(/^\/+/, "");
|
|
506
|
+
let normalized;
|
|
507
|
+
try {
|
|
508
|
+
normalized = (0, import_nestjs_media_core.folderPrefix)(trimmed);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
throw new import_common2.BadRequestException(`${what}: ${error.message}`);
|
|
545
511
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
512
|
+
if (normalized === "") throw new import_common2.BadRequestException(`${what} is required`);
|
|
513
|
+
return normalized;
|
|
514
|
+
}
|
|
515
|
+
__name(clientFolder, "clientFolder");
|
|
516
|
+
function isAddressable(key) {
|
|
517
|
+
try {
|
|
518
|
+
(0, import_nestjs_media_core.assertMediaKey)(key);
|
|
519
|
+
return true;
|
|
520
|
+
} catch {
|
|
521
|
+
return false;
|
|
553
522
|
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
523
|
+
}
|
|
524
|
+
__name(isAddressable, "isAddressable");
|
|
525
|
+
var CORE_CLIENT_ERRORS = /* @__PURE__ */ new Set([
|
|
526
|
+
"InvalidMediaKeyError",
|
|
527
|
+
"InvalidListingCursorError"
|
|
528
|
+
]);
|
|
529
|
+
function toHttp(error) {
|
|
530
|
+
if (error instanceof import_common2.HttpException) return error;
|
|
531
|
+
if (error instanceof import_storage.StorageFileNotFoundError) return new import_common2.NotFoundException(error.message);
|
|
532
|
+
const status = error?.status;
|
|
533
|
+
if ((error instanceof import_storage.StorageError || CORE_CLIENT_ERRORS.has(error?.name ?? "")) && typeof status === "number") {
|
|
534
|
+
return new import_common2.HttpException(error.message, status);
|
|
535
|
+
}
|
|
536
|
+
return error;
|
|
537
|
+
}
|
|
538
|
+
__name(toHttp, "toHttp");
|
|
539
|
+
async function http(run) {
|
|
540
|
+
try {
|
|
541
|
+
return await run();
|
|
542
|
+
} catch (error) {
|
|
543
|
+
throw toHttp(error);
|
|
562
544
|
}
|
|
563
|
-
const { start } = range;
|
|
564
|
-
if (!Number.isInteger(start) || start < 0 || start >= size) return null;
|
|
565
|
-
const end = range.end === void 0 ? size - 1 : Math.min(range.end, size - 1);
|
|
566
|
-
if (!Number.isInteger(end) || end < start) return null;
|
|
567
|
-
return {
|
|
568
|
-
start,
|
|
569
|
-
end
|
|
570
|
-
};
|
|
571
545
|
}
|
|
572
|
-
__name(
|
|
546
|
+
__name(http, "http");
|
|
573
547
|
function mapUpload(session) {
|
|
574
548
|
const percent = session.size !== void 0 && session.size > 0 ? Math.min(100, Math.round(session.offset / session.size * 100)) : null;
|
|
575
549
|
return {
|
|
@@ -613,108 +587,106 @@ var MediaConsoleService = class _MediaConsoleService {
|
|
|
613
587
|
actionsEnabled;
|
|
614
588
|
insightProviders;
|
|
615
589
|
objectUrls;
|
|
616
|
-
|
|
590
|
+
multipart;
|
|
591
|
+
constructor(storage, store, uploads, actionsEnabled, insightProviders = null, objectUrls = null, multipart = null) {
|
|
617
592
|
this.storage = storage;
|
|
618
593
|
this.store = store;
|
|
619
594
|
this.uploads = uploads;
|
|
620
595
|
this.actionsEnabled = actionsEnabled;
|
|
621
596
|
this.insightProviders = insightProviders;
|
|
622
597
|
this.objectUrls = objectUrls;
|
|
598
|
+
this.multipart = multipart;
|
|
623
599
|
}
|
|
600
|
+
logger = new import_common2.Logger(_MediaConsoleService.name);
|
|
624
601
|
/**
|
|
625
602
|
* The URL to report for an object's bytes, under the host's {@link ObjectUrlStrategy}. `auto`
|
|
626
|
-
* prefers
|
|
627
|
-
*
|
|
603
|
+
* prefers the disk's public URL, then a signed one (S3 presigns; a local disk signs with
|
|
604
|
+
* `signedUrls`), and falls back to this server's own route for a disk that can do neither.
|
|
605
|
+
* `proxy` always routes through this server.
|
|
628
606
|
*/
|
|
629
|
-
objectUrl(
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
607
|
+
async objectUrl(disk, name, key) {
|
|
608
|
+
const proxy = /* @__PURE__ */ __name(() => objectProxyUrl(this.objectUrls?.apiBasePath ?? "", "raw", name, key), "proxy");
|
|
609
|
+
if (this.objectUrls?.strategy === "proxy") return proxy();
|
|
610
|
+
if ((0, import_nestjs_media_core.hasPublicUrl)(disk)) return disk.url(key);
|
|
611
|
+
if (await (0, import_nestjs_media_core.canPresign)(disk)) return disk.signedUrl(key, {
|
|
612
|
+
expiresIn: URL_TTL_SECONDS * 1e3
|
|
613
|
+
});
|
|
614
|
+
return proxy();
|
|
634
615
|
}
|
|
635
|
-
logger = new import_common2.Logger(_MediaConsoleService.name);
|
|
636
616
|
diskOrThrow(disk) {
|
|
637
|
-
if (!this.storage || !this.storage.
|
|
617
|
+
if (!this.storage || !this.storage.names().includes(disk)) {
|
|
638
618
|
throw new import_common2.NotFoundException(`Unknown disk: ${disk}`);
|
|
639
619
|
}
|
|
640
620
|
return this.storage.disk(disk);
|
|
641
621
|
}
|
|
642
|
-
listDisks() {
|
|
622
|
+
async listDisks() {
|
|
643
623
|
const storage = this.storage;
|
|
644
624
|
if (!storage) return {
|
|
645
625
|
disks: []
|
|
646
626
|
};
|
|
647
|
-
const defaultDisk = storage.defaultDisk;
|
|
648
627
|
return {
|
|
649
|
-
disks: storage.
|
|
628
|
+
disks: await Promise.all(storage.names().map(async (name) => ({
|
|
650
629
|
name,
|
|
651
|
-
default: name === defaultDisk,
|
|
652
|
-
capabilities: storage.disk(name).
|
|
653
|
-
}))
|
|
630
|
+
default: name === storage.defaultDisk,
|
|
631
|
+
capabilities: await (0, import_nestjs_media_core.diskCapabilities)(storage.disk(name), (0, import_nestjs_media_core.multipartFor)(this.multipart ?? void 0, name))
|
|
632
|
+
})))
|
|
654
633
|
};
|
|
655
634
|
}
|
|
635
|
+
/**
|
|
636
|
+
* One folder level. `StorageDisk.list()` is flat; a disk whose multipart side-car lists with a
|
|
637
|
+
* delimiter (S3) asks the store for folders, any other disk folds its flat listing.
|
|
638
|
+
*/
|
|
656
639
|
async listObjects(disk, options) {
|
|
657
|
-
const
|
|
658
|
-
const
|
|
659
|
-
|
|
640
|
+
const store = this.diskOrThrow(disk);
|
|
641
|
+
const raw = (options.prefix ?? "").replace(/^\/+/, "");
|
|
642
|
+
let prefix;
|
|
643
|
+
try {
|
|
644
|
+
prefix = (0, import_nestjs_media_core.folderPrefix)(raw);
|
|
645
|
+
} catch (error) {
|
|
646
|
+
throw new import_common2.BadRequestException(error.message);
|
|
647
|
+
}
|
|
648
|
+
const level = await http(() => (0, import_nestjs_media_core.listFolderLevel)(store, prefix, {
|
|
649
|
+
limit: options.limit ?? DEFAULT_PAGE_LIMIT,
|
|
660
650
|
...options.cursor ? {
|
|
661
651
|
cursor: options.cursor
|
|
662
|
-
} : {}
|
|
663
|
-
|
|
664
|
-
});
|
|
652
|
+
} : {}
|
|
653
|
+
}, (0, import_nestjs_media_core.multipartFor)(this.multipart ?? void 0, disk)));
|
|
665
654
|
return {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
// that infinite-loops the folder tree. Filtering it out removes the trap; those leading-slash
|
|
670
|
-
// keys are unreachable from the console anyway (the driver strips the leading slash).
|
|
671
|
-
folders: result.folders.map((prefix) => ({
|
|
672
|
-
name: lastSegment(prefix),
|
|
673
|
-
prefix
|
|
655
|
+
folders: level.folders.map((folder) => ({
|
|
656
|
+
name: lastSegment(folder),
|
|
657
|
+
prefix: folder
|
|
674
658
|
})).filter((folder) => folder.name !== ""),
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
659
|
+
files: level.files.map((entry) => ({
|
|
660
|
+
entry,
|
|
661
|
+
name: entry.key.slice(prefix.length)
|
|
662
|
+
})).filter(({ name }) => name !== "" && name !== FOLDER_KEEP_FILE).map(({ entry, name }) => ({
|
|
678
663
|
key: entry.key,
|
|
679
|
-
name
|
|
680
|
-
sizeBytes: entry.
|
|
681
|
-
lastModified: entry.lastModified
|
|
664
|
+
name,
|
|
665
|
+
sizeBytes: entry.size,
|
|
666
|
+
lastModified: entry.lastModified.toISOString()
|
|
682
667
|
})),
|
|
683
|
-
...
|
|
684
|
-
cursor:
|
|
668
|
+
...level.cursor ? {
|
|
669
|
+
cursor: level.cursor
|
|
685
670
|
} : {}
|
|
686
671
|
};
|
|
687
672
|
}
|
|
688
673
|
async objectDetail(disk, key) {
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
} : {},
|
|
701
|
-
url
|
|
702
|
-
};
|
|
674
|
+
const store = this.diskOrThrow(disk);
|
|
675
|
+
return http(async () => {
|
|
676
|
+
const stat = await store.stat(key);
|
|
677
|
+
return {
|
|
678
|
+
key,
|
|
679
|
+
size: stat.size,
|
|
680
|
+
contentType: stat.contentType,
|
|
681
|
+
lastModified: stat.lastModified.toISOString(),
|
|
682
|
+
url: await this.objectUrl(store, disk, key)
|
|
683
|
+
};
|
|
684
|
+
});
|
|
703
685
|
}
|
|
704
686
|
/**
|
|
705
|
-
* What the HOST knows about this object, from every registered provider.
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
* endpoint is separate from its document listing: `objectDetail` is on the path of every preview
|
|
709
|
-
* and every navigation, and providers reach into host databases. Folding them in would put a host
|
|
710
|
-
* query on reads that only wanted a presigned URL.
|
|
711
|
-
*
|
|
712
|
-
* Failures are contained per provider. A provider that throws is logged and dropped, and the
|
|
713
|
-
* others still render — annotation must never be able to stop an admin opening a file, and one
|
|
714
|
-
* host lookup being down says nothing about the rest.
|
|
715
|
-
*
|
|
716
|
-
* Providers run concurrently: they are independent host lookups, and running them in series would
|
|
717
|
-
* make the panel as slow as their sum for no gain.
|
|
687
|
+
* What the HOST knows about this object, from every registered provider. Its own call rather
|
|
688
|
+
* than a field on `objectDetail`, so host lookups stay off the preview path. A provider that
|
|
689
|
+
* throws is logged and dropped; providers run concurrently.
|
|
718
690
|
*/
|
|
719
691
|
async objectInsights(disk, key) {
|
|
720
692
|
const providers = this.insightProviders ?? [];
|
|
@@ -740,177 +712,132 @@ var MediaConsoleService = class _MediaConsoleService {
|
|
|
740
712
|
};
|
|
741
713
|
}
|
|
742
714
|
/**
|
|
743
|
-
*
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
* With `range`, only that slice — which is what makes previewing a multi-hundred-MB object (a
|
|
748
|
-
* SQLite database read a page at a time) possible at all. The range is resolved HERE, not in the
|
|
749
|
-
* driver, because it has to be checked against the object's size and the `stat` for that is
|
|
750
|
-
* already on this path: the suffix form is made absolute, an overrunning `end` is clamped to the
|
|
751
|
-
* last byte, and anything genuinely unsatisfiable becomes a {@link RangeNotSatisfiableException}
|
|
752
|
-
* (HTTP 416) instead of a surprise empty body.
|
|
753
|
-
*
|
|
754
|
-
* A driver that reports `ranged: false` REFUSES a range request rather than quietly returning the
|
|
755
|
-
* whole object: the caller asked for 64 KB out of 400 MB, and silently answering with all 400 MB
|
|
756
|
-
* — which the caller would then read as if it were the slice — is the exact failure the `ranged`
|
|
757
|
-
* capability exists to make impossible.
|
|
715
|
+
* Streams an object as the response through `@nestjs/storage`'s `serveFile()`: a single `Range`
|
|
716
|
+
* gets a 206 with `Content-Range` (how a SQLite/ZIP/Parquet preview reads a few KB out of a huge
|
|
717
|
+
* object), `If-None-Match` a 304, a range past the end a 416, a missing file a 404. `inline`
|
|
718
|
+
* applies only to types that can't run script in this origin; others download.
|
|
758
719
|
*/
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const stat = await driver.stat(key);
|
|
762
|
-
const contentType = stat.contentType ?? "application/octet-stream";
|
|
763
|
-
if (!range) {
|
|
764
|
-
return {
|
|
765
|
-
stream: await driver.stream(key),
|
|
766
|
-
contentType,
|
|
767
|
-
size: stat.size
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
if (!driver.capabilities.ranged) {
|
|
771
|
-
throw new import_common2.NotImplementedException(`Disk "${disk}" cannot serve byte ranges`);
|
|
772
|
-
}
|
|
773
|
-
const resolved = resolveRange(range, stat.size);
|
|
774
|
-
if (!resolved) throw new RangeNotSatisfiableException(stat.size);
|
|
775
|
-
return {
|
|
776
|
-
stream: await driver.stream(key, resolved),
|
|
777
|
-
contentType,
|
|
778
|
-
size: stat.size,
|
|
779
|
-
range: resolved
|
|
780
|
-
};
|
|
720
|
+
serveObject(disk, key, options) {
|
|
721
|
+
return http(() => (0, import_storage.serveFile)(this.diskOrThrow(disk), key, options));
|
|
781
722
|
}
|
|
782
|
-
/**
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
throw new import_common2.PayloadTooLargeException(`Upload exceeds the ${MAX_UPLOAD_BYTES / (1024 * 1024)} MB console limit.`);
|
|
798
|
-
}
|
|
799
|
-
chunks.push(buffer);
|
|
800
|
-
}
|
|
801
|
-
await driver.put(key, Buffer.concat(chunks), contentType ? {
|
|
802
|
-
contentType
|
|
803
|
-
} : void 0);
|
|
723
|
+
/**
|
|
724
|
+
* Writes an object from a byte stream (a browser upload), streamed onto the disk: S3Disk turns a
|
|
725
|
+
* body of unknown length into a multipart upload itself, so nothing is buffered here. With the
|
|
726
|
+
* request's `Content-Length`, a body that ends short fails and nothing is written.
|
|
727
|
+
*/
|
|
728
|
+
async putObject(disk, key, body, contentType, contentLength) {
|
|
729
|
+
const store = this.diskOrThrow(disk);
|
|
730
|
+
await http(() => store.put(key, body, {
|
|
731
|
+
...contentType ? {
|
|
732
|
+
contentType
|
|
733
|
+
} : {},
|
|
734
|
+
...contentLength !== void 0 ? {
|
|
735
|
+
contentLength
|
|
736
|
+
} : {}
|
|
737
|
+
}));
|
|
804
738
|
}
|
|
805
|
-
/**
|
|
806
|
-
*
|
|
739
|
+
/**
|
|
740
|
+
* Creates a folder: a zero-byte `<prefix>/.keep` file (hidden from listings), since
|
|
741
|
+
* `@nestjs/storage` keys can't end with `/`. Listings show the folder because a key lives in it.
|
|
742
|
+
*/
|
|
807
743
|
async createFolder(disk, prefix) {
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
await this.diskOrThrow(disk).put(`${normalized}/`, Buffer.alloc(0));
|
|
744
|
+
const folder = clientFolder(prefix, "Folder name");
|
|
745
|
+
await http(() => this.diskOrThrow(disk).put(`${folder}${FOLDER_KEEP_FILE}`, ""));
|
|
811
746
|
}
|
|
812
747
|
async deleteObject(disk, key) {
|
|
813
|
-
await this.diskOrThrow(disk).delete(key);
|
|
814
|
-
}
|
|
815
|
-
/**
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
|
|
820
|
-
* end. Actions-gated. */
|
|
748
|
+
await http(() => this.diskOrThrow(disk).delete(key));
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Recursively deletes a folder: every file under `<prefix>/`, nested included (its `.keep`
|
|
752
|
+
* too), a batch of keys per `delete()`. A legacy `prefix/` marker object written by another tool
|
|
753
|
+
* can't be addressed through `@nestjs/storage`; it is skipped.
|
|
754
|
+
*/
|
|
821
755
|
async deleteFolder(disk, prefix) {
|
|
822
|
-
const
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
if (driver.deleteMany) await driver.deleteMany(keys);
|
|
837
|
-
else for (const key of keys) await driver.delete(key);
|
|
838
|
-
cursor = result.cursor;
|
|
839
|
-
} while (cursor);
|
|
840
|
-
await driver.delete(sweepPrefix);
|
|
756
|
+
const store = this.diskOrThrow(disk);
|
|
757
|
+
const folder = clientFolder(prefix, "Folder");
|
|
758
|
+
await http(async () => {
|
|
759
|
+
const keys = [];
|
|
760
|
+
for await (const entry of store.listAll({
|
|
761
|
+
prefix: folder,
|
|
762
|
+
limit: SWEEP_LIMIT
|
|
763
|
+
})) {
|
|
764
|
+
if (isAddressable(entry.key)) keys.push(entry.key);
|
|
765
|
+
}
|
|
766
|
+
for (let start = 0; start < keys.length; start += SWEEP_LIMIT) {
|
|
767
|
+
await store.delete(keys.slice(start, start + SWEEP_LIMIT));
|
|
768
|
+
}
|
|
769
|
+
});
|
|
841
770
|
}
|
|
842
771
|
async copyObject(fromDisk, from, toDisk, to) {
|
|
843
|
-
await this.transferKey("copy", fromDisk, from, toDisk, to);
|
|
772
|
+
await http(() => this.transferKey("copy", fromDisk, from, toDisk, to));
|
|
844
773
|
}
|
|
845
774
|
async moveObject(fromDisk, from, toDisk, to) {
|
|
846
|
-
await this.transferKey("move", fromDisk, from, toDisk, to);
|
|
775
|
+
await http(() => this.transferKey("move", fromDisk, from, toDisk, to));
|
|
847
776
|
}
|
|
848
|
-
/**
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
777
|
+
/**
|
|
778
|
+
* Copies or moves one object. Same disk: the disk's own `copy`/`move` (server-side on S3, a
|
|
779
|
+
* rename on a local disk). Across disks: the source's body streams into the destination's
|
|
780
|
+
* `put()` (no size limit, nothing buffered), keeping its content type, cache control,
|
|
781
|
+
* disposition and metadata; a `move` then deletes the source.
|
|
782
|
+
*/
|
|
852
783
|
async transferKey(op, fromDisk, from, toDisk, to) {
|
|
853
|
-
const
|
|
784
|
+
const source = this.diskOrThrow(fromDisk);
|
|
854
785
|
if (fromDisk === toDisk) {
|
|
855
|
-
if (op === "move") await
|
|
856
|
-
else await
|
|
786
|
+
if (op === "move") await source.move(from, to);
|
|
787
|
+
else await source.copy(from, to);
|
|
857
788
|
return;
|
|
858
789
|
}
|
|
859
|
-
const
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
790
|
+
const target = this.diskOrThrow(toDisk);
|
|
791
|
+
const download = await source.get(from);
|
|
792
|
+
try {
|
|
793
|
+
await target.put(to, download.body, {
|
|
794
|
+
contentType: download.contentType,
|
|
795
|
+
contentLength: download.size,
|
|
796
|
+
...download.cacheControl ? {
|
|
797
|
+
cacheControl: download.cacheControl
|
|
798
|
+
} : {},
|
|
799
|
+
...download.contentDisposition ? {
|
|
800
|
+
contentDisposition: download.contentDisposition
|
|
801
|
+
} : {},
|
|
802
|
+
metadata: download.metadata
|
|
803
|
+
});
|
|
804
|
+
} finally {
|
|
805
|
+
download.body.destroy();
|
|
863
806
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
} : void 0);
|
|
868
|
-
if (op === "move") await fromDriver.delete(from);
|
|
869
|
-
}
|
|
870
|
-
/** Moves a whole "folder" (every object under `<from>/`, nested included) to `<to>/`, on the same
|
|
871
|
-
* disk or across disks, preserving each key's path relative to the source. */
|
|
807
|
+
if (op === "move") await source.delete(from);
|
|
808
|
+
}
|
|
809
|
+
/** Moves a whole folder (every file under `<from>/`, nested included) to `<to>/`. */
|
|
872
810
|
async moveFolder(fromDisk, from, toDisk, to) {
|
|
873
|
-
await this.transferFolder("move", fromDisk, from, toDisk, to);
|
|
811
|
+
await http(() => this.transferFolder("move", fromDisk, from, toDisk, to));
|
|
874
812
|
}
|
|
875
|
-
/** Copies a whole
|
|
876
|
-
* across disks, preserving each key's path relative to the source. */
|
|
813
|
+
/** Copies a whole folder (every file under `<from>/`, nested included) to `<to>/`. */
|
|
877
814
|
async copyFolder(fromDisk, from, toDisk, to) {
|
|
878
|
-
await this.transferFolder("copy", fromDisk, from, toDisk, to);
|
|
815
|
+
await http(() => this.transferFolder("copy", fromDisk, from, toDisk, to));
|
|
879
816
|
}
|
|
880
|
-
/**
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
817
|
+
/**
|
|
818
|
+
* Shared engine for {@link moveFolder}/{@link copyFolder}: a flat sweep of the source, each key
|
|
819
|
+
* relocated via {@link transferKey}, keeping its path relative to the folder (an empty folder's
|
|
820
|
+
* `.keep` goes along). On the SAME disk, a destination inside the source is refused (it would
|
|
821
|
+
* recurse forever).
|
|
822
|
+
*/
|
|
885
823
|
async transferFolder(op, fromDisk, from, toDisk, to) {
|
|
886
|
-
const
|
|
887
|
-
|
|
888
|
-
const
|
|
889
|
-
const
|
|
890
|
-
if (
|
|
891
|
-
if (toNormalized === "") throw new import_common2.BadRequestException("Destination folder is required");
|
|
892
|
-
const fromPrefix = `${fromNormalized}/`;
|
|
893
|
-
const toPrefix = `${toNormalized}/`;
|
|
894
|
-
if (fromDisk === toDisk && (toPrefix === fromPrefix || toPrefix.startsWith(fromPrefix))) {
|
|
824
|
+
const source = this.diskOrThrow(fromDisk);
|
|
825
|
+
this.diskOrThrow(toDisk);
|
|
826
|
+
const fromPrefix = clientFolder(from, "Source folder");
|
|
827
|
+
const toPrefix = clientFolder(to, "Destination folder");
|
|
828
|
+
if (fromDisk === toDisk && toPrefix.startsWith(fromPrefix)) {
|
|
895
829
|
throw new import_common2.BadRequestException(`Cannot ${op} a folder into itself`);
|
|
896
830
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
});
|
|
906
|
-
|
|
907
|
-
const destKey = `${toPrefix}${entry.key.slice(fromPrefix.length)}`;
|
|
908
|
-
await this.transferKey(op, fromDisk, entry.key, toDisk, destKey);
|
|
909
|
-
}
|
|
910
|
-
cursor = result.cursor;
|
|
911
|
-
} while (cursor);
|
|
912
|
-
await toDriver.put(toPrefix, Buffer.alloc(0));
|
|
913
|
-
if (op === "move") await fromDriver.delete(fromPrefix);
|
|
831
|
+
const keys = [];
|
|
832
|
+
for await (const entry of source.listAll({
|
|
833
|
+
prefix: fromPrefix,
|
|
834
|
+
limit: SWEEP_LIMIT
|
|
835
|
+
})) {
|
|
836
|
+
if (isAddressable(entry.key)) keys.push(entry.key);
|
|
837
|
+
}
|
|
838
|
+
for (const key of keys) {
|
|
839
|
+
await this.transferKey(op, fromDisk, key, toDisk, `${toPrefix}${key.slice(fromPrefix.length)}`);
|
|
840
|
+
}
|
|
914
841
|
}
|
|
915
842
|
async listUploads(filter) {
|
|
916
843
|
if (!this.uploads || typeof this.uploads.list !== "function") return {
|
|
@@ -965,7 +892,7 @@ var MediaConsoleService = class _MediaConsoleService {
|
|
|
965
892
|
if (!this.store || typeof this.store.list !== "function") return {
|
|
966
893
|
records: []
|
|
967
894
|
};
|
|
968
|
-
const
|
|
895
|
+
const page2 = await this.store.list({
|
|
969
896
|
...filter.collection ? {
|
|
970
897
|
collection: filter.collection
|
|
971
898
|
} : {},
|
|
@@ -979,9 +906,9 @@ var MediaConsoleService = class _MediaConsoleService {
|
|
|
979
906
|
} : {}
|
|
980
907
|
});
|
|
981
908
|
return {
|
|
982
|
-
records:
|
|
983
|
-
...
|
|
984
|
-
cursor:
|
|
909
|
+
records: page2.records.map(mapRecord),
|
|
910
|
+
...page2.cursor ? {
|
|
911
|
+
cursor: page2.cursor
|
|
985
912
|
} : {}
|
|
986
913
|
};
|
|
987
914
|
}
|
|
@@ -990,8 +917,8 @@ var MediaConsoleService = class _MediaConsoleService {
|
|
|
990
917
|
const record = await this.store.find(id);
|
|
991
918
|
if (!record) throw new import_common2.NotFoundException(`Unknown media record: ${id}`);
|
|
992
919
|
const variants = await Promise.all(Object.entries(record.conversions).map(async ([name, conversion]) => {
|
|
993
|
-
const
|
|
994
|
-
const url =
|
|
920
|
+
const disk = this.storage?.names().includes(conversion.disk) ? this.storage.disk(conversion.disk) : null;
|
|
921
|
+
const url = disk ? await this.objectUrl(disk, conversion.disk, conversion.path).catch(() => "") : "";
|
|
995
922
|
return {
|
|
996
923
|
name,
|
|
997
924
|
url,
|
|
@@ -1012,7 +939,7 @@ var MediaConsoleService = class _MediaConsoleService {
|
|
|
1012
939
|
return {
|
|
1013
940
|
hasStore: this.store !== null && typeof this.store.list === "function",
|
|
1014
941
|
hasUploads: this.uploads !== null && typeof this.uploads.list === "function",
|
|
1015
|
-
disks: this.storage ? this.storage.
|
|
942
|
+
disks: this.storage ? this.storage.names().length : 0,
|
|
1016
943
|
actions: this.actionsEnabled === true
|
|
1017
944
|
};
|
|
1018
945
|
}
|
|
@@ -1020,7 +947,7 @@ var MediaConsoleService = class _MediaConsoleService {
|
|
|
1020
947
|
MediaConsoleService = _ts_decorate2([
|
|
1021
948
|
(0, import_common2.Injectable)(),
|
|
1022
949
|
_ts_param2(0, (0, import_common2.Optional)()),
|
|
1023
|
-
_ts_param2(0, (0, import_common2.Inject)(
|
|
950
|
+
_ts_param2(0, (0, import_common2.Inject)(import_storage.Storage)),
|
|
1024
951
|
_ts_param2(1, (0, import_common2.Optional)()),
|
|
1025
952
|
_ts_param2(1, (0, import_common2.Inject)(MEDIA_STORE)),
|
|
1026
953
|
_ts_param2(2, (0, import_common2.Optional)()),
|
|
@@ -1031,6 +958,8 @@ MediaConsoleService = _ts_decorate2([
|
|
|
1031
958
|
_ts_param2(4, (0, import_common2.Inject)(MEDIA_CONSOLE_OBJECT_INSIGHTS)),
|
|
1032
959
|
_ts_param2(5, (0, import_common2.Optional)()),
|
|
1033
960
|
_ts_param2(5, (0, import_common2.Inject)(MEDIA_CONSOLE_OBJECT_URLS)),
|
|
961
|
+
_ts_param2(6, (0, import_common2.Optional)()),
|
|
962
|
+
_ts_param2(6, (0, import_common2.Inject)(MEDIA_MULTIPART)),
|
|
1034
963
|
_ts_metadata2("design:type", Function),
|
|
1035
964
|
_ts_metadata2("design:paramtypes", [
|
|
1036
965
|
Object,
|
|
@@ -1038,6 +967,7 @@ MediaConsoleService = _ts_decorate2([
|
|
|
1038
967
|
Object,
|
|
1039
968
|
Object,
|
|
1040
969
|
Object,
|
|
970
|
+
Object,
|
|
1041
971
|
Object
|
|
1042
972
|
])
|
|
1043
973
|
], MediaConsoleService);
|
|
@@ -1095,10 +1025,12 @@ var MediaConsoleActionsController = class {
|
|
|
1095
1025
|
/**
|
|
1096
1026
|
* Uploads a file to `key` on the disk. The body is the raw bytes sent as `application/octet-stream`
|
|
1097
1027
|
* so the host's JSON/urlencoded body parsers never consume the stream; the real MIME rides as the
|
|
1098
|
-
* `type` query param and becomes the stored object's Content-Type. `@Req()` is the request stream
|
|
1028
|
+
* `type` query param and becomes the stored object's Content-Type. `@Req()` is the request stream,
|
|
1029
|
+
* streamed onto the disk (no size cap, nothing buffered).
|
|
1099
1030
|
*/
|
|
1100
1031
|
uploadObject(disk, key, request, type) {
|
|
1101
|
-
|
|
1032
|
+
const declared = Number(request.headers?.["content-length"]);
|
|
1033
|
+
return this.service.putObject(disk, key, request, type, Number.isSafeInteger(declared) && declared >= 0 ? declared : void 0);
|
|
1102
1034
|
}
|
|
1103
1035
|
createFolder(disk, body) {
|
|
1104
1036
|
return this.service.createFolder(disk, body.prefix);
|
|
@@ -1184,7 +1116,7 @@ _ts_decorate3([
|
|
|
1184
1116
|
_ts_metadata3("design:paramtypes", [
|
|
1185
1117
|
String,
|
|
1186
1118
|
String,
|
|
1187
|
-
|
|
1119
|
+
Object,
|
|
1188
1120
|
String
|
|
1189
1121
|
]),
|
|
1190
1122
|
_ts_metadata3("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
@@ -1234,7 +1166,7 @@ _ts_decorate3([
|
|
|
1234
1166
|
_ts_metadata3("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1235
1167
|
], MediaConsoleActionsController.prototype, "deleteLibraryRecord", null);
|
|
1236
1168
|
MediaConsoleActionsController = _ts_decorate3([
|
|
1237
|
-
(
|
|
1169
|
+
OpenToAuthorization(),
|
|
1238
1170
|
(0, import_common3.Controller)(),
|
|
1239
1171
|
_ts_param3(0, (0, import_common3.Inject)(MediaConsoleService)),
|
|
1240
1172
|
_ts_metadata3("design:type", Function),
|
|
@@ -1243,229 +1175,19 @@ MediaConsoleActionsController = _ts_decorate3([
|
|
|
1243
1175
|
])
|
|
1244
1176
|
], MediaConsoleActionsController);
|
|
1245
1177
|
|
|
1246
|
-
// src/server/media-console-auth.controller.ts
|
|
1247
|
-
var import_common4 = require("@nestjs/common");
|
|
1248
|
-
function _ts_decorate4(decorators, target, key, desc) {
|
|
1249
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1250
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1251
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
1252
|
-
} else {
|
|
1253
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
1254
|
-
if (d = decorators[i]) {
|
|
1255
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1260
|
-
}
|
|
1261
|
-
__name(_ts_decorate4, "_ts_decorate");
|
|
1262
|
-
function _ts_metadata4(metadataKey, metadataValue) {
|
|
1263
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1264
|
-
return Reflect.metadata(metadataKey, metadataValue);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
__name(_ts_metadata4, "_ts_metadata");
|
|
1268
|
-
function _ts_param4(paramIndex, decorator) {
|
|
1269
|
-
return function(target, key) {
|
|
1270
|
-
decorator(target, key, paramIndex);
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
__name(_ts_param4, "_ts_param");
|
|
1274
|
-
var MediaConsoleAuthController = class _MediaConsoleAuthController {
|
|
1275
|
-
static {
|
|
1276
|
-
__name(this, "MediaConsoleAuthController");
|
|
1277
|
-
}
|
|
1278
|
-
auth;
|
|
1279
|
-
cookiePath;
|
|
1280
|
-
logger = new import_common4.Logger(_MediaConsoleAuthController.name);
|
|
1281
|
-
/** One warn per hook kind, so a flaky hook doesn't spam logs every request. */
|
|
1282
|
-
warnedHooks = /* @__PURE__ */ new Set();
|
|
1283
|
-
constructor(auth, cookiePath) {
|
|
1284
|
-
this.auth = auth;
|
|
1285
|
-
this.cookiePath = cookiePath;
|
|
1286
|
-
}
|
|
1287
|
-
me(request) {
|
|
1288
|
-
if (!this.auth) return {
|
|
1289
|
-
authRequired: false
|
|
1290
|
-
};
|
|
1291
|
-
const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];
|
|
1292
|
-
const session = cookieValue !== void 0 ? verifySessionCookie(cookieValue, {
|
|
1293
|
-
secret: this.auth.secret
|
|
1294
|
-
}) : null;
|
|
1295
|
-
if (!session) throw new import_common4.UnauthorizedException({
|
|
1296
|
-
auth: {
|
|
1297
|
-
modes: this.auth.modes
|
|
1298
|
-
}
|
|
1299
|
-
});
|
|
1300
|
-
return {
|
|
1301
|
-
user: {
|
|
1302
|
-
id: session.sub,
|
|
1303
|
-
...session.name !== void 0 ? {
|
|
1304
|
-
name: session.name
|
|
1305
|
-
} : {},
|
|
1306
|
-
roles: session.roles
|
|
1307
|
-
}
|
|
1308
|
-
};
|
|
1309
|
-
}
|
|
1310
|
-
async login(body, request, response) {
|
|
1311
|
-
const auth = this.requireAuth();
|
|
1312
|
-
if (!auth.login) throw new import_common4.NotFoundException();
|
|
1313
|
-
if (typeof body?.username !== "string" || typeof body?.password !== "string") {
|
|
1314
|
-
throw new import_common4.BadRequestException("Body must include string `username` and `password`.");
|
|
1315
|
-
}
|
|
1316
|
-
const username = body.username;
|
|
1317
|
-
const password = body.password;
|
|
1318
|
-
const user = await this.runHook("login", () => auth.login?.(username, password) ?? null);
|
|
1319
|
-
if (!user) throw new import_common4.UnauthorizedException({
|
|
1320
|
-
message: "Invalid credentials"
|
|
1321
|
-
});
|
|
1322
|
-
return this.mint(user, request, response);
|
|
1323
|
-
}
|
|
1324
|
-
async session(request, response) {
|
|
1325
|
-
const auth = this.requireAuth();
|
|
1326
|
-
if (!auth.session) throw new import_common4.NotFoundException();
|
|
1327
|
-
const user = await this.runHook("session", () => auth.session?.(request) ?? null);
|
|
1328
|
-
if (!user) throw new import_common4.UnauthorizedException();
|
|
1329
|
-
return this.mint(user, request, response);
|
|
1330
|
-
}
|
|
1331
|
-
logout(request, response) {
|
|
1332
|
-
clearSessionCookie({
|
|
1333
|
-
cookiePath: this.cookiePath ?? "/",
|
|
1334
|
-
request,
|
|
1335
|
-
response
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
requireAuth() {
|
|
1339
|
-
if (!this.auth) throw new import_common4.NotFoundException();
|
|
1340
|
-
return this.auth;
|
|
1341
|
-
}
|
|
1342
|
-
mint(user, request, response) {
|
|
1343
|
-
const auth = this.requireAuth();
|
|
1344
|
-
issueSessionCookie(user, {
|
|
1345
|
-
auth,
|
|
1346
|
-
cookiePath: this.cookiePath ?? "/",
|
|
1347
|
-
request,
|
|
1348
|
-
response
|
|
1349
|
-
});
|
|
1350
|
-
return {
|
|
1351
|
-
user: {
|
|
1352
|
-
id: user.id,
|
|
1353
|
-
...user.name !== void 0 ? {
|
|
1354
|
-
name: user.name
|
|
1355
|
-
} : {},
|
|
1356
|
-
roles: user.roles ?? []
|
|
1357
|
-
}
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
/** Run a host hook defensively: a throw is a denial (null), warn-logged once per kind. */
|
|
1361
|
-
async runHook(kind, run) {
|
|
1362
|
-
try {
|
|
1363
|
-
return await run() ?? null;
|
|
1364
|
-
} catch (error) {
|
|
1365
|
-
if (!this.warnedHooks.has(kind)) {
|
|
1366
|
-
this.warnedHooks.add(kind);
|
|
1367
|
-
this.logger.warn(`Console auth ${kind} hook threw; treating as denial. ${String(error)}`);
|
|
1368
|
-
}
|
|
1369
|
-
return null;
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
};
|
|
1373
|
-
_ts_decorate4([
|
|
1374
|
-
(0, import_common4.Get)("me"),
|
|
1375
|
-
_ts_param4(0, (0, import_common4.Req)()),
|
|
1376
|
-
_ts_metadata4("design:type", Function),
|
|
1377
|
-
_ts_metadata4("design:paramtypes", [
|
|
1378
|
-
Object
|
|
1379
|
-
]),
|
|
1380
|
-
_ts_metadata4("design:returntype", typeof MeResponse === "undefined" ? Object : MeResponse)
|
|
1381
|
-
], MediaConsoleAuthController.prototype, "me", null);
|
|
1382
|
-
_ts_decorate4([
|
|
1383
|
-
(0, import_common4.Post)("login"),
|
|
1384
|
-
(0, import_common4.HttpCode)(200),
|
|
1385
|
-
_ts_param4(0, (0, import_common4.Body)()),
|
|
1386
|
-
_ts_param4(1, (0, import_common4.Req)()),
|
|
1387
|
-
_ts_param4(2, (0, import_common4.Res)({
|
|
1388
|
-
passthrough: true
|
|
1389
|
-
})),
|
|
1390
|
-
_ts_metadata4("design:type", Function),
|
|
1391
|
-
_ts_metadata4("design:paramtypes", [
|
|
1392
|
-
typeof LoginBody === "undefined" ? Object : LoginBody,
|
|
1393
|
-
Object,
|
|
1394
|
-
Object
|
|
1395
|
-
]),
|
|
1396
|
-
_ts_metadata4("design:returntype", Promise)
|
|
1397
|
-
], MediaConsoleAuthController.prototype, "login", null);
|
|
1398
|
-
_ts_decorate4([
|
|
1399
|
-
(0, import_common4.Post)("session"),
|
|
1400
|
-
(0, import_common4.HttpCode)(200),
|
|
1401
|
-
_ts_param4(0, (0, import_common4.Req)()),
|
|
1402
|
-
_ts_param4(1, (0, import_common4.Res)({
|
|
1403
|
-
passthrough: true
|
|
1404
|
-
})),
|
|
1405
|
-
_ts_metadata4("design:type", Function),
|
|
1406
|
-
_ts_metadata4("design:paramtypes", [
|
|
1407
|
-
Object,
|
|
1408
|
-
Object
|
|
1409
|
-
]),
|
|
1410
|
-
_ts_metadata4("design:returntype", Promise)
|
|
1411
|
-
], MediaConsoleAuthController.prototype, "session", null);
|
|
1412
|
-
_ts_decorate4([
|
|
1413
|
-
(0, import_common4.Post)("logout"),
|
|
1414
|
-
(0, import_common4.HttpCode)(204),
|
|
1415
|
-
_ts_param4(0, (0, import_common4.Req)()),
|
|
1416
|
-
_ts_param4(1, (0, import_common4.Res)({
|
|
1417
|
-
passthrough: true
|
|
1418
|
-
})),
|
|
1419
|
-
_ts_metadata4("design:type", Function),
|
|
1420
|
-
_ts_metadata4("design:paramtypes", [
|
|
1421
|
-
Object,
|
|
1422
|
-
Object
|
|
1423
|
-
]),
|
|
1424
|
-
_ts_metadata4("design:returntype", void 0)
|
|
1425
|
-
], MediaConsoleAuthController.prototype, "logout", null);
|
|
1426
|
-
MediaConsoleAuthController = _ts_decorate4([
|
|
1427
|
-
(0, import_common4.Controller)(),
|
|
1428
|
-
_ts_param4(0, (0, import_common4.Optional)()),
|
|
1429
|
-
_ts_param4(0, (0, import_common4.Inject)(MEDIA_CONSOLE_AUTH)),
|
|
1430
|
-
_ts_param4(1, (0, import_common4.Optional)()),
|
|
1431
|
-
_ts_param4(1, (0, import_common4.Inject)(MEDIA_CONSOLE_COOKIE_PATH)),
|
|
1432
|
-
_ts_metadata4("design:type", Function),
|
|
1433
|
-
_ts_metadata4("design:paramtypes", [
|
|
1434
|
-
Object,
|
|
1435
|
-
Object
|
|
1436
|
-
])
|
|
1437
|
-
], MediaConsoleAuthController);
|
|
1438
|
-
|
|
1439
1178
|
// src/server/media-console-read.controller.ts
|
|
1440
|
-
var
|
|
1179
|
+
var import_common4 = require("@nestjs/common");
|
|
1441
1180
|
|
|
1442
1181
|
// src/server/content-disposition.ts
|
|
1443
1182
|
var FALLBACK_FILENAME = "download";
|
|
1444
|
-
var ATTR_CHAR = /^[A-Za-z0-9!#$&+\-.^_`|~]$/;
|
|
1445
|
-
function toExtValue(name) {
|
|
1446
|
-
let encoded = "";
|
|
1447
|
-
for (const byte of new TextEncoder().encode(name)) {
|
|
1448
|
-
const char = String.fromCharCode(byte);
|
|
1449
|
-
encoded += ATTR_CHAR.test(char) ? char : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
1450
|
-
}
|
|
1451
|
-
return encoded;
|
|
1452
|
-
}
|
|
1453
|
-
__name(toExtValue, "toExtValue");
|
|
1454
1183
|
function downloadFilename(key) {
|
|
1455
1184
|
const segment = key.split("/").pop() ?? "";
|
|
1456
1185
|
return segment.trim() === "" ? FALLBACK_FILENAME : segment;
|
|
1457
1186
|
}
|
|
1458
1187
|
__name(downloadFilename, "downloadFilename");
|
|
1459
|
-
function attachmentDisposition(filename) {
|
|
1460
|
-
const name = filename.trim() === "" ? FALLBACK_FILENAME : filename;
|
|
1461
|
-
const ascii = name.replace(/[^\x20-\x7E]/g, "").replace(/["\\]/g, "").trim();
|
|
1462
|
-
const quoted = ascii === "" ? FALLBACK_FILENAME : ascii;
|
|
1463
|
-
return `attachment; filename="${quoted}"; filename*=UTF-8''${toExtValue(name)}`;
|
|
1464
|
-
}
|
|
1465
|
-
__name(attachmentDisposition, "attachmentDisposition");
|
|
1466
1188
|
|
|
1467
1189
|
// src/server/media-console-read.controller.ts
|
|
1468
|
-
function
|
|
1190
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
1469
1191
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1470
1192
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1471
1193
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1478,62 +1200,25 @@ function _ts_decorate5(decorators, target, key, desc) {
|
|
|
1478
1200
|
}
|
|
1479
1201
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1480
1202
|
}
|
|
1481
|
-
__name(
|
|
1482
|
-
function
|
|
1203
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
1204
|
+
function _ts_metadata4(metadataKey, metadataValue) {
|
|
1483
1205
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1484
1206
|
return Reflect.metadata(metadataKey, metadataValue);
|
|
1485
1207
|
}
|
|
1486
1208
|
}
|
|
1487
|
-
__name(
|
|
1488
|
-
function
|
|
1209
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
1210
|
+
function _ts_param4(paramIndex, decorator) {
|
|
1489
1211
|
return function(target, key) {
|
|
1490
1212
|
decorator(target, key, paramIndex);
|
|
1491
1213
|
};
|
|
1492
1214
|
}
|
|
1493
|
-
__name(
|
|
1215
|
+
__name(_ts_param4, "_ts_param");
|
|
1494
1216
|
function toLimit(value) {
|
|
1495
1217
|
if (value === void 0) return void 0;
|
|
1496
1218
|
const parsed = Number(value);
|
|
1497
1219
|
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : void 0;
|
|
1498
1220
|
}
|
|
1499
1221
|
__name(toLimit, "toLimit");
|
|
1500
|
-
var SINGLE_BYTE_RANGE = /^bytes=(\d*)-(\d*)$/;
|
|
1501
|
-
function parseRangeHeader(header) {
|
|
1502
|
-
if (header === void 0) return null;
|
|
1503
|
-
const match = SINGLE_BYTE_RANGE.exec(header.trim());
|
|
1504
|
-
if (!match) return null;
|
|
1505
|
-
const startText = match[1] ?? "";
|
|
1506
|
-
const endText = match[2] ?? "";
|
|
1507
|
-
if (startText === "" && endText === "") return null;
|
|
1508
|
-
if (startText === "") return {
|
|
1509
|
-
suffixLength: Number(endText)
|
|
1510
|
-
};
|
|
1511
|
-
const start = Number(startText);
|
|
1512
|
-
return endText === "" ? {
|
|
1513
|
-
start
|
|
1514
|
-
} : {
|
|
1515
|
-
start,
|
|
1516
|
-
end: Number(endText)
|
|
1517
|
-
};
|
|
1518
|
-
}
|
|
1519
|
-
__name(parseRangeHeader, "parseRangeHeader");
|
|
1520
|
-
function setHeader(response, name, value) {
|
|
1521
|
-
if (typeof response.setHeader === "function") {
|
|
1522
|
-
response.setHeader(name, value);
|
|
1523
|
-
return;
|
|
1524
|
-
}
|
|
1525
|
-
if (typeof response.raw?.setHeader === "function") response.raw.setHeader(name, value);
|
|
1526
|
-
}
|
|
1527
|
-
__name(setHeader, "setHeader");
|
|
1528
|
-
function setStatus(response, code) {
|
|
1529
|
-
if (typeof response.status === "function") {
|
|
1530
|
-
response.status(code);
|
|
1531
|
-
return;
|
|
1532
|
-
}
|
|
1533
|
-
if (response.raw) response.raw.statusCode = code;
|
|
1534
|
-
else response.statusCode = code;
|
|
1535
|
-
}
|
|
1536
|
-
__name(setStatus, "setStatus");
|
|
1537
1222
|
var MediaConsoleReadController = class {
|
|
1538
1223
|
static {
|
|
1539
1224
|
__name(this, "MediaConsoleReadController");
|
|
@@ -1568,63 +1253,29 @@ var MediaConsoleReadController = class {
|
|
|
1568
1253
|
return this.service.objectInsights(disk, key);
|
|
1569
1254
|
}
|
|
1570
1255
|
/**
|
|
1571
|
-
* Streams the object's bytes
|
|
1572
|
-
*
|
|
1256
|
+
* Streams the object's bytes from the same origin (`serveFile()`), so the SPA can render previews
|
|
1257
|
+
* past CORS: `inline` for types that can't run script here (images, PDF, audio, video, plain
|
|
1258
|
+
* text), `attachment` for the rest — the text previews read the body with `fetch`, which doesn't
|
|
1259
|
+
* care. A single `Range` gets a 206, which is how SQLite/ZIP/Parquet previews read a few KB out
|
|
1260
|
+
* of a multi-hundred-MB object.
|
|
1573
1261
|
*/
|
|
1574
|
-
objectRaw(disk, key,
|
|
1575
|
-
return this.
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
*
|
|
1581
|
-
* Its own route rather than a flag on `/raw` because `/raw` is load-bearing for every preview —
|
|
1582
|
-
* they depend on `inline`, and a disposition that varied by query param would be one wrong link
|
|
1583
|
-
* away from a PDF preview turning into a download.
|
|
1584
|
-
*
|
|
1585
|
-
* Same-origin and unconditional, deliberately: this is the console's own action, so it must work
|
|
1586
|
-
* on a host whose browser cannot reach the object store at all (no route to S3, no CORS grant).
|
|
1587
|
-
* That is exactly where `objectDetail`'s presigned `url` — a link to share, not a way to fetch —
|
|
1588
|
-
* is useless. See the `objectUrls` option.
|
|
1589
|
-
*/
|
|
1590
|
-
objectDownload(disk, key, rangeHeader, response) {
|
|
1591
|
-
return this.streamObject(disk, key, rangeHeader, response, attachmentDisposition(downloadFilename(key ?? "")));
|
|
1262
|
+
objectRaw(disk, key, req, res) {
|
|
1263
|
+
return this.service.serveObject(disk, key, {
|
|
1264
|
+
req,
|
|
1265
|
+
res,
|
|
1266
|
+
disposition: "inline"
|
|
1267
|
+
});
|
|
1592
1268
|
}
|
|
1593
1269
|
/**
|
|
1594
|
-
*
|
|
1595
|
-
*
|
|
1596
|
-
* ZIP's central directory) instead of the whole file, and what lets a browser resume an
|
|
1597
|
-
* interrupted download of one. With no `Range` the response is a plain 200 full body, which the
|
|
1598
|
-
* PDF/text/spreadsheet previews depend on.
|
|
1270
|
+
* The same bytes as `/raw`, but as a SAVE: `Content-Disposition: attachment` with the object's
|
|
1271
|
+
* own name. Same-origin and unconditional, so it works where the browser can't reach the store.
|
|
1599
1272
|
*/
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
} catch (error) {
|
|
1607
|
-
if (error instanceof RangeNotSatisfiableException) {
|
|
1608
|
-
setHeader(response, "Content-Range", `bytes */${error.size}`);
|
|
1609
|
-
}
|
|
1610
|
-
throw error;
|
|
1611
|
-
}
|
|
1612
|
-
const { stream, contentType, size, range } = result;
|
|
1613
|
-
if (!range) {
|
|
1614
|
-
return new import_common5.StreamableFile(stream, {
|
|
1615
|
-
type: contentType,
|
|
1616
|
-
disposition,
|
|
1617
|
-
...Number.isFinite(size) ? {
|
|
1618
|
-
length: size
|
|
1619
|
-
} : {}
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
setStatus(response, 206);
|
|
1623
|
-
setHeader(response, "Content-Range", `bytes ${range.start}-${range.end}/${size}`);
|
|
1624
|
-
return new import_common5.StreamableFile(stream, {
|
|
1625
|
-
type: contentType,
|
|
1626
|
-
disposition,
|
|
1627
|
-
length: range.end - range.start + 1
|
|
1273
|
+
objectDownload(disk, key, req, res) {
|
|
1274
|
+
return this.service.serveObject(disk, key, {
|
|
1275
|
+
req,
|
|
1276
|
+
res,
|
|
1277
|
+
disposition: "attachment",
|
|
1278
|
+
filename: downloadFilename(key ?? "")
|
|
1628
1279
|
});
|
|
1629
1280
|
}
|
|
1630
1281
|
uploads(disk, prefix) {
|
|
@@ -1667,151 +1318,151 @@ var MediaConsoleReadController = class {
|
|
|
1667
1318
|
return this.service.topology();
|
|
1668
1319
|
}
|
|
1669
1320
|
};
|
|
1670
|
-
|
|
1671
|
-
(0,
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1321
|
+
_ts_decorate4([
|
|
1322
|
+
(0, import_common4.Get)("disks"),
|
|
1323
|
+
_ts_metadata4("design:type", Function),
|
|
1324
|
+
_ts_metadata4("design:paramtypes", []),
|
|
1325
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1675
1326
|
], MediaConsoleReadController.prototype, "disks", null);
|
|
1676
|
-
|
|
1677
|
-
(0,
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1327
|
+
_ts_decorate4([
|
|
1328
|
+
(0, import_common4.Get)("disks/:disk/objects"),
|
|
1329
|
+
_ts_param4(0, (0, import_common4.Param)("disk")),
|
|
1330
|
+
_ts_param4(1, (0, import_common4.Query)("prefix")),
|
|
1331
|
+
_ts_param4(2, (0, import_common4.Query)("cursor")),
|
|
1332
|
+
_ts_param4(3, (0, import_common4.Query)("limit")),
|
|
1333
|
+
_ts_metadata4("design:type", Function),
|
|
1334
|
+
_ts_metadata4("design:paramtypes", [
|
|
1684
1335
|
String,
|
|
1685
1336
|
String,
|
|
1686
1337
|
String,
|
|
1687
1338
|
String
|
|
1688
1339
|
]),
|
|
1689
|
-
|
|
1340
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1690
1341
|
], MediaConsoleReadController.prototype, "objects", null);
|
|
1691
|
-
|
|
1692
|
-
(0,
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1342
|
+
_ts_decorate4([
|
|
1343
|
+
(0, import_common4.Get)("disks/:disk/object"),
|
|
1344
|
+
_ts_param4(0, (0, import_common4.Param)("disk")),
|
|
1345
|
+
_ts_param4(1, (0, import_common4.Query)("key")),
|
|
1346
|
+
_ts_metadata4("design:type", Function),
|
|
1347
|
+
_ts_metadata4("design:paramtypes", [
|
|
1697
1348
|
String,
|
|
1698
1349
|
String
|
|
1699
1350
|
]),
|
|
1700
|
-
|
|
1351
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1701
1352
|
], MediaConsoleReadController.prototype, "object", null);
|
|
1702
|
-
|
|
1703
|
-
(0,
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1353
|
+
_ts_decorate4([
|
|
1354
|
+
(0, import_common4.Get)("disks/:disk/object/insights"),
|
|
1355
|
+
_ts_param4(0, (0, import_common4.Param)("disk")),
|
|
1356
|
+
_ts_param4(1, (0, import_common4.Query)("key")),
|
|
1357
|
+
_ts_metadata4("design:type", Function),
|
|
1358
|
+
_ts_metadata4("design:paramtypes", [
|
|
1708
1359
|
String,
|
|
1709
1360
|
String
|
|
1710
1361
|
]),
|
|
1711
|
-
|
|
1362
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1712
1363
|
], MediaConsoleReadController.prototype, "objectInsights", null);
|
|
1713
|
-
|
|
1714
|
-
(0,
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1364
|
+
_ts_decorate4([
|
|
1365
|
+
(0, import_common4.Get)("disks/:disk/object/raw"),
|
|
1366
|
+
_ts_param4(0, (0, import_common4.Param)("disk")),
|
|
1367
|
+
_ts_param4(1, (0, import_common4.Query)("key")),
|
|
1368
|
+
_ts_param4(2, (0, import_common4.Req)()),
|
|
1369
|
+
_ts_param4(3, (0, import_common4.Res)({
|
|
1719
1370
|
passthrough: true
|
|
1720
1371
|
})),
|
|
1721
|
-
|
|
1722
|
-
|
|
1372
|
+
_ts_metadata4("design:type", Function),
|
|
1373
|
+
_ts_metadata4("design:paramtypes", [
|
|
1723
1374
|
String,
|
|
1724
1375
|
String,
|
|
1725
1376
|
Object,
|
|
1726
|
-
|
|
1377
|
+
Object
|
|
1727
1378
|
]),
|
|
1728
|
-
|
|
1379
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1729
1380
|
], MediaConsoleReadController.prototype, "objectRaw", null);
|
|
1730
|
-
|
|
1731
|
-
(0,
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1381
|
+
_ts_decorate4([
|
|
1382
|
+
(0, import_common4.Get)("disks/:disk/object/download"),
|
|
1383
|
+
_ts_param4(0, (0, import_common4.Param)("disk")),
|
|
1384
|
+
_ts_param4(1, (0, import_common4.Query)("key")),
|
|
1385
|
+
_ts_param4(2, (0, import_common4.Req)()),
|
|
1386
|
+
_ts_param4(3, (0, import_common4.Res)({
|
|
1736
1387
|
passthrough: true
|
|
1737
1388
|
})),
|
|
1738
|
-
|
|
1739
|
-
|
|
1389
|
+
_ts_metadata4("design:type", Function),
|
|
1390
|
+
_ts_metadata4("design:paramtypes", [
|
|
1740
1391
|
String,
|
|
1741
1392
|
String,
|
|
1742
1393
|
Object,
|
|
1743
|
-
|
|
1394
|
+
Object
|
|
1744
1395
|
]),
|
|
1745
|
-
|
|
1396
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1746
1397
|
], MediaConsoleReadController.prototype, "objectDownload", null);
|
|
1747
|
-
|
|
1748
|
-
(0,
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1398
|
+
_ts_decorate4([
|
|
1399
|
+
(0, import_common4.Get)("uploads"),
|
|
1400
|
+
_ts_param4(0, (0, import_common4.Query)("disk")),
|
|
1401
|
+
_ts_param4(1, (0, import_common4.Query)("prefix")),
|
|
1402
|
+
_ts_metadata4("design:type", Function),
|
|
1403
|
+
_ts_metadata4("design:paramtypes", [
|
|
1753
1404
|
String,
|
|
1754
1405
|
String
|
|
1755
1406
|
]),
|
|
1756
|
-
|
|
1407
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1757
1408
|
], MediaConsoleReadController.prototype, "uploads", null);
|
|
1758
|
-
|
|
1759
|
-
(0,
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1409
|
+
_ts_decorate4([
|
|
1410
|
+
(0, import_common4.Get)("uploads/:id"),
|
|
1411
|
+
_ts_param4(0, (0, import_common4.Param)("id")),
|
|
1412
|
+
_ts_metadata4("design:type", Function),
|
|
1413
|
+
_ts_metadata4("design:paramtypes", [
|
|
1763
1414
|
String
|
|
1764
1415
|
]),
|
|
1765
|
-
|
|
1416
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1766
1417
|
], MediaConsoleReadController.prototype, "upload", null);
|
|
1767
|
-
|
|
1768
|
-
(0,
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1418
|
+
_ts_decorate4([
|
|
1419
|
+
(0, import_common4.Get)("library/collections"),
|
|
1420
|
+
_ts_metadata4("design:type", Function),
|
|
1421
|
+
_ts_metadata4("design:paramtypes", []),
|
|
1422
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1772
1423
|
], MediaConsoleReadController.prototype, "collections", null);
|
|
1773
|
-
|
|
1774
|
-
(0,
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1424
|
+
_ts_decorate4([
|
|
1425
|
+
(0, import_common4.Get)("library"),
|
|
1426
|
+
_ts_param4(0, (0, import_common4.Query)("collection")),
|
|
1427
|
+
_ts_param4(1, (0, import_common4.Query)("disk")),
|
|
1428
|
+
_ts_param4(2, (0, import_common4.Query)("cursor")),
|
|
1429
|
+
_ts_param4(3, (0, import_common4.Query)("limit")),
|
|
1430
|
+
_ts_metadata4("design:type", Function),
|
|
1431
|
+
_ts_metadata4("design:paramtypes", [
|
|
1781
1432
|
String,
|
|
1782
1433
|
String,
|
|
1783
1434
|
String,
|
|
1784
1435
|
String
|
|
1785
1436
|
]),
|
|
1786
|
-
|
|
1437
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1787
1438
|
], MediaConsoleReadController.prototype, "library", null);
|
|
1788
|
-
|
|
1789
|
-
(0,
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1439
|
+
_ts_decorate4([
|
|
1440
|
+
(0, import_common4.Get)("library/:id"),
|
|
1441
|
+
_ts_param4(0, (0, import_common4.Param)("id")),
|
|
1442
|
+
_ts_metadata4("design:type", Function),
|
|
1443
|
+
_ts_metadata4("design:paramtypes", [
|
|
1793
1444
|
String
|
|
1794
1445
|
]),
|
|
1795
|
-
|
|
1446
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1796
1447
|
], MediaConsoleReadController.prototype, "libraryRecord", null);
|
|
1797
|
-
|
|
1798
|
-
(0,
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1448
|
+
_ts_decorate4([
|
|
1449
|
+
(0, import_common4.Get)("topology"),
|
|
1450
|
+
_ts_metadata4("design:type", Function),
|
|
1451
|
+
_ts_metadata4("design:paramtypes", []),
|
|
1452
|
+
_ts_metadata4("design:returntype", typeof Topology === "undefined" ? Object : Topology)
|
|
1802
1453
|
], MediaConsoleReadController.prototype, "topology", null);
|
|
1803
|
-
MediaConsoleReadController =
|
|
1804
|
-
(
|
|
1805
|
-
(0,
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1454
|
+
MediaConsoleReadController = _ts_decorate4([
|
|
1455
|
+
OpenToAuthorization(),
|
|
1456
|
+
(0, import_common4.Controller)(),
|
|
1457
|
+
_ts_param4(0, (0, import_common4.Inject)(MediaConsoleService)),
|
|
1458
|
+
_ts_metadata4("design:type", Function),
|
|
1459
|
+
_ts_metadata4("design:paramtypes", [
|
|
1809
1460
|
typeof MediaConsoleService === "undefined" ? Object : MediaConsoleService
|
|
1810
1461
|
])
|
|
1811
1462
|
], MediaConsoleReadController);
|
|
1812
1463
|
|
|
1813
1464
|
// src/server/media-console-api.module.ts
|
|
1814
|
-
function
|
|
1465
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
1815
1466
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1816
1467
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1817
1468
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1824,81 +1475,57 @@ function _ts_decorate6(decorators, target, key, desc) {
|
|
|
1824
1475
|
}
|
|
1825
1476
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1826
1477
|
}
|
|
1827
|
-
__name(
|
|
1828
|
-
var READ_BASE_GUARDS = baseGuards(MediaConsoleReadController);
|
|
1829
|
-
var ACTIONS_BASE_GUARDS = baseGuards(MediaConsoleActionsController);
|
|
1478
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
1830
1479
|
var MediaConsoleApiModule = class _MediaConsoleApiModule {
|
|
1831
1480
|
static {
|
|
1832
1481
|
__name(this, "MediaConsoleApiModule");
|
|
1833
1482
|
}
|
|
1834
1483
|
static register(options) {
|
|
1835
|
-
stampGuards(options.guards, [
|
|
1836
|
-
[
|
|
1837
|
-
MediaConsoleReadController,
|
|
1838
|
-
READ_BASE_GUARDS
|
|
1839
|
-
],
|
|
1840
|
-
[
|
|
1841
|
-
MediaConsoleActionsController,
|
|
1842
|
-
ACTIONS_BASE_GUARDS
|
|
1843
|
-
]
|
|
1844
|
-
]);
|
|
1845
1484
|
return {
|
|
1846
1485
|
module: _MediaConsoleApiModule,
|
|
1847
1486
|
imports: options.imports ?? [],
|
|
1848
|
-
controllers: [
|
|
1849
|
-
MediaConsoleReadController,
|
|
1850
|
-
MediaConsoleAuthController,
|
|
1487
|
+
controllers: options.routes === false ? [] : [
|
|
1488
|
+
consoleController(MediaConsoleReadController, options.decorators),
|
|
1851
1489
|
...options.actions ? [
|
|
1852
|
-
MediaConsoleActionsController
|
|
1490
|
+
consoleController(MediaConsoleActionsController, options.decorators)
|
|
1853
1491
|
] : []
|
|
1854
1492
|
],
|
|
1855
1493
|
providers: [
|
|
1856
1494
|
MediaConsoleService,
|
|
1857
|
-
|
|
1495
|
+
consoleAuthorizeProvider(options.authorize, options.moduleName ?? "MediaConsoleApiModule.register()"),
|
|
1858
1496
|
{
|
|
1859
1497
|
provide: MEDIA_DASHBOARD_ACTIONS,
|
|
1860
1498
|
useValue: options.actions
|
|
1861
1499
|
},
|
|
1862
|
-
{
|
|
1863
|
-
provide: MEDIA_CONSOLE_COOKIE_PATH,
|
|
1864
|
-
useValue: options.cookiePath
|
|
1865
|
-
},
|
|
1866
1500
|
{
|
|
1867
1501
|
provide: MEDIA_CONSOLE_OBJECT_URLS,
|
|
1868
1502
|
useValue: options.objectUrls
|
|
1869
1503
|
},
|
|
1870
|
-
options.authProvider,
|
|
1871
1504
|
options.insightsProvider ?? {
|
|
1872
1505
|
provide: MEDIA_CONSOLE_OBJECT_INSIGHTS,
|
|
1873
1506
|
useValue: []
|
|
1874
|
-
}
|
|
1875
|
-
// Class guards need a DI provider so Nest can instantiate them in THIS module's context
|
|
1876
|
-
// (where `imports` above resolves their dependencies). An already-instantiated guard needs
|
|
1877
|
-
// none — it's used as-is.
|
|
1878
|
-
...(options.guards ?? []).filter(isGuardClass)
|
|
1507
|
+
}
|
|
1879
1508
|
],
|
|
1880
|
-
//
|
|
1881
|
-
//
|
|
1882
|
-
// hand an unauthenticated navigation to the host's `unauthenticatedPage`. Exporting keeps the
|
|
1883
|
-
// single `authProvider` instance shared; re-providing the same factory in both modules would
|
|
1884
|
-
// run a `forRootAsync` host's `useAuth` twice.
|
|
1509
|
+
// For a host mounting the controllers itself (`routes: false`): its subclasses inject these, and
|
|
1510
|
+
// `ConsoleAuthorizeGuard` injects the resolved `authorize` check.
|
|
1885
1511
|
exports: [
|
|
1886
1512
|
MediaConsoleService,
|
|
1887
|
-
|
|
1513
|
+
MEDIA_DASHBOARD_ACTIONS,
|
|
1514
|
+
CONSOLE_AUTHORIZE_DECIDE
|
|
1888
1515
|
]
|
|
1889
1516
|
};
|
|
1890
1517
|
}
|
|
1891
1518
|
};
|
|
1892
|
-
MediaConsoleApiModule =
|
|
1893
|
-
(0,
|
|
1519
|
+
MediaConsoleApiModule = _ts_decorate5([
|
|
1520
|
+
(0, import_common5.Module)({})
|
|
1894
1521
|
], MediaConsoleApiModule);
|
|
1895
1522
|
|
|
1896
1523
|
// src/server/media-dashboard-ui.controller.ts
|
|
1897
1524
|
var import_node_fs = require("fs");
|
|
1898
1525
|
var import_node_path = require("path");
|
|
1899
1526
|
var import_node_url = require("url");
|
|
1900
|
-
var
|
|
1901
|
-
function
|
|
1527
|
+
var import_common6 = require("@nestjs/common");
|
|
1528
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
1902
1529
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1903
1530
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1904
1531
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1911,19 +1538,19 @@ function _ts_decorate7(decorators, target, key, desc) {
|
|
|
1911
1538
|
}
|
|
1912
1539
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1913
1540
|
}
|
|
1914
|
-
__name(
|
|
1915
|
-
function
|
|
1541
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1542
|
+
function _ts_metadata5(metadataKey, metadataValue) {
|
|
1916
1543
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1917
1544
|
return Reflect.metadata(metadataKey, metadataValue);
|
|
1918
1545
|
}
|
|
1919
1546
|
}
|
|
1920
|
-
__name(
|
|
1921
|
-
function
|
|
1547
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
1548
|
+
function _ts_param5(paramIndex, decorator) {
|
|
1922
1549
|
return function(target, key) {
|
|
1923
1550
|
decorator(target, key, paramIndex);
|
|
1924
1551
|
};
|
|
1925
1552
|
}
|
|
1926
|
-
__name(
|
|
1553
|
+
__name(_ts_param5, "_ts_param");
|
|
1927
1554
|
var BUILD_BASE = "/media";
|
|
1928
1555
|
function spaDir() {
|
|
1929
1556
|
return (0, import_node_url.fileURLToPath)(new URL("../spa", importMetaUrl));
|
|
@@ -1938,129 +1565,75 @@ var CONTENT_TYPES = {
|
|
|
1938
1565
|
".woff2": "font/woff2",
|
|
1939
1566
|
".ico": "image/x-icon"
|
|
1940
1567
|
};
|
|
1941
|
-
var MediaDashboardUiController = class
|
|
1568
|
+
var MediaDashboardUiController = class {
|
|
1942
1569
|
static {
|
|
1943
1570
|
__name(this, "MediaDashboardUiController");
|
|
1944
1571
|
}
|
|
1945
1572
|
basePath;
|
|
1946
1573
|
apiBasePath;
|
|
1947
|
-
auth;
|
|
1948
1574
|
dir = spaDir();
|
|
1949
|
-
|
|
1950
|
-
warned = false;
|
|
1951
|
-
constructor(basePath, apiBasePath, auth = null) {
|
|
1575
|
+
constructor(basePath, apiBasePath) {
|
|
1952
1576
|
this.basePath = basePath;
|
|
1953
1577
|
this.apiBasePath = apiBasePath;
|
|
1954
|
-
this.auth = auth;
|
|
1955
1578
|
}
|
|
1956
1579
|
// index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = "stuck
|
|
1957
|
-
// loading after a deploy"). The hashed assets below are immutable.
|
|
1958
|
-
|
|
1959
|
-
//
|
|
1960
|
-
// Non-passthrough `@Res()` so a host's `auth.unauthenticatedPage` can OWN the response and serve
|
|
1961
|
-
// its own page in place of the SPA. Without a hook this behaves exactly as before: the shell is
|
|
1962
|
-
// served to everyone, and the SPA's own auth screen renders once its API calls come back 401.
|
|
1963
|
-
async index(req, res) {
|
|
1964
|
-
if (await this.serveUnauthenticatedPage(req, res)) return;
|
|
1580
|
+
// loading after a deploy"). The hashed assets below are immutable.
|
|
1581
|
+
index() {
|
|
1965
1582
|
const indexPath = (0, import_node_path.join)(this.dir, "index.html");
|
|
1966
1583
|
if (!(0, import_node_fs.existsSync)(indexPath)) {
|
|
1967
|
-
throw new
|
|
1584
|
+
throw new import_common6.NotFoundException("Console SPA is not built. Run the package build.");
|
|
1968
1585
|
}
|
|
1969
1586
|
const html = (0, import_node_fs.readFileSync)(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
|
|
1970
1587
|
const inject = `<script>window.__MEDIA_BASE__='${this.basePath}';window.__MEDIA_API__='${this.apiBasePath}';</script>`;
|
|
1971
|
-
|
|
1972
|
-
}
|
|
1973
|
-
/**
|
|
1974
|
-
* Hand the response to the host's `unauthenticatedPage` when this navigation has no valid session.
|
|
1975
|
-
* Returns `true` when the host wrote a page (the caller must not also serve the SPA).
|
|
1976
|
-
*
|
|
1977
|
-
* Mode-A-only, deliberately: with `login` configured the SPA's own login form is the way in, so
|
|
1978
|
-
* replacing the shell would lock a Mode B host out of its own console.
|
|
1979
|
-
*/
|
|
1980
|
-
async serveUnauthenticatedPage(req, res) {
|
|
1981
|
-
const auth = this.auth;
|
|
1982
|
-
const hook = auth?.unauthenticatedPage;
|
|
1983
|
-
if (!auth || !hook) return false;
|
|
1984
|
-
if (auth.modes.includes("login")) return false;
|
|
1985
|
-
if (this.hasValidSession(auth, req)) return false;
|
|
1986
|
-
try {
|
|
1987
|
-
await hook({
|
|
1988
|
-
request: req,
|
|
1989
|
-
response: res,
|
|
1990
|
-
basePath: this.basePath
|
|
1991
|
-
});
|
|
1992
|
-
if (responseAlreadyWritten(res)) return true;
|
|
1993
|
-
this.warnOnce("auth.unauthenticatedPage returned without writing a response; serving the console SPA instead. The hook must write AND end the response it is given.");
|
|
1994
|
-
} catch (error) {
|
|
1995
|
-
this.warnOnce(`auth.unauthenticatedPage threw; serving the console SPA instead. ${String(error)}`);
|
|
1996
|
-
if (responseAlreadyWritten(res)) return true;
|
|
1997
|
-
}
|
|
1998
|
-
return false;
|
|
1999
|
-
}
|
|
2000
|
-
hasValidSession(auth, req) {
|
|
2001
|
-
const cookieValue = parseCookieHeader(readCookieHeader(req))[SESSION_COOKIE_NAME];
|
|
2002
|
-
if (cookieValue === void 0) return false;
|
|
2003
|
-
return verifySessionCookie(cookieValue, {
|
|
2004
|
-
secret: auth.secret
|
|
2005
|
-
}) !== null;
|
|
2006
|
-
}
|
|
2007
|
-
/** Once per process, not once per request: a broken hook fires on every unauthenticated visit. */
|
|
2008
|
-
warnOnce(message) {
|
|
2009
|
-
if (this.warned) return;
|
|
2010
|
-
this.warned = true;
|
|
2011
|
-
this.logger.warn(message);
|
|
1588
|
+
return html.includes("</head>") ? html.replace("</head>", `${inject}</head>`) : inject + html;
|
|
2012
1589
|
}
|
|
2013
1590
|
asset(file) {
|
|
2014
1591
|
const safe = (0, import_node_path.basename)(file);
|
|
2015
|
-
if (safe !== file) throw new
|
|
1592
|
+
if (safe !== file) throw new import_common6.NotFoundException();
|
|
2016
1593
|
const root = (0, import_node_path.resolve)(this.dir, "assets");
|
|
2017
1594
|
const assetPath = (0, import_node_path.resolve)(root, safe);
|
|
2018
1595
|
if (!assetPath.startsWith(root + import_node_path.sep) || !(0, import_node_fs.existsSync)(assetPath)) {
|
|
2019
|
-
throw new
|
|
1596
|
+
throw new import_common6.NotFoundException();
|
|
2020
1597
|
}
|
|
2021
1598
|
const type = CONTENT_TYPES[(0, import_node_path.extname)(safe)] ?? "application/octet-stream";
|
|
2022
|
-
return new
|
|
1599
|
+
return new import_common6.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
|
|
2023
1600
|
type
|
|
2024
1601
|
});
|
|
2025
1602
|
}
|
|
2026
1603
|
};
|
|
2027
|
-
|
|
2028
|
-
(0,
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
Object
|
|
2035
|
-
]),
|
|
2036
|
-
_ts_metadata6("design:returntype", Promise)
|
|
1604
|
+
_ts_decorate6([
|
|
1605
|
+
(0, import_common6.Get)(),
|
|
1606
|
+
(0, import_common6.Header)("Content-Type", "text/html; charset=utf-8"),
|
|
1607
|
+
(0, import_common6.Header)("Cache-Control", "no-store, must-revalidate"),
|
|
1608
|
+
_ts_metadata5("design:type", Function),
|
|
1609
|
+
_ts_metadata5("design:paramtypes", []),
|
|
1610
|
+
_ts_metadata5("design:returntype", String)
|
|
2037
1611
|
], MediaDashboardUiController.prototype, "index", null);
|
|
2038
|
-
|
|
2039
|
-
(0,
|
|
2040
|
-
(0,
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
1612
|
+
_ts_decorate6([
|
|
1613
|
+
(0, import_common6.Get)("assets/:file"),
|
|
1614
|
+
(0, import_common6.Header)("Cache-Control", "public, max-age=31536000, immutable"),
|
|
1615
|
+
_ts_param5(0, (0, import_common6.Param)("file")),
|
|
1616
|
+
_ts_metadata5("design:type", Function),
|
|
1617
|
+
_ts_metadata5("design:paramtypes", [
|
|
2044
1618
|
String
|
|
2045
1619
|
]),
|
|
2046
|
-
|
|
1620
|
+
_ts_metadata5("design:returntype", typeof import_common6.StreamableFile === "undefined" ? Object : import_common6.StreamableFile)
|
|
2047
1621
|
], MediaDashboardUiController.prototype, "asset", null);
|
|
2048
|
-
MediaDashboardUiController =
|
|
2049
|
-
(
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
String,
|
|
1622
|
+
MediaDashboardUiController = _ts_decorate6([
|
|
1623
|
+
OpenToAuthorization(),
|
|
1624
|
+
ConsolePage(),
|
|
1625
|
+
(0, import_common6.Controller)(),
|
|
1626
|
+
_ts_param5(0, (0, import_common6.Inject)(MEDIA_DASHBOARD_BASE_PATH)),
|
|
1627
|
+
_ts_param5(1, (0, import_common6.Inject)(MEDIA_DASHBOARD_API_PATH)),
|
|
1628
|
+
_ts_metadata5("design:type", Function),
|
|
1629
|
+
_ts_metadata5("design:paramtypes", [
|
|
2057
1630
|
String,
|
|
2058
|
-
|
|
1631
|
+
String
|
|
2059
1632
|
])
|
|
2060
1633
|
], MediaDashboardUiController);
|
|
2061
1634
|
|
|
2062
1635
|
// src/server/media-dashboard.module.ts
|
|
2063
|
-
function
|
|
1636
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
2064
1637
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2065
1638
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
2066
1639
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -2073,7 +1646,7 @@ function _ts_decorate8(decorators, target, key, desc) {
|
|
|
2073
1646
|
}
|
|
2074
1647
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2075
1648
|
}
|
|
2076
|
-
__name(
|
|
1649
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
2077
1650
|
function normalize(path) {
|
|
2078
1651
|
return `/${path.replace(/^\/+|\/+$/g, "")}`;
|
|
2079
1652
|
}
|
|
@@ -2083,39 +1656,25 @@ var MediaDashboardModule = class _MediaDashboardModule {
|
|
|
2083
1656
|
__name(this, "MediaDashboardModule");
|
|
2084
1657
|
}
|
|
2085
1658
|
static forRoot(options = {}) {
|
|
2086
|
-
|
|
2087
|
-
return _MediaDashboardModule.build(options, apiBasePath, {
|
|
2088
|
-
provide: MEDIA_CONSOLE_AUTH,
|
|
2089
|
-
useValue: resolveConsoleAuth(options.auth)
|
|
2090
|
-
}, {
|
|
1659
|
+
return _MediaDashboardModule.build("MediaDashboardModule.forRoot()", options, {
|
|
2091
1660
|
provide: MEDIA_CONSOLE_OBJECT_INSIGHTS,
|
|
2092
1661
|
useValue: options.objectInsights ?? []
|
|
2093
|
-
}
|
|
1662
|
+
});
|
|
2094
1663
|
}
|
|
2095
|
-
static forRootAsync(options) {
|
|
2096
|
-
|
|
2097
|
-
const authProvider = {
|
|
2098
|
-
provide: MEDIA_CONSOLE_AUTH,
|
|
2099
|
-
inject: options.inject ?? [],
|
|
2100
|
-
useFactory: /* @__PURE__ */ __name(async (...deps) => resolveConsoleAuth(await options.useAuth(...deps)), "useFactory")
|
|
2101
|
-
};
|
|
2102
|
-
const insightsProvider = {
|
|
1664
|
+
static forRootAsync(options = {}) {
|
|
1665
|
+
return _MediaDashboardModule.build("MediaDashboardModule.forRootAsync()", options, {
|
|
2103
1666
|
provide: MEDIA_CONSOLE_OBJECT_INSIGHTS,
|
|
2104
|
-
inject: options.
|
|
1667
|
+
inject: options.inject ?? [],
|
|
2105
1668
|
useFactory: /* @__PURE__ */ __name(async (...deps) => options.useObjectInsights === void 0 ? [] : await options.useObjectInsights(...deps), "useFactory")
|
|
2106
|
-
};
|
|
2107
|
-
return _MediaDashboardModule.build(options, apiBasePath, authProvider, insightsProvider, options.imports);
|
|
1669
|
+
});
|
|
2108
1670
|
}
|
|
2109
|
-
/** Shared wiring: static routing + the API module, with
|
|
2110
|
-
static build(
|
|
1671
|
+
/** Shared wiring: static routing + the API module, with the insights from the given provider. */
|
|
1672
|
+
static build(name, options, insightsProvider) {
|
|
2111
1673
|
const basePath = normalize(options.basePath ?? "/media");
|
|
1674
|
+
const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
|
|
2112
1675
|
const actions = options.actions === true;
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
MediaDashboardUiController,
|
|
2116
|
-
[]
|
|
2117
|
-
]
|
|
2118
|
-
]);
|
|
1676
|
+
const mount = options.routes !== false;
|
|
1677
|
+
const imports = options.imports;
|
|
2119
1678
|
return {
|
|
2120
1679
|
module: _MediaDashboardModule,
|
|
2121
1680
|
imports: [
|
|
@@ -2126,38 +1685,33 @@ var MediaDashboardModule = class _MediaDashboardModule {
|
|
|
2126
1685
|
strategy: options.objectUrls ?? "auto",
|
|
2127
1686
|
apiBasePath
|
|
2128
1687
|
},
|
|
2129
|
-
// `Path=/`, deliberately NOT scoped to `apiBasePath` (nor to `basePath`): the two mounts
|
|
2130
|
-
// are independently configurable and can live at unrelated paths (e.g. a `/media` UI
|
|
2131
|
-
// with a `/api/media/console` API). Scoped to the API base, the browser withheld the
|
|
2132
|
-
// cookie on the full-page navigation to `basePath`, so the freshly minted session could
|
|
2133
|
-
// not open the console it was minted for — the launcher landed on the unauthenticated
|
|
2134
|
-
// page. Scoping to `basePath` instead would just move the failure to the API calls.
|
|
2135
|
-
// The cookie is HttpOnly + signed + short-lived, so the broader scope is a reasonable
|
|
2136
|
-
// trade for being correct in any mount configuration. Matches nestjs-durable-dashboard
|
|
2137
|
-
// and nestjs-agent-dashboard; telescope can scope to its mount because its UI and API
|
|
2138
|
-
// always share one root.
|
|
2139
|
-
cookiePath: "/",
|
|
2140
|
-
authProvider,
|
|
2141
1688
|
insightsProvider,
|
|
2142
1689
|
imports,
|
|
2143
|
-
...options.
|
|
2144
|
-
|
|
2145
|
-
} : {}
|
|
1690
|
+
...options.authorize !== void 0 ? {
|
|
1691
|
+
authorize: options.authorize
|
|
1692
|
+
} : {},
|
|
1693
|
+
moduleName: name,
|
|
1694
|
+
...options.decorators ? {
|
|
1695
|
+
decorators: options.decorators
|
|
1696
|
+
} : {},
|
|
1697
|
+
routes: mount
|
|
2146
1698
|
}),
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
MediaDashboardUiController
|
|
1699
|
+
...mount ? [
|
|
1700
|
+
import_core2.RouterModule.register([
|
|
1701
|
+
{
|
|
1702
|
+
path: basePath,
|
|
1703
|
+
module: _MediaDashboardModule
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
path: apiBasePath,
|
|
1707
|
+
module: MediaConsoleApiModule
|
|
1708
|
+
}
|
|
1709
|
+
])
|
|
1710
|
+
] : []
|
|
2160
1711
|
],
|
|
1712
|
+
controllers: mount ? [
|
|
1713
|
+
consoleController(MediaDashboardUiController, options.decorators)
|
|
1714
|
+
] : [],
|
|
2161
1715
|
providers: [
|
|
2162
1716
|
{
|
|
2163
1717
|
provide: MEDIA_DASHBOARD_BASE_PATH,
|
|
@@ -2166,28 +1720,44 @@ var MediaDashboardModule = class _MediaDashboardModule {
|
|
|
2166
1720
|
{
|
|
2167
1721
|
provide: MEDIA_DASHBOARD_API_PATH,
|
|
2168
1722
|
useValue: apiBasePath
|
|
2169
|
-
}
|
|
2170
|
-
// MediaDashboardUiController is hosted HERE, so its guards DI-instantiate from this
|
|
2171
|
-
// module — class guards need a provider; an already-instantiated guard needs none.
|
|
2172
|
-
...(options.guards ?? []).filter(isGuardClass)
|
|
1723
|
+
}
|
|
2173
1724
|
],
|
|
2174
|
-
// Re-export the API module so its MediaConsoleService reaches importers if they want it
|
|
1725
|
+
// Re-export the API module so its MediaConsoleService reaches importers if they want it; the
|
|
1726
|
+
// resolved paths too, so a host mounting the controllers itself (`routes: false`) can extend
|
|
1727
|
+
// `MediaDashboardUiController` in a module of its own.
|
|
2175
1728
|
exports: [
|
|
2176
|
-
MediaConsoleApiModule
|
|
1729
|
+
MediaConsoleApiModule,
|
|
1730
|
+
MEDIA_DASHBOARD_BASE_PATH,
|
|
1731
|
+
MEDIA_DASHBOARD_API_PATH
|
|
2177
1732
|
]
|
|
2178
1733
|
};
|
|
2179
1734
|
}
|
|
2180
1735
|
};
|
|
2181
|
-
MediaDashboardModule =
|
|
2182
|
-
(0,
|
|
1736
|
+
MediaDashboardModule = _ts_decorate7([
|
|
1737
|
+
(0, import_common7.Module)({})
|
|
2183
1738
|
], MediaDashboardModule);
|
|
2184
1739
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2185
1740
|
0 && (module.exports = {
|
|
2186
|
-
|
|
1741
|
+
CONSOLE_AUTHORIZE_DECIDE,
|
|
1742
|
+
CONSOLE_AUTHORIZE_MISSING_WARNING,
|
|
1743
|
+
CONSOLE_GATE,
|
|
1744
|
+
CONSOLE_PAGE_METADATA,
|
|
1745
|
+
ConsoleAccessDenied,
|
|
1746
|
+
ConsoleAuthorizeFilter,
|
|
1747
|
+
ConsoleAuthorizeGuard,
|
|
1748
|
+
ConsolePage,
|
|
1749
|
+
MEDIA_MULTIPART,
|
|
2187
1750
|
MEDIA_STORE,
|
|
2188
1751
|
MEDIA_UPLOAD_SESSIONS,
|
|
1752
|
+
MediaConsoleActionsController,
|
|
2189
1753
|
MediaConsoleApiModule,
|
|
1754
|
+
MediaConsoleReadController,
|
|
2190
1755
|
MediaConsoleService,
|
|
2191
|
-
MediaDashboardModule
|
|
1756
|
+
MediaDashboardModule,
|
|
1757
|
+
MediaDashboardUiController,
|
|
1758
|
+
UseConsoleAuthorize,
|
|
1759
|
+
authorizeByToken,
|
|
1760
|
+
normalizeConsoleResult,
|
|
1761
|
+
resolveConsoleAuthorize
|
|
2192
1762
|
});
|
|
2193
1763
|
//# sourceMappingURL=index.cjs.map
|