@dudousxd/nestjs-media-dashboard 0.2.1 → 0.3.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.
@@ -74,6 +74,8 @@ export declare const mediaConsoleClient: {
74
74
  }) => Promise<void>;
75
75
  /** Create a folder (a zero-byte marker at `<prefix>/`) on the disk. */
76
76
  createFolder: (disk: string, prefix: string) => Promise<void>;
77
+ /** Recursively delete a folder (every object under `<prefix>/` plus the marker). */
78
+ deleteFolder: (disk: string, prefix: string) => Promise<void>;
77
79
  };
78
80
  export type MediaConsoleClient = typeof mediaConsoleClient;
79
81
  //# sourceMappingURL=media-console-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"media-console-client.d.ts","sourceRoot":"","sources":["../../src/client/media-console-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"media-console-client.d.ts","sourceRoot":"","sources":["../../src/client/media-console-client.ts"],"names":[],"mappings":"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"}
@@ -147,5 +147,7 @@ export const mediaConsoleClient = {
147
147
  }), file),
148
148
  /** Create a folder (a zero-byte marker at `<prefix>/`) on the disk. */
149
149
  createFolder: (disk, prefix) => send('POST', `/disks/${encodeURIComponent(disk)}/folder`, { prefix }),
150
+ /** Recursively delete a folder (every object under `<prefix>/` plus the marker). */
151
+ deleteFolder: (disk, prefix) => send('DELETE', withQuery(`/disks/${encodeURIComponent(disk)}/folder`, { prefix })),
150
152
  };
151
153
  //# sourceMappingURL=media-console-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"media-console-client.js","sourceRoot":"","sources":["../../src/client/media-console-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"media-console-client.js","sourceRoot":"","sources":["../../src/client/media-console-client.ts"],"names":[],"mappings":"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"}
@@ -402,6 +402,8 @@ function _ts_param2(paramIndex, decorator) {
402
402
  __name(_ts_param2, "_ts_param");
403
403
  var URL_TTL_SECONDS = 300;
404
404
  var DEFAULT_PAGE_LIMIT = 50;
405
+ var MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
406
+ var DELETE_SWEEP_LIMIT = 1e3;
405
407
  function lastSegment(prefix) {
406
408
  const trimmed = prefix.replace(/\/+$/, "");
407
409
  const slash = trimmed.lastIndexOf("/");
@@ -532,9 +534,25 @@ var MediaConsoleService = class {
532
534
  };
533
535
  }
534
536
  /** Writes an object from a byte stream (a browser upload). The caller supplies the full key
535
- * (prefix + filename); an unknown disk 404s via {@link diskOrThrow}. Actions-gated. */
537
+ * (prefix + filename); an unknown disk 404s via {@link diskOrThrow}. Actions-gated.
538
+ *
539
+ * The request stream is buffered before the write: S3's PutObject needs a known Content-Length,
540
+ * which a raw request stream doesn't carry, so passing the stream straight through fails. Bounded
541
+ * at {@link MAX_UPLOAD_BYTES} so a runaway upload can't exhaust the pod's heap — larger files
542
+ * belong on the resumable upload path, not this console convenience upload. */
536
543
  async putObject(disk, key, body, contentType) {
537
- await this.diskOrThrow(disk).put(key, body, contentType ? {
544
+ const driver = this.diskOrThrow(disk);
545
+ const chunks = [];
546
+ let total = 0;
547
+ for await (const chunk of body) {
548
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
549
+ total += buffer.length;
550
+ if (total > MAX_UPLOAD_BYTES) {
551
+ throw new import_common2.PayloadTooLargeException(`Upload exceeds the ${MAX_UPLOAD_BYTES / (1024 * 1024)} MB console limit.`);
552
+ }
553
+ chunks.push(buffer);
554
+ }
555
+ await driver.put(key, Buffer.concat(chunks), contentType ? {
538
556
  contentType
539
557
  } : void 0);
540
558
  }
@@ -548,6 +566,28 @@ var MediaConsoleService = class {
548
566
  async deleteObject(disk, key) {
549
567
  await this.diskOrThrow(disk).delete(key);
550
568
  }
569
+ /** Recursively deletes a "folder": every object under `<prefix>/` (nested included) plus the
570
+ * zero-byte marker itself. Sweeps the prefix without a delimiter (flat listing) so it reaches
571
+ * nested keys, paginating until the disk reports no more. Actions-gated. */
572
+ async deleteFolder(disk, prefix) {
573
+ const driver = this.diskOrThrow(disk);
574
+ const normalized = prefix.replace(/^\/+/, "").replace(/\/+$/, "");
575
+ if (normalized === "") throw new import_common2.BadRequestException("Folder is required");
576
+ const sweepPrefix = `${normalized}/`;
577
+ let cursor;
578
+ do {
579
+ const result = await driver.list(sweepPrefix, {
580
+ limit: DELETE_SWEEP_LIMIT,
581
+ ...cursor ? {
582
+ cursor
583
+ } : {}
584
+ });
585
+ for (const entry of result.files) {
586
+ await driver.delete(entry.key);
587
+ }
588
+ cursor = result.cursor;
589
+ } while (cursor);
590
+ }
551
591
  async copyObject(disk, from, to) {
552
592
  await this.diskOrThrow(disk).copy(from, to);
553
593
  }
@@ -722,6 +762,9 @@ var MediaConsoleActionsController = class {
722
762
  createFolder(disk, body) {
723
763
  return this.service.createFolder(disk, body.prefix);
724
764
  }
765
+ deleteFolder(disk, prefix) {
766
+ return this.service.deleteFolder(disk, prefix);
767
+ }
725
768
  abortUpload(id) {
726
769
  return this.service.abortUpload(id);
727
770
  }
@@ -793,6 +836,18 @@ _ts_decorate3([
793
836
  ]),
794
837
  _ts_metadata3("design:returntype", typeof Promise === "undefined" ? Object : Promise)
795
838
  ], MediaConsoleActionsController.prototype, "createFolder", null);
839
+ _ts_decorate3([
840
+ (0, import_common3.Delete)("disks/:disk/folder"),
841
+ (0, import_common3.HttpCode)(204),
842
+ _ts_param3(0, (0, import_common3.Param)("disk")),
843
+ _ts_param3(1, (0, import_common3.Query)("prefix")),
844
+ _ts_metadata3("design:type", Function),
845
+ _ts_metadata3("design:paramtypes", [
846
+ String,
847
+ String
848
+ ]),
849
+ _ts_metadata3("design:returntype", typeof Promise === "undefined" ? Object : Promise)
850
+ ], MediaConsoleActionsController.prototype, "deleteFolder", null);
796
851
  _ts_decorate3([
797
852
  (0, import_common3.Post)("uploads/:id/abort"),
798
853
  (0, import_common3.HttpCode)(204),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/index.ts","../../../../node_modules/.pnpm/tsup@8.3.5_@swc+core@1.15.41_jiti@1.21.7_postcss@8.4.49_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js","../../src/server/media-dashboard.module.ts","../../src/server/auth/config.ts","../../src/server/media-console-api.module.ts","../../src/server/media-console-actions.controller.ts","../../src/server/media-console.guard.ts","../../src/server/auth/cookie-header.ts","../../src/server/auth/request.ts","../../src/server/auth/response.ts","../../src/server/auth/session-cookie.ts","../../src/server/auth/session-cookie-io.ts","../../src/server/tokens.ts","../../src/server/media-console.service.ts","../../src/server/media-console-auth.controller.ts","../../src/server/media-console-read.controller.ts","../../src/server/media-dashboard-ui.controller.ts"],"sourcesContent":["export {\n MediaDashboardModule,\n type MediaDashboardOptions,\n type MediaDashboardAsyncOptions,\n} from './media-dashboard.module.js';\nexport { MediaConsoleApiModule } from './media-console-api.module.js';\nexport { MediaConsoleService } from './media-console.service.js';\nexport type {\n ConsoleAuthOptions,\n LoginHook,\n SessionHook,\n} from './auth/config.js';\nexport type { ConsoleSessionUser } from './auth/session-cookie.js';\nexport {\n MEDIA_STORAGE_SHARED,\n MEDIA_STORE,\n MEDIA_UPLOAD_SESSIONS,\n} from './tokens.js';\n// The API response types (also published at the `./client` entry) for host reuse.\nexport type * from '../client/types.js';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import {\n type DynamicModule,\n Module,\n type ModuleMetadata,\n type OptionalFactoryDependency,\n type Provider,\n} from '@nestjs/common';\nimport type { InjectionToken } from '@nestjs/common';\nimport { RouterModule } from '@nestjs/core';\nimport { type ConsoleAuthOptions, resolveConsoleAuth } from './auth/config.js';\nimport { MediaConsoleApiModule } from './media-console-api.module.js';\nimport { MediaDashboardUiController } from './media-dashboard-ui.controller.js';\nimport {\n MEDIA_CONSOLE_AUTH,\n MEDIA_DASHBOARD_API_PATH,\n MEDIA_DASHBOARD_BASE_PATH,\n} from './tokens.js';\n\nexport interface MediaDashboardOptions {\n /**\n * Where the SPA (UI) is served. Default `/media`. A page route — keep it out of an `/api`\n * prefix so it reads as a UI, not an endpoint.\n */\n basePath?: string;\n /**\n * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under\n * your app's `/api` prefix — e.g. `/api/media/console` — so the API inherits the app's\n * auth/proxy rules while the UI stays at `basePath`.\n */\n apiBasePath?: string;\n /**\n * Enable the destructive endpoints (delete object/record, copy/move, abort upload). Default\n * `false` — the read API is always available. Front the mount with your own guard either way.\n */\n actions?: boolean;\n /**\n * Gate the console (SPA + API) behind a built-in session-cookie login, telescope-style. Omit to\n * leave the console open (front it with your own guard). When set, the SPA renders a login screen\n * until a valid cookie exists; supply a `login(username, password)` and/or `session(request)`\n * hook that returns a session user (or `null` to deny) — see {@link ConsoleAuthOptions}.\n */\n auth?: ConsoleAuthOptions;\n}\n\n/**\n * Async variant of {@link MediaDashboardOptions}. The mount paths + `actions` stay static (the\n * router needs them at module-definition time), but `auth` is built by an injected factory — so\n * the `login`/`session` hooks can reach your DB/services (e.g. an EntityManager to validate an\n * admin). Returning `undefined` from the factory leaves the console open.\n */\nexport interface MediaDashboardAsyncOptions {\n basePath?: string;\n apiBasePath?: string;\n actions?: boolean;\n /** Modules exporting the providers `inject` needs (omit when they're global). */\n imports?: ModuleMetadata['imports'];\n /** Providers injected into `useAuth`, in order. */\n inject?: Array<InjectionToken | OptionalFactoryDependency>;\n /** Build the `auth` config from injected deps (or `undefined` to leave the console open). */\n useAuth: (\n ...deps: any[]\n ) => ConsoleAuthOptions | undefined | Promise<ConsoleAuthOptions | undefined>;\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n\n/**\n * Mounts the /media console: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`\n * (default `<basePath>/api`). Import via `MediaDashboardModule.forRoot(...)` (or `forRootAsync` when\n * the `auth` hooks need injected services) alongside `MediaModule` (global), so it resolves the\n * storage/store/upload tokens. Exclude the UI/API paths from any global `/api` prefix.\n */\n@Module({})\nexport class MediaDashboardModule {\n static forRoot(options: MediaDashboardOptions = {}): DynamicModule {\n const apiBasePath = normalize(\n options.apiBasePath ?? `${normalize(options.basePath ?? '/media')}/api`,\n );\n return MediaDashboardModule.build(options, apiBasePath, {\n provide: MEDIA_CONSOLE_AUTH,\n useValue: resolveConsoleAuth(options.auth),\n });\n }\n\n static forRootAsync(options: MediaDashboardAsyncOptions): DynamicModule {\n const apiBasePath = normalize(\n options.apiBasePath ?? `${normalize(options.basePath ?? '/media')}/api`,\n );\n const authProvider: Provider = {\n provide: MEDIA_CONSOLE_AUTH,\n inject: options.inject ?? [],\n useFactory: async (...deps: any[]) => resolveConsoleAuth(await options.useAuth(...deps)),\n };\n return MediaDashboardModule.build(options, apiBasePath, authProvider, options.imports);\n }\n\n /** Shared wiring: static routing + the API module, with `auth` supplied by the given provider. */\n private static build(\n options: { basePath?: string; apiBasePath?: string; actions?: boolean },\n apiBasePath: string,\n authProvider: Provider,\n imports?: ModuleMetadata['imports'],\n ): DynamicModule {\n const basePath = normalize(options.basePath ?? '/media');\n const actions = options.actions === true;\n return {\n module: MediaDashboardModule,\n imports: [\n MediaConsoleApiModule.register({ actions, cookiePath: apiBasePath, authProvider, imports }),\n RouterModule.register([\n { path: basePath, module: MediaDashboardModule }, // the UI controller below\n { path: apiBasePath, module: MediaConsoleApiModule },\n ]),\n ],\n controllers: [MediaDashboardUiController],\n providers: [\n { provide: MEDIA_DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: MEDIA_DASHBOARD_API_PATH, useValue: apiBasePath },\n ],\n // Re-export the API module so its MediaConsoleService reaches importers if they want it.\n exports: [MediaConsoleApiModule],\n };\n }\n}\n","import type { ConsoleSessionUser } from './session-cookie.js';\n\n/** Host hook for Mode A — validates the host's own auth on the raw request. */\nexport type SessionHook = (\n request: unknown,\n) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;\n\n/** Host hook for Mode B — validates submitted credentials from the built-in login screen. */\nexport type LoginHook = (\n username: string,\n password: string,\n) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;\n\n/** Author-facing `auth` option on `MediaDashboardModule.forRoot`. Mirrors telescope's dashboardAuth. */\nexport interface ConsoleAuthOptions {\n /** REQUIRED HMAC-SHA256 signing key. Missing/empty => boot error (fail closed). */\n secret: string;\n /** Cookie TTL as a duration string (`'8h'`, `'30m'`, `'7d'`). Default `'8h'`. */\n ttl?: string;\n /** Mode A: validate the host's own auth on the raw request. */\n session?: SessionHook;\n /** Mode B: validate credentials from the built-in login screen. */\n login?: LoginHook;\n}\n\nexport type AuthMode = 'session' | 'login';\n\n/** Resolved, validated console-auth config shared by the guard, controller, and SPA. */\nexport interface ResolvedConsoleAuth {\n secret: string;\n ttlMs: number;\n modes: AuthMode[];\n session?: SessionHook;\n login?: LoginHook;\n}\n\nconst DEFAULT_TTL_MS = 8 * 60 * 60 * 1000;\nconst DURATION_UNITS: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n};\n\n/** Parse a `'<number><s|m|h|d>'` duration to ms; falls back to the 8h default on a bad value. */\nfunction durationToMs(ttl: string | undefined): number {\n if (ttl === undefined) return DEFAULT_TTL_MS;\n const match = /^(\\d+)([smhd])$/.exec(ttl.trim());\n if (!match) return DEFAULT_TTL_MS;\n const unit = DURATION_UNITS[match[2] ?? ''];\n if (unit === undefined) return DEFAULT_TTL_MS;\n return Number(match[1]) * unit;\n}\n\n/**\n * Validate + resolve the `auth` option. Returns `null` when unconfigured (the console stays open —\n * front it with your own guard). Throws at boot (fail closed) when configured but missing a secret\n * or any hook — the host learns immediately rather than shipping an un-mintable gate.\n */\nexport function resolveConsoleAuth(\n options: ConsoleAuthOptions | undefined,\n): ResolvedConsoleAuth | null {\n if (!options) return null;\n if (!options.secret) {\n throw new Error('MediaDashboardModule: auth.secret is required when `auth` is configured.');\n }\n const modes: AuthMode[] = [];\n if (options.session) modes.push('session');\n if (options.login) modes.push('login');\n if (modes.length === 0) {\n throw new Error('MediaDashboardModule: auth needs a `login` and/or `session` hook.');\n }\n return {\n secret: options.secret,\n ttlMs: durationToMs(options.ttl),\n modes,\n ...(options.session ? { session: options.session } : {}),\n ...(options.login ? { login: options.login } : {}),\n };\n}\n","import { type DynamicModule, Module, type ModuleMetadata, type Provider } from '@nestjs/common';\nimport { MediaConsoleActionsController } from './media-console-actions.controller.js';\nimport { MediaConsoleAuthController } from './media-console-auth.controller.js';\nimport { MediaConsoleReadController } from './media-console-read.controller.js';\nimport { MediaConsoleGuard } from './media-console.guard.js';\nimport { MediaConsoleService } from './media-console.service.js';\nimport { MEDIA_CONSOLE_COOKIE_PATH, MEDIA_DASHBOARD_ACTIONS } from './tokens.js';\n\ninterface ApiModuleOptions {\n actions: boolean;\n /** Cookie `Path` — the JSON API base — so the session cookie rides every console API request. */\n cookiePath: string;\n /** Provider for `MEDIA_CONSOLE_AUTH` — a `useValue` (forRoot) or a `useFactory` (forRootAsync). */\n authProvider: Provider;\n /** Extra imports the auth factory's `inject` deps live in (empty when they're global). */\n imports?: ModuleMetadata['imports'];\n}\n\n/**\n * Holds the console's JSON API: the read controller (always), the actions controller (only when\n * `actions: true`), and the auth controller (always — it mints the session the guard checks, so it\n * is never itself guarded). The read/action controllers carry `MediaConsoleGuard`, a no-op unless\n * the host configured `auth`.\n */\n@Module({})\nexport class MediaConsoleApiModule {\n static register(options: ApiModuleOptions): DynamicModule {\n return {\n module: MediaConsoleApiModule,\n imports: options.imports ?? [],\n controllers: [\n MediaConsoleReadController,\n MediaConsoleAuthController,\n ...(options.actions ? [MediaConsoleActionsController] : []),\n ],\n providers: [\n MediaConsoleService,\n MediaConsoleGuard,\n { provide: MEDIA_DASHBOARD_ACTIONS, useValue: options.actions },\n { provide: MEDIA_CONSOLE_COOKIE_PATH, useValue: options.cookiePath },\n options.authProvider,\n ],\n exports: [MediaConsoleService],\n };\n }\n}\n","import type { Readable } from 'node:stream';\nimport {\n Body,\n Controller,\n Delete,\n HttpCode,\n Inject,\n Param,\n Post,\n Query,\n Req,\n UseGuards,\n} from '@nestjs/common';\nimport { MediaConsoleGuard } from './media-console.guard.js';\nimport { MediaConsoleService } from './media-console.service.js';\n\ninterface CopyMoveBody {\n from: string;\n to: string;\n}\n\ninterface CreateFolderBody {\n prefix: string;\n}\n\n/**\n * Destructive JSON API for the /media console. Only registered when the host opts in with\n * `MediaDashboardModule.forRoot({ actions: true })` (default off). Bare `@Controller()` — the path\n * prefix comes from `RouterModule`, sharing the API base with the read controller. Gated by\n * `MediaConsoleGuard` (session cookie) when the host configured `auth`.\n */\n@UseGuards(MediaConsoleGuard)\n@Controller()\nexport class MediaConsoleActionsController {\n constructor(@Inject(MediaConsoleService) private readonly service: MediaConsoleService) {}\n\n @Delete('disks/:disk/object')\n @HttpCode(204)\n deleteObject(@Param('disk') disk: string, @Query('key') key: string): Promise<void> {\n return this.service.deleteObject(disk, key);\n }\n\n @Post('disks/:disk/copy')\n @HttpCode(204)\n copyObject(@Param('disk') disk: string, @Body() body: CopyMoveBody): Promise<void> {\n return this.service.copyObject(disk, body.from, body.to);\n }\n\n @Post('disks/:disk/move')\n @HttpCode(204)\n moveObject(@Param('disk') disk: string, @Body() body: CopyMoveBody): Promise<void> {\n return this.service.moveObject(disk, body.from, body.to);\n }\n\n /**\n * Uploads a file to `key` on the disk. The body is the raw bytes sent as `application/octet-stream`\n * so the host's JSON/urlencoded body parsers never consume the stream; the real MIME rides as the\n * `type` query param and becomes the stored object's Content-Type. `@Req()` is the request stream.\n */\n @Post('disks/:disk/upload')\n @HttpCode(204)\n uploadObject(\n @Param('disk') disk: string,\n @Query('key') key: string,\n @Req() request: Readable,\n @Query('type') type?: string,\n ): Promise<void> {\n return this.service.putObject(disk, key, request, type);\n }\n\n @Post('disks/:disk/folder')\n @HttpCode(204)\n createFolder(@Param('disk') disk: string, @Body() body: CreateFolderBody): Promise<void> {\n return this.service.createFolder(disk, body.prefix);\n }\n\n @Post('uploads/:id/abort')\n @HttpCode(204)\n abortUpload(@Param('id') id: string): Promise<void> {\n return this.service.abortUpload(id);\n }\n\n @Delete('library/:id')\n @HttpCode(204)\n deleteLibraryRecord(@Param('id') id: string): Promise<void> {\n return this.service.deleteLibraryRecord(id);\n }\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n Inject,\n Injectable,\n Optional,\n UnauthorizedException,\n} from '@nestjs/common';\nimport type { ResolvedConsoleAuth } from './auth/config.js';\nimport { parseCookieHeader } from './auth/cookie-header.js';\nimport { attachSession, readCookieHeader } from './auth/request.js';\nimport { SESSION_COOKIE_NAME, issueSessionCookie } from './auth/session-cookie-io.js';\nimport { type ConsoleSession, verifySessionCookie } from './auth/session-cookie.js';\nimport { MEDIA_CONSOLE_AUTH, MEDIA_CONSOLE_COOKIE_PATH } from './tokens.js';\n\n/**\n * Gates the console's read + action controllers on a valid session cookie — but ONLY when the host\n * configured `auth`. With no auth configured the resolved value is `null` and the guard is a\n * no-op (the console stays open; front it with your own guard). The auth controller that MINTS the\n * cookie is deliberately NOT decorated with this guard.\n */\n@Injectable()\nexport class MediaConsoleGuard implements CanActivate {\n constructor(\n @Optional() @Inject(MEDIA_CONSOLE_AUTH) private readonly auth: ResolvedConsoleAuth | null,\n @Optional() @Inject(MEDIA_CONSOLE_COOKIE_PATH) private readonly cookiePath: string | null,\n ) {}\n\n canActivate(context: ExecutionContext): boolean {\n if (!this.auth) return true;\n const http = context.switchToHttp();\n const request = http.getRequest();\n const session = this.verifyRequestSession(request);\n // Absent/invalid/expired cookie => 401 (not 403): the SPA reads this as \"show the login screen\".\n if (!session) throw new UnauthorizedException();\n attachSession(request, session);\n this.maybeRenew(http.getResponse(), request, session);\n return true;\n }\n\n private verifyRequestSession(request: unknown): ConsoleSession | null {\n if (!this.auth) return null;\n const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];\n if (cookieValue === undefined) return null;\n return verifySessionCookie(cookieValue, { secret: this.auth.secret });\n }\n\n /**\n * Sliding renewal: when a valid cookie is past half its TTL, re-issue a fresh one so active users\n * never get logged out mid-session. Appends a new Set-Cookie (preserving any others already set).\n */\n private maybeRenew(response: unknown, request: unknown, session: ConsoleSession): void {\n if (!this.auth) return;\n const now = Date.now();\n if (now - session.iat <= this.auth.ttlMs / 2) return;\n issueSessionCookie(\n {\n id: session.sub,\n ...(session.name !== undefined ? { name: session.name } : {}),\n roles: session.roles,\n },\n {\n auth: this.auth,\n cookiePath: this.cookiePath ?? '/',\n request,\n response,\n now,\n },\n );\n }\n}\n","/**\n * Parse a raw `Cookie` request header into a name→value map. Dependency-free so it works on raw\n * Express AND Fastify requests without `cookie-parser`. Values are URL-decoded; malformed segments\n * are skipped, never thrown.\n */\nexport function parseCookieHeader(header: string | undefined): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (typeof header !== 'string' || header === '') return cookies;\n for (const segment of header.split(';')) {\n const eq = segment.indexOf('=');\n if (eq <= 0) continue;\n const name = segment.slice(0, eq).trim();\n if (name === '') continue;\n let rawValue = segment.slice(eq + 1).trim();\n // Strip a single pair of wrapping double-quotes (RFC 6265 quoted form).\n if (rawValue.length >= 2 && rawValue.startsWith('\"') && rawValue.endsWith('\"')) {\n rawValue = rawValue.slice(1, -1);\n }\n // First occurrence wins; don't clobber an earlier value with a later dup.\n if (name in cookies) continue;\n try {\n cookies[name] = decodeURIComponent(rawValue);\n } catch {\n cookies[name] = rawValue;\n }\n }\n return cookies;\n}\n\nexport interface SetCookieOptions {\n path: string;\n maxAgeSeconds: number;\n secure: boolean;\n clear?: boolean;\n}\n\n/**\n * Serialize a `Set-Cookie` header value. Always `HttpOnly` + `SameSite=Lax` (Lax blocks\n * cross-site POSTs carrying the cookie — CSRF coverage for the console's mutation POSTs).\n * Platform-agnostic: just a string.\n */\nexport function serializeSetCookie(name: string, value: string, options: SetCookieOptions): string {\n const parts = [`${name}=${options.clear ? '' : encodeURIComponent(value)}`];\n parts.push(`Path=${options.path}`);\n parts.push('HttpOnly');\n parts.push('SameSite=Lax');\n if (options.secure) parts.push('Secure');\n if (options.clear) {\n parts.push('Max-Age=0');\n parts.push('Expires=Thu, 01 Jan 1970 00:00:00 GMT');\n } else {\n parts.push(`Max-Age=${options.maxAgeSeconds}`);\n }\n return parts.join('; ');\n}\n","import type { ConsoleSession } from './session-cookie.js';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readHeaders(request: unknown): Record<string, unknown> {\n if (!isRecord(request)) return {};\n const headers = request.headers;\n return isRecord(headers) ? headers : {};\n}\n\n/** Coerce a single header (string or string[]) to a string, else undefined. */\nfunction headerToString(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (Array.isArray(value) && value.every((part) => typeof part === 'string')) {\n return value.join(',');\n }\n return undefined;\n}\n\n/** Read the raw `Cookie` request header (Express and Fastify both expose `headers`). */\nexport function readCookieHeader(request: unknown): string | undefined {\n return headerToString(readHeaders(request).cookie);\n}\n\n/**\n * Decide whether the cookie should carry `Secure`. True when the request is https directly\n * (`request.protocol === 'https'`) OR a proxy forwarded https (`x-forwarded-proto` includes\n * 'https'). Defensive structural reads — never throws, defaults to insecure (works on plain http\n * in dev).\n */\nexport function isHttpsRequest(request: unknown): boolean {\n if (isRecord(request) && request.protocol === 'https') return true;\n const forwarded = headerToString(readHeaders(request)['x-forwarded-proto']);\n if (forwarded === undefined) return false;\n return forwarded\n .split(',')\n .map((proto) => proto.trim().toLowerCase())\n .includes('https');\n}\n\n/** Attach the verified session onto the raw request so downstream code can read it. */\nexport function attachSession(request: unknown, session: ConsoleSession): void {\n if (!isRecord(request)) return;\n (request as { consoleSession?: ConsoleSession }).consoleSession = session;\n}\n","function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\ninterface RawNodeResponse {\n getHeader(name: string): number | string | string[] | undefined;\n setHeader(name: string, value: string | string[]): unknown;\n}\n\nfunction isRawNodeResponse(value: unknown): value is RawNodeResponse {\n return (\n isRecord(value) &&\n typeof value.getHeader === 'function' &&\n typeof value.setHeader === 'function'\n );\n}\n\n/**\n * Resolve the writable Node `ServerResponse` from a platform response. Express' response IS the\n * Node response; Fastify's reply wraps it on `.raw`. Returns the outer response when it already\n * exposes get/setHeader (Express), otherwise the unwrapped `.raw` (Fastify).\n */\nfunction resolveRawResponse(response: unknown): RawNodeResponse | null {\n if (isRawNodeResponse(response)) return response;\n if (isRecord(response) && isRawNodeResponse(response.raw)) return response.raw;\n return null;\n}\n\nfunction existingSetCookies(response: RawNodeResponse): string[] {\n const current = response.getHeader('set-cookie');\n if (Array.isArray(current)) return current;\n if (typeof current === 'string') return [current];\n return [];\n}\n\n/**\n * Append a `Set-Cookie` header to the response WITHOUT clobbering any cookies already queued by\n * the host. Platform-agnostic raw write — no-ops gracefully if the response can't be unwrapped.\n */\nexport function appendSetCookie(response: unknown, cookie: string): void {\n const raw = resolveRawResponse(response);\n if (!raw) return;\n raw.setHeader('set-cookie', [...existingSetCookies(raw), cookie]);\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** Clock-skew grace applied to `exp` so a marginally-late clock doesn't bounce a valid cookie. */\nconst EXP_GRACE_MS = 30_000;\n\n/** The validated console session attached to a request once its cookie verifies. */\nexport interface ConsoleSession {\n /** Stable user id (the session user's `id`). */\n sub: string;\n /** Optional display name. */\n name?: string;\n /** Free-form role strings; the console does not interpret them. */\n roles: string[];\n /** Issued-at, epoch milliseconds. */\n iat: number;\n /** Expiry, epoch milliseconds. */\n exp: number;\n}\n\n/** The session user a host `login`/`session` hook returns to mint a cookie. */\nexport interface ConsoleSessionUser {\n id: string;\n name?: string;\n roles?: string[];\n}\n\nexport interface SignOptions {\n secret: string;\n ttlMs: number;\n /** Injectable clock (epoch ms) for deterministic tests. Defaults to `Date.now()`. */\n now?: number;\n}\n\nexport interface VerifyOptions {\n secret: string;\n now?: number;\n}\n\nfunction base64urlEncode(value: string): string {\n return Buffer.from(value, 'utf8').toString('base64url');\n}\n\nfunction base64urlDecode(value: string): string {\n return Buffer.from(value, 'base64url').toString('utf8');\n}\n\nfunction hmac(secret: string, payload: string): string {\n return createHmac('sha256', secret).update(payload).digest('base64url');\n}\n\n/**\n * Sign a session into the cookie value `base64url(payload).base64url(hmac)`. Stateless\n * HMAC-SHA256, no store — `node:crypto` only, no JWT dependency. Mirrors the telescope\n * dashboard's session cookie so the console gate behaves identically.\n */\nexport function signSessionCookie(user: ConsoleSessionUser, options: SignOptions): string {\n const issuedAt = options.now ?? Date.now();\n const session: ConsoleSession = {\n sub: user.id,\n ...(user.name !== undefined ? { name: user.name } : {}),\n roles: user.roles ?? [],\n iat: issuedAt,\n exp: issuedAt + options.ttlMs,\n };\n const encodedPayload = base64urlEncode(JSON.stringify(session));\n return `${encodedPayload}.${hmac(options.secret, encodedPayload)}`;\n}\n\n/** Type guard for the decoded payload shape — defends against tampered/legacy payloads. */\nfunction isSessionPayload(value: unknown): value is ConsoleSession {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n if (typeof record.sub !== 'string') return false;\n if (record.name !== undefined && typeof record.name !== 'string') return false;\n if (!Array.isArray(record.roles)) return false;\n if (!record.roles.every((role) => typeof role === 'string')) return false;\n if (typeof record.iat !== 'number' || !Number.isFinite(record.iat)) return false;\n if (typeof record.exp !== 'number' || !Number.isFinite(record.exp)) return false;\n return true;\n}\n\n/**\n * Verify a cookie value and return the session, or `null` for anything tampered, malformed, or\n * expired (past `exp` + a 30s grace). Constant-time signature comparison. NEVER throws — any\n * parse failure yields `null`.\n */\nexport function verifySessionCookie(value: string, options: VerifyOptions): ConsoleSession | null {\n try {\n const dot = value.indexOf('.');\n if (dot <= 0 || dot === value.length - 1) return null;\n const encodedPayload = value.slice(0, dot);\n const provided = Buffer.from(value.slice(dot + 1), 'base64url');\n const expected = Buffer.from(hmac(options.secret, encodedPayload), 'base64url');\n // timingSafeEqual throws on a length mismatch — guard it so a wrong-length (tampered)\n // signature returns null instead of throwing.\n if (provided.length !== expected.length) return null;\n if (!timingSafeEqual(provided, expected)) return null;\n const decoded: unknown = JSON.parse(base64urlDecode(encodedPayload));\n if (!isSessionPayload(decoded)) return null;\n const now = options.now ?? Date.now();\n if (now > decoded.exp + EXP_GRACE_MS) return null;\n return decoded;\n } catch {\n return null;\n }\n}\n","import { serializeSetCookie } from './cookie-header.js';\nimport { isHttpsRequest } from './request.js';\nimport { appendSetCookie } from './response.js';\nimport { type ConsoleSessionUser, signSessionCookie } from './session-cookie.js';\n\n/** Cookie name carrying the signed console session. */\nexport const SESSION_COOKIE_NAME = 'media_console_session';\n\ninterface CookieContext {\n auth: { secret: string; ttlMs: number };\n /** Cookie `Path` — the JSON API base, so the cookie rides every console API request. */\n cookiePath: string;\n request: unknown;\n response: unknown;\n now?: number;\n}\n\n/**\n * Sign a fresh session for `user` and append it as a `Set-Cookie` on the response, scoped to the\n * console API path and `Secure` when the request is https.\n */\nexport function issueSessionCookie(user: ConsoleSessionUser, context: CookieContext): void {\n const value = signSessionCookie(user, {\n secret: context.auth.secret,\n ttlMs: context.auth.ttlMs,\n ...(context.now !== undefined ? { now: context.now } : {}),\n });\n appendSetCookie(\n context.response,\n serializeSetCookie(SESSION_COOKIE_NAME, value, {\n path: context.cookiePath,\n maxAgeSeconds: Math.floor(context.auth.ttlMs / 1000),\n secure: isHttpsRequest(context.request),\n }),\n );\n}\n\n/** Append a cookie-clearing `Set-Cookie` (Max-Age=0) scoped to the console API path. */\nexport function clearSessionCookie(context: Omit<CookieContext, 'auth' | 'now'>): void {\n appendSetCookie(\n context.response,\n serializeSetCookie(SESSION_COOKIE_NAME, '', {\n path: context.cookiePath,\n maxAgeSeconds: 0,\n secure: isHttpsRequest(context.request),\n clear: true,\n }),\n );\n}\n","// Cross-package by-value tokens. These MUST use the exact same `Symbol.for` keys as\n// `@dudousxd/nestjs-media`'s `tokens.ts`. The global symbol registry guarantees identity across\n// packages, so the console resolves the host-provided values without importing the (heavier,\n// controller-carrying) nestjs package — this package depends only on `-core`. Do not change these\n// keys without changing them there too. Mirrors `@dudousxd/nestjs-media-telescope`'s media-tokens.\n\n/** The configured `MediaStore` (or `null`), provided by `MediaModule`. */\nexport const MEDIA_STORE: symbol = Symbol.for('nestjs-media:store');\n/** The configured `UploadSessionStore` (or `null`), provided by `MediaModule`. */\nexport const MEDIA_UPLOAD_SESSIONS: symbol = Symbol.for('nestjs-media:upload-sessions');\n/** The `StorageManager` (alias of `MediaModule`'s internal `MEDIA_STORAGE` token). */\nexport const MEDIA_STORAGE_SHARED: symbol = Symbol.for('nestjs-media:storage');\n\n/** Carries the resolved UI mount base (e.g. `/media`) to the UI controller. */\nexport const MEDIA_DASHBOARD_BASE_PATH: symbol = Symbol('MEDIA_DASHBOARD_BASE_PATH');\n/** Carries the resolved JSON API base (e.g. `/api/media/console`) the SPA fetches from. */\nexport const MEDIA_DASHBOARD_API_PATH: symbol = Symbol('MEDIA_DASHBOARD_API_PATH');\n/** Carries whether destructive action routes were enabled (`options.actions`). */\nexport const MEDIA_DASHBOARD_ACTIONS: symbol = Symbol('MEDIA_DASHBOARD_ACTIONS');\n\n/** Carries the resolved console-auth config (`ResolvedConsoleAuth | null`) to the guard/controller. */\nexport const MEDIA_CONSOLE_AUTH: symbol = Symbol('MEDIA_CONSOLE_AUTH');\n/** Carries the cookie `Path` (the JSON API base) so the session cookie rides every API request. */\nexport const MEDIA_CONSOLE_COOKIE_PATH: symbol = Symbol('MEDIA_CONSOLE_COOKIE_PATH');\n","import type { Readable } from 'node:stream';\nimport type {\n MediaRecord,\n MediaStore,\n StorageDriver,\n StorageManager,\n UploadSession,\n UploadSessionStore,\n} from '@dudousxd/nestjs-media-core';\nimport {\n BadRequestException,\n Inject,\n Injectable,\n NotFoundException,\n Optional,\n} from '@nestjs/common';\nimport type {\n CollectionsResponse,\n DiskListResponse,\n LibraryDetailResponse,\n LibraryListResponse,\n LibraryRecord,\n ObjectDetailResponse,\n ObjectListResponse,\n Topology,\n UploadDetailResponse,\n UploadInfo,\n UploadListResponse,\n} from '../client/types.js';\nimport {\n MEDIA_DASHBOARD_ACTIONS,\n MEDIA_STORAGE_SHARED,\n MEDIA_STORE,\n MEDIA_UPLOAD_SESSIONS,\n} from './tokens.js';\n\n/** Seconds a generated preview/download URL stays valid. */\nconst URL_TTL_SECONDS = 300;\nconst DEFAULT_PAGE_LIMIT = 50;\n\n/** Last path segment of a folder prefix, ignoring a trailing slash. */\nfunction lastSegment(prefix: string): string {\n const trimmed = prefix.replace(/\\/+$/, '');\n const slash = trimmed.lastIndexOf('/');\n return slash === -1 ? trimmed : trimmed.slice(slash + 1);\n}\n\nfunction mapUpload(session: UploadSession): UploadInfo {\n const percent =\n session.size !== undefined && session.size > 0\n ? Math.min(100, Math.round((session.offset / session.size) * 100))\n : null;\n return {\n id: session.id,\n disk: session.disk,\n key: session.key,\n offset: session.offset,\n size: session.size ?? null,\n percent,\n parts: session.parts,\n multipart: session.multipartUploadId !== undefined,\n ...(session.createdAt ? { createdAt: session.createdAt.toISOString() } : {}),\n };\n}\n\nfunction mapRecord(record: MediaRecord): LibraryRecord {\n return {\n id: record.id,\n ownerType: record.ownerType,\n ownerId: record.ownerId,\n collection: record.collection,\n name: record.name,\n fileName: record.fileName,\n mimeType: record.mimeType,\n size: record.size,\n disk: record.disk,\n path: record.path,\n createdAt: record.createdAt.toISOString(),\n };\n}\n\n/**\n * Read + action logic behind the console's JSON API. Resolves the three by-value media tokens\n * with `@Optional()` so a host without a `MediaStore`/upload store still boots — every method\n * degrades to an empty shape instead of throwing when its capability is absent.\n */\n@Injectable()\nexport class MediaConsoleService {\n constructor(\n @Optional() @Inject(MEDIA_STORAGE_SHARED) private readonly storage: StorageManager | null,\n @Optional() @Inject(MEDIA_STORE) private readonly store: MediaStore | null,\n @Optional() @Inject(MEDIA_UPLOAD_SESSIONS) private readonly uploads: UploadSessionStore | null,\n @Optional() @Inject(MEDIA_DASHBOARD_ACTIONS) private readonly actionsEnabled: boolean | null,\n ) {}\n\n private diskOrThrow(disk: string): StorageDriver {\n if (!this.storage || !this.storage.diskNames().includes(disk)) {\n throw new NotFoundException(`Unknown disk: ${disk}`);\n }\n return this.storage.disk(disk);\n }\n\n listDisks(): DiskListResponse {\n const storage = this.storage;\n if (!storage) return { disks: [] };\n const defaultDisk = storage.defaultDisk;\n return {\n disks: storage.diskNames().map((name) => ({\n name,\n default: name === defaultDisk,\n capabilities: storage.disk(name).capabilities,\n })),\n };\n }\n\n async listObjects(\n disk: string,\n options: { prefix?: string; cursor?: string; limit?: number },\n ): Promise<ObjectListResponse> {\n const driver = this.diskOrThrow(disk);\n const result = await driver.list(options.prefix ?? '', {\n delimiter: '/',\n ...(options.cursor ? { cursor: options.cursor } : {}),\n limit: options.limit ?? DEFAULT_PAGE_LIMIT,\n });\n return {\n folders: result.folders.map((prefix) => ({ name: lastSegment(prefix), prefix })),\n // Drop the zero-byte \"folder marker\" (a key ending in `/`, whose name is empty after the\n // prefix) that `createFolder` writes — it's the folder itself, not a file inside it.\n files: result.files\n .filter((entry) => entry.name !== '')\n .map((entry) => ({\n key: entry.key,\n name: entry.name,\n sizeBytes: entry.sizeBytes,\n lastModified: entry.lastModified ? entry.lastModified.toISOString() : null,\n })),\n ...(result.cursor ? { cursor: result.cursor } : {}),\n };\n }\n\n async objectDetail(disk: string, key: string): Promise<ObjectDetailResponse> {\n const driver = this.diskOrThrow(disk);\n const stat = await driver.stat(key);\n const url = driver.capabilities.presign\n ? await driver.temporaryUrl(key, URL_TTL_SECONDS)\n : await driver.url(key);\n return {\n key,\n size: stat.size,\n ...(stat.contentType ? { contentType: stat.contentType } : {}),\n ...(stat.lastModified ? { lastModified: stat.lastModified.toISOString() } : {}),\n url,\n };\n }\n\n /** The object's raw byte stream plus the metadata the controller needs to serve it inline. Used by\n * the console's same-origin preview proxy so text/PDF render in the browser instead of downloading\n * (and so a CORS-locked bucket is still previewable). */\n async objectStream(\n disk: string,\n key: string,\n ): Promise<{ stream: Readable; contentType: string; size: number }> {\n const driver = this.diskOrThrow(disk);\n const stat = await driver.stat(key);\n const stream = await driver.stream(key);\n return { stream, contentType: stat.contentType ?? 'application/octet-stream', size: stat.size };\n }\n\n /** Writes an object from a byte stream (a browser upload). The caller supplies the full key\n * (prefix + filename); an unknown disk 404s via {@link diskOrThrow}. Actions-gated. */\n async putObject(disk: string, key: string, body: Readable, contentType?: string): Promise<void> {\n await this.diskOrThrow(disk).put(key, body, contentType ? { contentType } : undefined);\n }\n\n /** Creates a \"folder\" — a zero-byte marker object at `<prefix>/`, which S3-style listing surfaces\n * as a navigable folder. Normalizes to exactly one trailing slash; rejects an empty prefix. */\n async createFolder(disk: string, prefix: string): Promise<void> {\n const normalized = prefix.replace(/^\\/+/, '').replace(/\\/+$/, '');\n if (normalized === '') throw new BadRequestException('Folder name is required');\n await this.diskOrThrow(disk).put(`${normalized}/`, Buffer.alloc(0));\n }\n\n async deleteObject(disk: string, key: string): Promise<void> {\n await this.diskOrThrow(disk).delete(key);\n }\n\n async copyObject(disk: string, from: string, to: string): Promise<void> {\n await this.diskOrThrow(disk).copy(from, to);\n }\n\n async moveObject(disk: string, from: string, to: string): Promise<void> {\n await this.diskOrThrow(disk).move(from, to);\n }\n\n async listUploads(filter: { disk?: string; prefix?: string }): Promise<UploadListResponse> {\n if (!this.uploads || typeof this.uploads.list !== 'function') return { uploads: [] };\n const sessions = await this.uploads.list({\n ...(filter.disk ? { disk: filter.disk } : {}),\n ...(filter.prefix ? { keyPrefix: filter.prefix } : {}),\n });\n return { uploads: sessions.map(mapUpload) };\n }\n\n async uploadDetail(id: string): Promise<UploadDetailResponse> {\n if (!this.uploads) throw new NotFoundException('No upload store configured');\n const session = await this.uploads.get(id);\n if (!session) throw new NotFoundException(`Unknown upload: ${id}`);\n const parts = this.uploads.listParts ? await this.uploads.listParts(id) : [];\n return { upload: mapUpload(session), parts };\n }\n\n /**\n * Cancels a resumable session by removing its record from the upload store, so it stops\n * appearing as in-progress. NOTE: this does NOT tear down an underlying native multipart upload\n * (e.g. an S3 multipart) or its temporary parts — the decoupled console resolves only the\n * `UploadSessionStore`, not the `ResumableUploadManager` that owns `abort()`. An incomplete\n * multipart is reaped by the bucket's lifecycle policy. Surfaced as \"Cancel session\" in the UI.\n */\n async abortUpload(id: string): Promise<void> {\n if (!this.uploads) throw new NotFoundException('No upload store configured');\n await this.uploads.delete(id);\n }\n\n async listCollections(): Promise<CollectionsResponse> {\n if (!this.store || typeof this.store.aggregate !== 'function') return { collections: [] };\n const buckets = await this.store.aggregate({ groupBy: 'collection', sum: 'size' });\n return { collections: buckets };\n }\n\n async listLibrary(filter: {\n collection?: string;\n disk?: string;\n cursor?: string;\n limit?: number;\n }): Promise<LibraryListResponse> {\n if (!this.store || typeof this.store.list !== 'function') return { records: [] };\n const page = await this.store.list(\n {\n ...(filter.collection ? { collection: filter.collection } : {}),\n ...(filter.disk ? { disk: filter.disk } : {}),\n },\n {\n limit: filter.limit ?? DEFAULT_PAGE_LIMIT,\n ...(filter.cursor ? { cursor: filter.cursor } : {}),\n },\n );\n return {\n records: page.records.map(mapRecord),\n ...(page.cursor ? { cursor: page.cursor } : {}),\n };\n }\n\n async libraryDetail(id: string): Promise<LibraryDetailResponse> {\n if (!this.store) throw new NotFoundException('No media store configured');\n const record = await this.store.find(id);\n if (!record) throw new NotFoundException(`Unknown media record: ${id}`);\n const variants = await Promise.all(\n Object.entries(record.conversions).map(async ([name, conversion]) => {\n const driver = this.storage?.diskNames().includes(conversion.disk)\n ? this.storage.disk(conversion.disk)\n : null;\n const url = driver?.capabilities.presign\n ? await driver.temporaryUrl(conversion.path, URL_TTL_SECONDS)\n : ((await driver?.url(conversion.path)) ?? '');\n return { name, url };\n }),\n );\n return { record: mapRecord(record), variants };\n }\n\n async deleteLibraryRecord(id: string): Promise<void> {\n if (!this.store) throw new NotFoundException('No media store configured');\n await this.store.delete(id);\n }\n\n topology(): Topology {\n return {\n hasStore: this.store !== null && typeof this.store.list === 'function',\n hasUploads: this.uploads !== null && typeof this.uploads.list === 'function',\n disks: this.storage ? this.storage.diskNames().length : 0,\n actions: this.actionsEnabled === true,\n };\n }\n}\n","import {\n BadRequestException,\n Body,\n Controller,\n Get,\n HttpCode,\n Inject,\n Logger,\n NotFoundException,\n Optional,\n Post,\n Req,\n Res,\n UnauthorizedException,\n} from '@nestjs/common';\nimport type { ConsoleAuthOptions, ResolvedConsoleAuth } from './auth/config.js';\nimport { parseCookieHeader } from './auth/cookie-header.js';\nimport { readCookieHeader } from './auth/request.js';\nimport {\n SESSION_COOKIE_NAME,\n clearSessionCookie,\n issueSessionCookie,\n} from './auth/session-cookie-io.js';\nimport { type ConsoleSessionUser, verifySessionCookie } from './auth/session-cookie.js';\nimport { MEDIA_CONSOLE_AUTH, MEDIA_CONSOLE_COOKIE_PATH } from './tokens.js';\n\ninterface LoginBody {\n username?: unknown;\n password?: unknown;\n}\n\n/** Response of `GET /me`: the console SPA renders the login screen or the console from this. */\ntype MeResponse =\n | { authRequired: false }\n | { user: { id: string; name?: string; roles: string[] } };\n\n/**\n * Mints/clears the console session cookie. Deliberately NOT decorated with `MediaConsoleGuard` —\n * these endpoints CREATE the session the gate checks for. When the host didn't configure `auth`,\n * `GET /me` reports `authRequired: false` (the SPA shows the console) and login/logout 404.\n */\n@Controller()\nexport class MediaConsoleAuthController {\n private readonly logger = new Logger(MediaConsoleAuthController.name);\n /** One warn per hook kind, so a flaky hook doesn't spam logs every request. */\n private readonly warnedHooks = new Set<string>();\n\n constructor(\n @Optional() @Inject(MEDIA_CONSOLE_AUTH) private readonly auth: ResolvedConsoleAuth | null,\n @Optional() @Inject(MEDIA_CONSOLE_COOKIE_PATH) private readonly cookiePath: string | null,\n ) {}\n\n @Get('me')\n me(@Req() request: unknown): MeResponse {\n if (!this.auth) return { authRequired: false };\n const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];\n const session =\n cookieValue !== undefined\n ? verifySessionCookie(cookieValue, { secret: this.auth.secret })\n : null;\n // The UNauthenticated SPA learns which login mode(s) to offer from this 401 body.\n if (!session) throw new UnauthorizedException({ auth: { modes: this.auth.modes } });\n return {\n user: {\n id: session.sub,\n ...(session.name !== undefined ? { name: session.name } : {}),\n roles: session.roles,\n },\n };\n }\n\n @Post('login')\n @HttpCode(200)\n async login(\n @Body() body: LoginBody,\n @Req() request: unknown,\n @Res({ passthrough: true }) response: unknown,\n ): Promise<MeResponse> {\n const auth = this.requireAuth();\n if (!auth.login) throw new NotFoundException();\n if (typeof body?.username !== 'string' || typeof body?.password !== 'string') {\n throw new BadRequestException('Body must include string `username` and `password`.');\n }\n const username = body.username;\n const password = body.password;\n // Uniform 401 for unknown user / bad password — no user-enumeration.\n const user = await this.runHook('login', () => auth.login?.(username, password) ?? null);\n if (!user) throw new UnauthorizedException({ message: 'Invalid credentials' });\n return this.mint(user, request, response);\n }\n\n @Post('session')\n @HttpCode(200)\n async session(\n @Req() request: unknown,\n @Res({ passthrough: true }) response: unknown,\n ): Promise<MeResponse> {\n const auth = this.requireAuth();\n if (!auth.session) throw new NotFoundException();\n const user = await this.runHook('session', () => auth.session?.(request) ?? null);\n if (!user) throw new UnauthorizedException();\n return this.mint(user, request, response);\n }\n\n @Post('logout')\n @HttpCode(204)\n logout(@Req() request: unknown, @Res({ passthrough: true }) response: unknown): void {\n // Best-effort: even without auth configured, clearing is harmless.\n clearSessionCookie({ cookiePath: this.cookiePath ?? '/', request, response });\n }\n\n private requireAuth(): ResolvedConsoleAuth {\n if (!this.auth) throw new NotFoundException();\n return this.auth;\n }\n\n private mint(user: ConsoleSessionUser, request: unknown, response: unknown): MeResponse {\n const auth = this.requireAuth();\n issueSessionCookie(user, {\n auth,\n cookiePath: this.cookiePath ?? '/',\n request,\n response,\n });\n return {\n user: {\n id: user.id,\n ...(user.name !== undefined ? { name: user.name } : {}),\n roles: user.roles ?? [],\n },\n };\n }\n\n /** Run a host hook defensively: a throw is a denial (null), warn-logged once per kind. */\n private async runHook(\n kind: string,\n run: () => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null,\n ): Promise<ConsoleSessionUser | null> {\n try {\n return (await run()) ?? null;\n } catch (error) {\n if (!this.warnedHooks.has(kind)) {\n this.warnedHooks.add(kind);\n this.logger.warn(`Console auth ${kind} hook threw; treating as denial. ${String(error)}`);\n }\n return null;\n }\n }\n}\n\nexport type { ConsoleAuthOptions };\n","import { Controller, Get, Inject, Param, Query, StreamableFile, UseGuards } from '@nestjs/common';\nimport type {\n CollectionsResponse,\n DiskListResponse,\n LibraryDetailResponse,\n LibraryListResponse,\n ObjectDetailResponse,\n ObjectListResponse,\n Topology,\n UploadDetailResponse,\n UploadListResponse,\n} from '../client/types.js';\nimport { MediaConsoleGuard } from './media-console.guard.js';\nimport { MediaConsoleService } from './media-console.service.js';\n\n/** Parse an optional numeric query param; undefined when absent or not a finite number. */\nfunction toLimit(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : undefined;\n}\n\n/**\n * Read-only JSON API for the /media console. Bare `@Controller()` — the path prefix is applied by\n * `RouterModule` (set in `MediaDashboardModule.forRoot({ apiBasePath })`). Always mounted.\n * `MediaConsoleGuard` gates it on a session cookie when the host configured `auth` (else a no-op).\n */\n@UseGuards(MediaConsoleGuard)\n@Controller()\nexport class MediaConsoleReadController {\n constructor(@Inject(MediaConsoleService) private readonly service: MediaConsoleService) {}\n\n @Get('disks')\n disks(): DiskListResponse {\n return this.service.listDisks();\n }\n\n @Get('disks/:disk/objects')\n objects(\n @Param('disk') disk: string,\n @Query('prefix') prefix?: string,\n @Query('cursor') cursor?: string,\n @Query('limit') limit?: string,\n ): Promise<ObjectListResponse> {\n const limitValue = toLimit(limit);\n return this.service.listObjects(disk, {\n ...(prefix ? { prefix } : {}),\n ...(cursor ? { cursor } : {}),\n ...(limitValue !== undefined ? { limit: limitValue } : {}),\n });\n }\n\n @Get('disks/:disk/object')\n object(@Param('disk') disk: string, @Query('key') key: string): Promise<ObjectDetailResponse> {\n return this.service.objectDetail(disk, key);\n }\n\n /** Streams the object's bytes inline (Content-Disposition: inline) from the same origin, so the SPA\n * can render text/PDF previews the browser would otherwise download, and read text past CORS. */\n @Get('disks/:disk/object/raw')\n async objectRaw(@Param('disk') disk: string, @Query('key') key: string): Promise<StreamableFile> {\n const { stream, contentType, size } = await this.service.objectStream(disk, key);\n return new StreamableFile(stream, {\n type: contentType,\n disposition: 'inline',\n ...(Number.isFinite(size) ? { length: size } : {}),\n });\n }\n\n @Get('uploads')\n uploads(\n @Query('disk') disk?: string,\n @Query('prefix') prefix?: string,\n ): Promise<UploadListResponse> {\n return this.service.listUploads({\n ...(disk ? { disk } : {}),\n ...(prefix ? { prefix } : {}),\n });\n }\n\n @Get('uploads/:id')\n upload(@Param('id') id: string): Promise<UploadDetailResponse> {\n return this.service.uploadDetail(id);\n }\n\n @Get('library/collections')\n collections(): Promise<CollectionsResponse> {\n return this.service.listCollections();\n }\n\n @Get('library')\n library(\n @Query('collection') collection?: string,\n @Query('disk') disk?: string,\n @Query('cursor') cursor?: string,\n @Query('limit') limit?: string,\n ): Promise<LibraryListResponse> {\n const limitValue = toLimit(limit);\n return this.service.listLibrary({\n ...(collection ? { collection } : {}),\n ...(disk ? { disk } : {}),\n ...(cursor ? { cursor } : {}),\n ...(limitValue !== undefined ? { limit: limitValue } : {}),\n });\n }\n\n @Get('library/:id')\n libraryRecord(@Param('id') id: string): Promise<LibraryDetailResponse> {\n return this.service.libraryDetail(id);\n }\n\n @Get('topology')\n topology(): Topology {\n return this.service.topology();\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { basename, extname, join, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n Controller,\n Get,\n Header,\n Inject,\n NotFoundException,\n Param,\n StreamableFile,\n} from '@nestjs/common';\nimport { MEDIA_DASHBOARD_API_PATH, MEDIA_DASHBOARD_BASE_PATH } from './tokens.js';\n\n/** The base the SPA bundle was built with (Vite `base`); rewritten to the configured base at serve time. */\nconst BUILD_BASE = '/media';\n\n/** dist/server/index.js -> ../spa (the Vite build output). */\nfunction spaDir(): string {\n return fileURLToPath(new URL('../spa', import.meta.url));\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.woff2': 'font/woff2',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Serves the bundled /media console SPA at the configured base (+ hashed assets at `<base>/assets`).\n * The path comes from `RouterModule` (set by `MediaDashboardModule.forRoot({ basePath })`), so the\n * controller routes are relative.\n */\n@Controller()\nexport class MediaDashboardUiController {\n private readonly dir = spaDir();\n\n constructor(\n @Inject(MEDIA_DASHBOARD_BASE_PATH) private readonly basePath: string,\n @Inject(MEDIA_DASHBOARD_API_PATH) private readonly apiBasePath: string,\n ) {}\n\n // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = \"stuck\n // loading after a deploy\"). The hashed assets below are immutable.\n @Get()\n @Header('Content-Type', 'text/html; charset=utf-8')\n @Header('Cache-Control', 'no-store, must-revalidate')\n index(): string {\n const indexPath = join(this.dir, 'index.html');\n if (!existsSync(indexPath)) {\n throw new NotFoundException('Console SPA is not built. Run the package build.');\n }\n // Built with Vite base `/media/`; rewrite asset URLs to the configured base so the SPA loads\n // from `<base>/assets` wherever it's mounted, and tell the client its API base.\n const html = readFileSync(indexPath, 'utf8').replaceAll(\n `=\"${BUILD_BASE}/`,\n `=\"${this.basePath}/`,\n );\n const inject = `<script>window.__MEDIA_BASE__='${this.basePath}';window.__MEDIA_API__='${this.apiBasePath}';</script>`;\n return html.includes('</head>') ? html.replace('</head>', `${inject}</head>`) : inject + html;\n }\n\n @Get('assets/:file')\n @Header('Cache-Control', 'public, max-age=31536000, immutable')\n asset(@Param('file') file: string): StreamableFile {\n const safe = basename(file);\n if (safe !== file) throw new NotFoundException();\n const root = resolve(this.dir, 'assets');\n const assetPath = resolve(root, safe);\n if (!assetPath.startsWith(root + sep) || !existsSync(assetPath)) {\n throw new NotFoundException();\n }\n const type = CONTENT_TYPES[extname(safe)] ?? 'application/octet-stream';\n return new StreamableFile(readFileSync(assetPath), { type });\n }\n}\n"],"mappings":"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","names":["getImportMetaUrl","document","URL","__filename","href","currentScript","src","baseURI","importMetaUrl","import_common","DEFAULT_TTL_MS","DURATION_UNITS","s","m","h","d","durationToMs","ttl","undefined","match","exec","trim","unit","Number","resolveConsoleAuth","options","secret","Error","modes","session","push","login","length","ttlMs","import_common","import_common","parseCookieHeader","header","cookies","segment","split","eq","indexOf","name","slice","trim","rawValue","length","startsWith","endsWith","decodeURIComponent","serializeSetCookie","value","options","parts","clear","encodeURIComponent","push","path","secure","maxAgeSeconds","join","isRecord","value","readHeaders","request","headers","headerToString","Array","isArray","every","part","join","undefined","readCookieHeader","cookie","isHttpsRequest","protocol","forwarded","split","map","proto","trim","toLowerCase","includes","attachSession","session","consoleSession","isRecord","value","isRawNodeResponse","getHeader","setHeader","resolveRawResponse","response","raw","existingSetCookies","current","Array","isArray","appendSetCookie","cookie","EXP_GRACE_MS","base64urlEncode","value","Buffer","from","toString","base64urlDecode","hmac","secret","payload","createHmac","update","digest","signSessionCookie","user","options","issuedAt","now","Date","session","sub","id","name","undefined","roles","iat","exp","ttlMs","encodedPayload","JSON","stringify","isSessionPayload","record","Array","isArray","every","role","Number","isFinite","verifySessionCookie","dot","indexOf","length","slice","provided","expected","timingSafeEqual","decoded","parse","SESSION_COOKIE_NAME","issueSessionCookie","user","context","value","signSessionCookie","secret","auth","ttlMs","now","undefined","appendSetCookie","response","serializeSetCookie","path","cookiePath","maxAgeSeconds","Math","floor","secure","isHttpsRequest","request","clearSessionCookie","clear","MEDIA_STORE","Symbol","for","MEDIA_UPLOAD_SESSIONS","MEDIA_STORAGE_SHARED","MEDIA_DASHBOARD_BASE_PATH","MEDIA_DASHBOARD_API_PATH","MEDIA_DASHBOARD_ACTIONS","MEDIA_CONSOLE_AUTH","MEDIA_CONSOLE_COOKIE_PATH","MediaConsoleGuard","auth","cookiePath","canActivate","context","http","switchToHttp","request","getRequest","session","verifyRequestSession","UnauthorizedException","attachSession","maybeRenew","getResponse","cookieValue","parseCookieHeader","readCookieHeader","SESSION_COOKIE_NAME","undefined","verifySessionCookie","secret","response","now","Date","iat","ttlMs","issueSessionCookie","id","sub","name","roles","import_common","URL_TTL_SECONDS","DEFAULT_PAGE_LIMIT","lastSegment","prefix","trimmed","replace","slash","lastIndexOf","slice","mapUpload","session","percent","size","undefined","Math","min","round","offset","id","disk","key","parts","multipart","multipartUploadId","createdAt","toISOString","mapRecord","record","ownerType","ownerId","collection","name","fileName","mimeType","path","MediaConsoleService","storage","store","uploads","actionsEnabled","diskOrThrow","diskNames","includes","NotFoundException","listDisks","disks","defaultDisk","map","default","capabilities","listObjects","options","driver","result","list","delimiter","cursor","limit","folders","files","filter","entry","sizeBytes","lastModified","objectDetail","stat","url","presign","temporaryUrl","contentType","objectStream","stream","putObject","body","put","createFolder","normalized","BadRequestException","Buffer","alloc","deleteObject","delete","copyObject","from","to","copy","moveObject","move","listUploads","sessions","keyPrefix","uploadDetail","get","listParts","upload","abortUpload","listCollections","aggregate","collections","buckets","groupBy","sum","listLibrary","records","page","libraryDetail","find","variants","Promise","all","Object","entries","conversions","conversion","deleteLibraryRecord","topology","hasStore","hasUploads","length","actions","MediaConsoleActionsController","service","deleteObject","disk","key","copyObject","body","from","to","moveObject","uploadObject","request","type","putObject","createFolder","prefix","abortUpload","id","deleteLibraryRecord","import_common","MediaConsoleAuthController","logger","Logger","name","warnedHooks","Set","auth","cookiePath","me","request","authRequired","cookieValue","parseCookieHeader","readCookieHeader","SESSION_COOKIE_NAME","session","undefined","verifySessionCookie","secret","UnauthorizedException","modes","user","id","sub","roles","login","body","response","requireAuth","NotFoundException","username","password","BadRequestException","runHook","message","mint","logout","clearSessionCookie","issueSessionCookie","kind","run","error","has","add","warn","String","passthrough","import_common","toLimit","value","undefined","parsed","Number","isFinite","Math","floor","MediaConsoleReadController","service","disks","listDisks","objects","disk","prefix","cursor","limit","limitValue","listObjects","object","key","objectDetail","objectRaw","stream","contentType","size","objectStream","StreamableFile","type","disposition","length","uploads","listUploads","upload","id","uploadDetail","collections","listCollections","library","collection","listLibrary","libraryRecord","libraryDetail","topology","MediaConsoleApiModule","register","options","module","imports","controllers","MediaConsoleReadController","MediaConsoleAuthController","actions","MediaConsoleActionsController","providers","MediaConsoleService","MediaConsoleGuard","provide","MEDIA_DASHBOARD_ACTIONS","useValue","MEDIA_CONSOLE_COOKIE_PATH","cookiePath","authProvider","exports","import_common","BUILD_BASE","spaDir","fileURLToPath","URL","CONTENT_TYPES","MediaDashboardUiController","dir","basePath","apiBasePath","index","indexPath","join","existsSync","NotFoundException","html","readFileSync","replaceAll","inject","includes","replace","asset","file","safe","basename","root","resolve","assetPath","startsWith","sep","type","extname","StreamableFile","normalize","path","replace","MediaDashboardModule","forRoot","options","apiBasePath","basePath","build","provide","MEDIA_CONSOLE_AUTH","useValue","resolveConsoleAuth","auth","forRootAsync","authProvider","inject","useFactory","deps","useAuth","imports","actions","module","MediaConsoleApiModule","register","cookiePath","RouterModule","controllers","MediaDashboardUiController","providers","MEDIA_DASHBOARD_BASE_PATH","MEDIA_DASHBOARD_API_PATH","exports"]}
1
+ {"version":3,"sources":["../../src/server/index.ts","../../../../node_modules/.pnpm/tsup@8.3.5_@swc+core@1.15.41_jiti@1.21.7_postcss@8.4.49_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js","../../src/server/media-dashboard.module.ts","../../src/server/auth/config.ts","../../src/server/media-console-api.module.ts","../../src/server/media-console-actions.controller.ts","../../src/server/media-console.guard.ts","../../src/server/auth/cookie-header.ts","../../src/server/auth/request.ts","../../src/server/auth/response.ts","../../src/server/auth/session-cookie.ts","../../src/server/auth/session-cookie-io.ts","../../src/server/tokens.ts","../../src/server/media-console.service.ts","../../src/server/media-console-auth.controller.ts","../../src/server/media-console-read.controller.ts","../../src/server/media-dashboard-ui.controller.ts"],"sourcesContent":["export {\n MediaDashboardModule,\n type MediaDashboardOptions,\n type MediaDashboardAsyncOptions,\n} from './media-dashboard.module.js';\nexport { MediaConsoleApiModule } from './media-console-api.module.js';\nexport { MediaConsoleService } from './media-console.service.js';\nexport type {\n ConsoleAuthOptions,\n LoginHook,\n SessionHook,\n} from './auth/config.js';\nexport type { ConsoleSessionUser } from './auth/session-cookie.js';\nexport {\n MEDIA_STORAGE_SHARED,\n MEDIA_STORE,\n MEDIA_UPLOAD_SESSIONS,\n} from './tokens.js';\n// The API response types (also published at the `./client` entry) for host reuse.\nexport type * from '../client/types.js';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import {\n type DynamicModule,\n Module,\n type ModuleMetadata,\n type OptionalFactoryDependency,\n type Provider,\n} from '@nestjs/common';\nimport type { InjectionToken } from '@nestjs/common';\nimport { RouterModule } from '@nestjs/core';\nimport { type ConsoleAuthOptions, resolveConsoleAuth } from './auth/config.js';\nimport { MediaConsoleApiModule } from './media-console-api.module.js';\nimport { MediaDashboardUiController } from './media-dashboard-ui.controller.js';\nimport {\n MEDIA_CONSOLE_AUTH,\n MEDIA_DASHBOARD_API_PATH,\n MEDIA_DASHBOARD_BASE_PATH,\n} from './tokens.js';\n\nexport interface MediaDashboardOptions {\n /**\n * Where the SPA (UI) is served. Default `/media`. A page route — keep it out of an `/api`\n * prefix so it reads as a UI, not an endpoint.\n */\n basePath?: string;\n /**\n * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under\n * your app's `/api` prefix — e.g. `/api/media/console` — so the API inherits the app's\n * auth/proxy rules while the UI stays at `basePath`.\n */\n apiBasePath?: string;\n /**\n * Enable the destructive endpoints (delete object/record, copy/move, abort upload). Default\n * `false` — the read API is always available. Front the mount with your own guard either way.\n */\n actions?: boolean;\n /**\n * Gate the console (SPA + API) behind a built-in session-cookie login, telescope-style. Omit to\n * leave the console open (front it with your own guard). When set, the SPA renders a login screen\n * until a valid cookie exists; supply a `login(username, password)` and/or `session(request)`\n * hook that returns a session user (or `null` to deny) — see {@link ConsoleAuthOptions}.\n */\n auth?: ConsoleAuthOptions;\n}\n\n/**\n * Async variant of {@link MediaDashboardOptions}. The mount paths + `actions` stay static (the\n * router needs them at module-definition time), but `auth` is built by an injected factory — so\n * the `login`/`session` hooks can reach your DB/services (e.g. an EntityManager to validate an\n * admin). Returning `undefined` from the factory leaves the console open.\n */\nexport interface MediaDashboardAsyncOptions {\n basePath?: string;\n apiBasePath?: string;\n actions?: boolean;\n /** Modules exporting the providers `inject` needs (omit when they're global). */\n imports?: ModuleMetadata['imports'];\n /** Providers injected into `useAuth`, in order. */\n inject?: Array<InjectionToken | OptionalFactoryDependency>;\n /** Build the `auth` config from injected deps (or `undefined` to leave the console open). */\n useAuth: (\n ...deps: any[]\n ) => ConsoleAuthOptions | undefined | Promise<ConsoleAuthOptions | undefined>;\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n\n/**\n * Mounts the /media console: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`\n * (default `<basePath>/api`). Import via `MediaDashboardModule.forRoot(...)` (or `forRootAsync` when\n * the `auth` hooks need injected services) alongside `MediaModule` (global), so it resolves the\n * storage/store/upload tokens. Exclude the UI/API paths from any global `/api` prefix.\n */\n@Module({})\nexport class MediaDashboardModule {\n static forRoot(options: MediaDashboardOptions = {}): DynamicModule {\n const apiBasePath = normalize(\n options.apiBasePath ?? `${normalize(options.basePath ?? '/media')}/api`,\n );\n return MediaDashboardModule.build(options, apiBasePath, {\n provide: MEDIA_CONSOLE_AUTH,\n useValue: resolveConsoleAuth(options.auth),\n });\n }\n\n static forRootAsync(options: MediaDashboardAsyncOptions): DynamicModule {\n const apiBasePath = normalize(\n options.apiBasePath ?? `${normalize(options.basePath ?? '/media')}/api`,\n );\n const authProvider: Provider = {\n provide: MEDIA_CONSOLE_AUTH,\n inject: options.inject ?? [],\n useFactory: async (...deps: any[]) => resolveConsoleAuth(await options.useAuth(...deps)),\n };\n return MediaDashboardModule.build(options, apiBasePath, authProvider, options.imports);\n }\n\n /** Shared wiring: static routing + the API module, with `auth` supplied by the given provider. */\n private static build(\n options: { basePath?: string; apiBasePath?: string; actions?: boolean },\n apiBasePath: string,\n authProvider: Provider,\n imports?: ModuleMetadata['imports'],\n ): DynamicModule {\n const basePath = normalize(options.basePath ?? '/media');\n const actions = options.actions === true;\n return {\n module: MediaDashboardModule,\n imports: [\n MediaConsoleApiModule.register({ actions, cookiePath: apiBasePath, authProvider, imports }),\n RouterModule.register([\n { path: basePath, module: MediaDashboardModule }, // the UI controller below\n { path: apiBasePath, module: MediaConsoleApiModule },\n ]),\n ],\n controllers: [MediaDashboardUiController],\n providers: [\n { provide: MEDIA_DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: MEDIA_DASHBOARD_API_PATH, useValue: apiBasePath },\n ],\n // Re-export the API module so its MediaConsoleService reaches importers if they want it.\n exports: [MediaConsoleApiModule],\n };\n }\n}\n","import type { ConsoleSessionUser } from './session-cookie.js';\n\n/** Host hook for Mode A — validates the host's own auth on the raw request. */\nexport type SessionHook = (\n request: unknown,\n) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;\n\n/** Host hook for Mode B — validates submitted credentials from the built-in login screen. */\nexport type LoginHook = (\n username: string,\n password: string,\n) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;\n\n/** Author-facing `auth` option on `MediaDashboardModule.forRoot`. Mirrors telescope's dashboardAuth. */\nexport interface ConsoleAuthOptions {\n /** REQUIRED HMAC-SHA256 signing key. Missing/empty => boot error (fail closed). */\n secret: string;\n /** Cookie TTL as a duration string (`'8h'`, `'30m'`, `'7d'`). Default `'8h'`. */\n ttl?: string;\n /** Mode A: validate the host's own auth on the raw request. */\n session?: SessionHook;\n /** Mode B: validate credentials from the built-in login screen. */\n login?: LoginHook;\n}\n\nexport type AuthMode = 'session' | 'login';\n\n/** Resolved, validated console-auth config shared by the guard, controller, and SPA. */\nexport interface ResolvedConsoleAuth {\n secret: string;\n ttlMs: number;\n modes: AuthMode[];\n session?: SessionHook;\n login?: LoginHook;\n}\n\nconst DEFAULT_TTL_MS = 8 * 60 * 60 * 1000;\nconst DURATION_UNITS: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n};\n\n/** Parse a `'<number><s|m|h|d>'` duration to ms; falls back to the 8h default on a bad value. */\nfunction durationToMs(ttl: string | undefined): number {\n if (ttl === undefined) return DEFAULT_TTL_MS;\n const match = /^(\\d+)([smhd])$/.exec(ttl.trim());\n if (!match) return DEFAULT_TTL_MS;\n const unit = DURATION_UNITS[match[2] ?? ''];\n if (unit === undefined) return DEFAULT_TTL_MS;\n return Number(match[1]) * unit;\n}\n\n/**\n * Validate + resolve the `auth` option. Returns `null` when unconfigured (the console stays open —\n * front it with your own guard). Throws at boot (fail closed) when configured but missing a secret\n * or any hook — the host learns immediately rather than shipping an un-mintable gate.\n */\nexport function resolveConsoleAuth(\n options: ConsoleAuthOptions | undefined,\n): ResolvedConsoleAuth | null {\n if (!options) return null;\n if (!options.secret) {\n throw new Error('MediaDashboardModule: auth.secret is required when `auth` is configured.');\n }\n const modes: AuthMode[] = [];\n if (options.session) modes.push('session');\n if (options.login) modes.push('login');\n if (modes.length === 0) {\n throw new Error('MediaDashboardModule: auth needs a `login` and/or `session` hook.');\n }\n return {\n secret: options.secret,\n ttlMs: durationToMs(options.ttl),\n modes,\n ...(options.session ? { session: options.session } : {}),\n ...(options.login ? { login: options.login } : {}),\n };\n}\n","import { type DynamicModule, Module, type ModuleMetadata, type Provider } from '@nestjs/common';\nimport { MediaConsoleActionsController } from './media-console-actions.controller.js';\nimport { MediaConsoleAuthController } from './media-console-auth.controller.js';\nimport { MediaConsoleReadController } from './media-console-read.controller.js';\nimport { MediaConsoleGuard } from './media-console.guard.js';\nimport { MediaConsoleService } from './media-console.service.js';\nimport { MEDIA_CONSOLE_COOKIE_PATH, MEDIA_DASHBOARD_ACTIONS } from './tokens.js';\n\ninterface ApiModuleOptions {\n actions: boolean;\n /** Cookie `Path` — the JSON API base — so the session cookie rides every console API request. */\n cookiePath: string;\n /** Provider for `MEDIA_CONSOLE_AUTH` — a `useValue` (forRoot) or a `useFactory` (forRootAsync). */\n authProvider: Provider;\n /** Extra imports the auth factory's `inject` deps live in (empty when they're global). */\n imports?: ModuleMetadata['imports'];\n}\n\n/**\n * Holds the console's JSON API: the read controller (always), the actions controller (only when\n * `actions: true`), and the auth controller (always — it mints the session the guard checks, so it\n * is never itself guarded). The read/action controllers carry `MediaConsoleGuard`, a no-op unless\n * the host configured `auth`.\n */\n@Module({})\nexport class MediaConsoleApiModule {\n static register(options: ApiModuleOptions): DynamicModule {\n return {\n module: MediaConsoleApiModule,\n imports: options.imports ?? [],\n controllers: [\n MediaConsoleReadController,\n MediaConsoleAuthController,\n ...(options.actions ? [MediaConsoleActionsController] : []),\n ],\n providers: [\n MediaConsoleService,\n MediaConsoleGuard,\n { provide: MEDIA_DASHBOARD_ACTIONS, useValue: options.actions },\n { provide: MEDIA_CONSOLE_COOKIE_PATH, useValue: options.cookiePath },\n options.authProvider,\n ],\n exports: [MediaConsoleService],\n };\n }\n}\n","import type { Readable } from 'node:stream';\nimport {\n Body,\n Controller,\n Delete,\n HttpCode,\n Inject,\n Param,\n Post,\n Query,\n Req,\n UseGuards,\n} from '@nestjs/common';\nimport { MediaConsoleGuard } from './media-console.guard.js';\nimport { MediaConsoleService } from './media-console.service.js';\n\ninterface CopyMoveBody {\n from: string;\n to: string;\n}\n\ninterface CreateFolderBody {\n prefix: string;\n}\n\n/**\n * Destructive JSON API for the /media console. Only registered when the host opts in with\n * `MediaDashboardModule.forRoot({ actions: true })` (default off). Bare `@Controller()` — the path\n * prefix comes from `RouterModule`, sharing the API base with the read controller. Gated by\n * `MediaConsoleGuard` (session cookie) when the host configured `auth`.\n */\n@UseGuards(MediaConsoleGuard)\n@Controller()\nexport class MediaConsoleActionsController {\n constructor(@Inject(MediaConsoleService) private readonly service: MediaConsoleService) {}\n\n @Delete('disks/:disk/object')\n @HttpCode(204)\n deleteObject(@Param('disk') disk: string, @Query('key') key: string): Promise<void> {\n return this.service.deleteObject(disk, key);\n }\n\n @Post('disks/:disk/copy')\n @HttpCode(204)\n copyObject(@Param('disk') disk: string, @Body() body: CopyMoveBody): Promise<void> {\n return this.service.copyObject(disk, body.from, body.to);\n }\n\n @Post('disks/:disk/move')\n @HttpCode(204)\n moveObject(@Param('disk') disk: string, @Body() body: CopyMoveBody): Promise<void> {\n return this.service.moveObject(disk, body.from, body.to);\n }\n\n /**\n * Uploads a file to `key` on the disk. The body is the raw bytes sent as `application/octet-stream`\n * so the host's JSON/urlencoded body parsers never consume the stream; the real MIME rides as the\n * `type` query param and becomes the stored object's Content-Type. `@Req()` is the request stream.\n */\n @Post('disks/:disk/upload')\n @HttpCode(204)\n uploadObject(\n @Param('disk') disk: string,\n @Query('key') key: string,\n @Req() request: Readable,\n @Query('type') type?: string,\n ): Promise<void> {\n return this.service.putObject(disk, key, request, type);\n }\n\n @Post('disks/:disk/folder')\n @HttpCode(204)\n createFolder(@Param('disk') disk: string, @Body() body: CreateFolderBody): Promise<void> {\n return this.service.createFolder(disk, body.prefix);\n }\n\n @Delete('disks/:disk/folder')\n @HttpCode(204)\n deleteFolder(@Param('disk') disk: string, @Query('prefix') prefix: string): Promise<void> {\n return this.service.deleteFolder(disk, prefix);\n }\n\n @Post('uploads/:id/abort')\n @HttpCode(204)\n abortUpload(@Param('id') id: string): Promise<void> {\n return this.service.abortUpload(id);\n }\n\n @Delete('library/:id')\n @HttpCode(204)\n deleteLibraryRecord(@Param('id') id: string): Promise<void> {\n return this.service.deleteLibraryRecord(id);\n }\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n Inject,\n Injectable,\n Optional,\n UnauthorizedException,\n} from '@nestjs/common';\nimport type { ResolvedConsoleAuth } from './auth/config.js';\nimport { parseCookieHeader } from './auth/cookie-header.js';\nimport { attachSession, readCookieHeader } from './auth/request.js';\nimport { SESSION_COOKIE_NAME, issueSessionCookie } from './auth/session-cookie-io.js';\nimport { type ConsoleSession, verifySessionCookie } from './auth/session-cookie.js';\nimport { MEDIA_CONSOLE_AUTH, MEDIA_CONSOLE_COOKIE_PATH } from './tokens.js';\n\n/**\n * Gates the console's read + action controllers on a valid session cookie — but ONLY when the host\n * configured `auth`. With no auth configured the resolved value is `null` and the guard is a\n * no-op (the console stays open; front it with your own guard). The auth controller that MINTS the\n * cookie is deliberately NOT decorated with this guard.\n */\n@Injectable()\nexport class MediaConsoleGuard implements CanActivate {\n constructor(\n @Optional() @Inject(MEDIA_CONSOLE_AUTH) private readonly auth: ResolvedConsoleAuth | null,\n @Optional() @Inject(MEDIA_CONSOLE_COOKIE_PATH) private readonly cookiePath: string | null,\n ) {}\n\n canActivate(context: ExecutionContext): boolean {\n if (!this.auth) return true;\n const http = context.switchToHttp();\n const request = http.getRequest();\n const session = this.verifyRequestSession(request);\n // Absent/invalid/expired cookie => 401 (not 403): the SPA reads this as \"show the login screen\".\n if (!session) throw new UnauthorizedException();\n attachSession(request, session);\n this.maybeRenew(http.getResponse(), request, session);\n return true;\n }\n\n private verifyRequestSession(request: unknown): ConsoleSession | null {\n if (!this.auth) return null;\n const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];\n if (cookieValue === undefined) return null;\n return verifySessionCookie(cookieValue, { secret: this.auth.secret });\n }\n\n /**\n * Sliding renewal: when a valid cookie is past half its TTL, re-issue a fresh one so active users\n * never get logged out mid-session. Appends a new Set-Cookie (preserving any others already set).\n */\n private maybeRenew(response: unknown, request: unknown, session: ConsoleSession): void {\n if (!this.auth) return;\n const now = Date.now();\n if (now - session.iat <= this.auth.ttlMs / 2) return;\n issueSessionCookie(\n {\n id: session.sub,\n ...(session.name !== undefined ? { name: session.name } : {}),\n roles: session.roles,\n },\n {\n auth: this.auth,\n cookiePath: this.cookiePath ?? '/',\n request,\n response,\n now,\n },\n );\n }\n}\n","/**\n * Parse a raw `Cookie` request header into a name→value map. Dependency-free so it works on raw\n * Express AND Fastify requests without `cookie-parser`. Values are URL-decoded; malformed segments\n * are skipped, never thrown.\n */\nexport function parseCookieHeader(header: string | undefined): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (typeof header !== 'string' || header === '') return cookies;\n for (const segment of header.split(';')) {\n const eq = segment.indexOf('=');\n if (eq <= 0) continue;\n const name = segment.slice(0, eq).trim();\n if (name === '') continue;\n let rawValue = segment.slice(eq + 1).trim();\n // Strip a single pair of wrapping double-quotes (RFC 6265 quoted form).\n if (rawValue.length >= 2 && rawValue.startsWith('\"') && rawValue.endsWith('\"')) {\n rawValue = rawValue.slice(1, -1);\n }\n // First occurrence wins; don't clobber an earlier value with a later dup.\n if (name in cookies) continue;\n try {\n cookies[name] = decodeURIComponent(rawValue);\n } catch {\n cookies[name] = rawValue;\n }\n }\n return cookies;\n}\n\nexport interface SetCookieOptions {\n path: string;\n maxAgeSeconds: number;\n secure: boolean;\n clear?: boolean;\n}\n\n/**\n * Serialize a `Set-Cookie` header value. Always `HttpOnly` + `SameSite=Lax` (Lax blocks\n * cross-site POSTs carrying the cookie — CSRF coverage for the console's mutation POSTs).\n * Platform-agnostic: just a string.\n */\nexport function serializeSetCookie(name: string, value: string, options: SetCookieOptions): string {\n const parts = [`${name}=${options.clear ? '' : encodeURIComponent(value)}`];\n parts.push(`Path=${options.path}`);\n parts.push('HttpOnly');\n parts.push('SameSite=Lax');\n if (options.secure) parts.push('Secure');\n if (options.clear) {\n parts.push('Max-Age=0');\n parts.push('Expires=Thu, 01 Jan 1970 00:00:00 GMT');\n } else {\n parts.push(`Max-Age=${options.maxAgeSeconds}`);\n }\n return parts.join('; ');\n}\n","import type { ConsoleSession } from './session-cookie.js';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readHeaders(request: unknown): Record<string, unknown> {\n if (!isRecord(request)) return {};\n const headers = request.headers;\n return isRecord(headers) ? headers : {};\n}\n\n/** Coerce a single header (string or string[]) to a string, else undefined. */\nfunction headerToString(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (Array.isArray(value) && value.every((part) => typeof part === 'string')) {\n return value.join(',');\n }\n return undefined;\n}\n\n/** Read the raw `Cookie` request header (Express and Fastify both expose `headers`). */\nexport function readCookieHeader(request: unknown): string | undefined {\n return headerToString(readHeaders(request).cookie);\n}\n\n/**\n * Decide whether the cookie should carry `Secure`. True when the request is https directly\n * (`request.protocol === 'https'`) OR a proxy forwarded https (`x-forwarded-proto` includes\n * 'https'). Defensive structural reads — never throws, defaults to insecure (works on plain http\n * in dev).\n */\nexport function isHttpsRequest(request: unknown): boolean {\n if (isRecord(request) && request.protocol === 'https') return true;\n const forwarded = headerToString(readHeaders(request)['x-forwarded-proto']);\n if (forwarded === undefined) return false;\n return forwarded\n .split(',')\n .map((proto) => proto.trim().toLowerCase())\n .includes('https');\n}\n\n/** Attach the verified session onto the raw request so downstream code can read it. */\nexport function attachSession(request: unknown, session: ConsoleSession): void {\n if (!isRecord(request)) return;\n (request as { consoleSession?: ConsoleSession }).consoleSession = session;\n}\n","function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\ninterface RawNodeResponse {\n getHeader(name: string): number | string | string[] | undefined;\n setHeader(name: string, value: string | string[]): unknown;\n}\n\nfunction isRawNodeResponse(value: unknown): value is RawNodeResponse {\n return (\n isRecord(value) &&\n typeof value.getHeader === 'function' &&\n typeof value.setHeader === 'function'\n );\n}\n\n/**\n * Resolve the writable Node `ServerResponse` from a platform response. Express' response IS the\n * Node response; Fastify's reply wraps it on `.raw`. Returns the outer response when it already\n * exposes get/setHeader (Express), otherwise the unwrapped `.raw` (Fastify).\n */\nfunction resolveRawResponse(response: unknown): RawNodeResponse | null {\n if (isRawNodeResponse(response)) return response;\n if (isRecord(response) && isRawNodeResponse(response.raw)) return response.raw;\n return null;\n}\n\nfunction existingSetCookies(response: RawNodeResponse): string[] {\n const current = response.getHeader('set-cookie');\n if (Array.isArray(current)) return current;\n if (typeof current === 'string') return [current];\n return [];\n}\n\n/**\n * Append a `Set-Cookie` header to the response WITHOUT clobbering any cookies already queued by\n * the host. Platform-agnostic raw write — no-ops gracefully if the response can't be unwrapped.\n */\nexport function appendSetCookie(response: unknown, cookie: string): void {\n const raw = resolveRawResponse(response);\n if (!raw) return;\n raw.setHeader('set-cookie', [...existingSetCookies(raw), cookie]);\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** Clock-skew grace applied to `exp` so a marginally-late clock doesn't bounce a valid cookie. */\nconst EXP_GRACE_MS = 30_000;\n\n/** The validated console session attached to a request once its cookie verifies. */\nexport interface ConsoleSession {\n /** Stable user id (the session user's `id`). */\n sub: string;\n /** Optional display name. */\n name?: string;\n /** Free-form role strings; the console does not interpret them. */\n roles: string[];\n /** Issued-at, epoch milliseconds. */\n iat: number;\n /** Expiry, epoch milliseconds. */\n exp: number;\n}\n\n/** The session user a host `login`/`session` hook returns to mint a cookie. */\nexport interface ConsoleSessionUser {\n id: string;\n name?: string;\n roles?: string[];\n}\n\nexport interface SignOptions {\n secret: string;\n ttlMs: number;\n /** Injectable clock (epoch ms) for deterministic tests. Defaults to `Date.now()`. */\n now?: number;\n}\n\nexport interface VerifyOptions {\n secret: string;\n now?: number;\n}\n\nfunction base64urlEncode(value: string): string {\n return Buffer.from(value, 'utf8').toString('base64url');\n}\n\nfunction base64urlDecode(value: string): string {\n return Buffer.from(value, 'base64url').toString('utf8');\n}\n\nfunction hmac(secret: string, payload: string): string {\n return createHmac('sha256', secret).update(payload).digest('base64url');\n}\n\n/**\n * Sign a session into the cookie value `base64url(payload).base64url(hmac)`. Stateless\n * HMAC-SHA256, no store — `node:crypto` only, no JWT dependency. Mirrors the telescope\n * dashboard's session cookie so the console gate behaves identically.\n */\nexport function signSessionCookie(user: ConsoleSessionUser, options: SignOptions): string {\n const issuedAt = options.now ?? Date.now();\n const session: ConsoleSession = {\n sub: user.id,\n ...(user.name !== undefined ? { name: user.name } : {}),\n roles: user.roles ?? [],\n iat: issuedAt,\n exp: issuedAt + options.ttlMs,\n };\n const encodedPayload = base64urlEncode(JSON.stringify(session));\n return `${encodedPayload}.${hmac(options.secret, encodedPayload)}`;\n}\n\n/** Type guard for the decoded payload shape — defends against tampered/legacy payloads. */\nfunction isSessionPayload(value: unknown): value is ConsoleSession {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n if (typeof record.sub !== 'string') return false;\n if (record.name !== undefined && typeof record.name !== 'string') return false;\n if (!Array.isArray(record.roles)) return false;\n if (!record.roles.every((role) => typeof role === 'string')) return false;\n if (typeof record.iat !== 'number' || !Number.isFinite(record.iat)) return false;\n if (typeof record.exp !== 'number' || !Number.isFinite(record.exp)) return false;\n return true;\n}\n\n/**\n * Verify a cookie value and return the session, or `null` for anything tampered, malformed, or\n * expired (past `exp` + a 30s grace). Constant-time signature comparison. NEVER throws — any\n * parse failure yields `null`.\n */\nexport function verifySessionCookie(value: string, options: VerifyOptions): ConsoleSession | null {\n try {\n const dot = value.indexOf('.');\n if (dot <= 0 || dot === value.length - 1) return null;\n const encodedPayload = value.slice(0, dot);\n const provided = Buffer.from(value.slice(dot + 1), 'base64url');\n const expected = Buffer.from(hmac(options.secret, encodedPayload), 'base64url');\n // timingSafeEqual throws on a length mismatch — guard it so a wrong-length (tampered)\n // signature returns null instead of throwing.\n if (provided.length !== expected.length) return null;\n if (!timingSafeEqual(provided, expected)) return null;\n const decoded: unknown = JSON.parse(base64urlDecode(encodedPayload));\n if (!isSessionPayload(decoded)) return null;\n const now = options.now ?? Date.now();\n if (now > decoded.exp + EXP_GRACE_MS) return null;\n return decoded;\n } catch {\n return null;\n }\n}\n","import { serializeSetCookie } from './cookie-header.js';\nimport { isHttpsRequest } from './request.js';\nimport { appendSetCookie } from './response.js';\nimport { type ConsoleSessionUser, signSessionCookie } from './session-cookie.js';\n\n/** Cookie name carrying the signed console session. */\nexport const SESSION_COOKIE_NAME = 'media_console_session';\n\ninterface CookieContext {\n auth: { secret: string; ttlMs: number };\n /** Cookie `Path` — the JSON API base, so the cookie rides every console API request. */\n cookiePath: string;\n request: unknown;\n response: unknown;\n now?: number;\n}\n\n/**\n * Sign a fresh session for `user` and append it as a `Set-Cookie` on the response, scoped to the\n * console API path and `Secure` when the request is https.\n */\nexport function issueSessionCookie(user: ConsoleSessionUser, context: CookieContext): void {\n const value = signSessionCookie(user, {\n secret: context.auth.secret,\n ttlMs: context.auth.ttlMs,\n ...(context.now !== undefined ? { now: context.now } : {}),\n });\n appendSetCookie(\n context.response,\n serializeSetCookie(SESSION_COOKIE_NAME, value, {\n path: context.cookiePath,\n maxAgeSeconds: Math.floor(context.auth.ttlMs / 1000),\n secure: isHttpsRequest(context.request),\n }),\n );\n}\n\n/** Append a cookie-clearing `Set-Cookie` (Max-Age=0) scoped to the console API path. */\nexport function clearSessionCookie(context: Omit<CookieContext, 'auth' | 'now'>): void {\n appendSetCookie(\n context.response,\n serializeSetCookie(SESSION_COOKIE_NAME, '', {\n path: context.cookiePath,\n maxAgeSeconds: 0,\n secure: isHttpsRequest(context.request),\n clear: true,\n }),\n );\n}\n","// Cross-package by-value tokens. These MUST use the exact same `Symbol.for` keys as\n// `@dudousxd/nestjs-media`'s `tokens.ts`. The global symbol registry guarantees identity across\n// packages, so the console resolves the host-provided values without importing the (heavier,\n// controller-carrying) nestjs package — this package depends only on `-core`. Do not change these\n// keys without changing them there too. Mirrors `@dudousxd/nestjs-media-telescope`'s media-tokens.\n\n/** The configured `MediaStore` (or `null`), provided by `MediaModule`. */\nexport const MEDIA_STORE: symbol = Symbol.for('nestjs-media:store');\n/** The configured `UploadSessionStore` (or `null`), provided by `MediaModule`. */\nexport const MEDIA_UPLOAD_SESSIONS: symbol = Symbol.for('nestjs-media:upload-sessions');\n/** The `StorageManager` (alias of `MediaModule`'s internal `MEDIA_STORAGE` token). */\nexport const MEDIA_STORAGE_SHARED: symbol = Symbol.for('nestjs-media:storage');\n\n/** Carries the resolved UI mount base (e.g. `/media`) to the UI controller. */\nexport const MEDIA_DASHBOARD_BASE_PATH: symbol = Symbol('MEDIA_DASHBOARD_BASE_PATH');\n/** Carries the resolved JSON API base (e.g. `/api/media/console`) the SPA fetches from. */\nexport const MEDIA_DASHBOARD_API_PATH: symbol = Symbol('MEDIA_DASHBOARD_API_PATH');\n/** Carries whether destructive action routes were enabled (`options.actions`). */\nexport const MEDIA_DASHBOARD_ACTIONS: symbol = Symbol('MEDIA_DASHBOARD_ACTIONS');\n\n/** Carries the resolved console-auth config (`ResolvedConsoleAuth | null`) to the guard/controller. */\nexport const MEDIA_CONSOLE_AUTH: symbol = Symbol('MEDIA_CONSOLE_AUTH');\n/** Carries the cookie `Path` (the JSON API base) so the session cookie rides every API request. */\nexport const MEDIA_CONSOLE_COOKIE_PATH: symbol = Symbol('MEDIA_CONSOLE_COOKIE_PATH');\n","import type { Readable } from 'node:stream';\nimport type {\n MediaRecord,\n MediaStore,\n StorageDriver,\n StorageManager,\n UploadSession,\n UploadSessionStore,\n} from '@dudousxd/nestjs-media-core';\nimport {\n BadRequestException,\n Inject,\n Injectable,\n NotFoundException,\n Optional,\n PayloadTooLargeException,\n} from '@nestjs/common';\nimport type {\n CollectionsResponse,\n DiskListResponse,\n LibraryDetailResponse,\n LibraryListResponse,\n LibraryRecord,\n ObjectDetailResponse,\n ObjectListResponse,\n Topology,\n UploadDetailResponse,\n UploadInfo,\n UploadListResponse,\n} from '../client/types.js';\nimport {\n MEDIA_DASHBOARD_ACTIONS,\n MEDIA_STORAGE_SHARED,\n MEDIA_STORE,\n MEDIA_UPLOAD_SESSIONS,\n} from './tokens.js';\n\n/** Seconds a generated preview/download URL stays valid. */\nconst URL_TTL_SECONDS = 300;\nconst DEFAULT_PAGE_LIMIT = 50;\n/** Ceiling for a console (direct) upload — buffered in memory, so bounded to protect the heap. */\nconst MAX_UPLOAD_BYTES = 100 * 1024 * 1024;\n/** Page size when sweeping a folder's objects for a recursive delete. */\nconst DELETE_SWEEP_LIMIT = 1000;\n\n/** Last path segment of a folder prefix, ignoring a trailing slash. */\nfunction lastSegment(prefix: string): string {\n const trimmed = prefix.replace(/\\/+$/, '');\n const slash = trimmed.lastIndexOf('/');\n return slash === -1 ? trimmed : trimmed.slice(slash + 1);\n}\n\nfunction mapUpload(session: UploadSession): UploadInfo {\n const percent =\n session.size !== undefined && session.size > 0\n ? Math.min(100, Math.round((session.offset / session.size) * 100))\n : null;\n return {\n id: session.id,\n disk: session.disk,\n key: session.key,\n offset: session.offset,\n size: session.size ?? null,\n percent,\n parts: session.parts,\n multipart: session.multipartUploadId !== undefined,\n ...(session.createdAt ? { createdAt: session.createdAt.toISOString() } : {}),\n };\n}\n\nfunction mapRecord(record: MediaRecord): LibraryRecord {\n return {\n id: record.id,\n ownerType: record.ownerType,\n ownerId: record.ownerId,\n collection: record.collection,\n name: record.name,\n fileName: record.fileName,\n mimeType: record.mimeType,\n size: record.size,\n disk: record.disk,\n path: record.path,\n createdAt: record.createdAt.toISOString(),\n };\n}\n\n/**\n * Read + action logic behind the console's JSON API. Resolves the three by-value media tokens\n * with `@Optional()` so a host without a `MediaStore`/upload store still boots — every method\n * degrades to an empty shape instead of throwing when its capability is absent.\n */\n@Injectable()\nexport class MediaConsoleService {\n constructor(\n @Optional() @Inject(MEDIA_STORAGE_SHARED) private readonly storage: StorageManager | null,\n @Optional() @Inject(MEDIA_STORE) private readonly store: MediaStore | null,\n @Optional() @Inject(MEDIA_UPLOAD_SESSIONS) private readonly uploads: UploadSessionStore | null,\n @Optional() @Inject(MEDIA_DASHBOARD_ACTIONS) private readonly actionsEnabled: boolean | null,\n ) {}\n\n private diskOrThrow(disk: string): StorageDriver {\n if (!this.storage || !this.storage.diskNames().includes(disk)) {\n throw new NotFoundException(`Unknown disk: ${disk}`);\n }\n return this.storage.disk(disk);\n }\n\n listDisks(): DiskListResponse {\n const storage = this.storage;\n if (!storage) return { disks: [] };\n const defaultDisk = storage.defaultDisk;\n return {\n disks: storage.diskNames().map((name) => ({\n name,\n default: name === defaultDisk,\n capabilities: storage.disk(name).capabilities,\n })),\n };\n }\n\n async listObjects(\n disk: string,\n options: { prefix?: string; cursor?: string; limit?: number },\n ): Promise<ObjectListResponse> {\n const driver = this.diskOrThrow(disk);\n const result = await driver.list(options.prefix ?? '', {\n delimiter: '/',\n ...(options.cursor ? { cursor: options.cursor } : {}),\n limit: options.limit ?? DEFAULT_PAGE_LIMIT,\n });\n return {\n folders: result.folders.map((prefix) => ({ name: lastSegment(prefix), prefix })),\n // Drop the zero-byte \"folder marker\" (a key ending in `/`, whose name is empty after the\n // prefix) that `createFolder` writes — it's the folder itself, not a file inside it.\n files: result.files\n .filter((entry) => entry.name !== '')\n .map((entry) => ({\n key: entry.key,\n name: entry.name,\n sizeBytes: entry.sizeBytes,\n lastModified: entry.lastModified ? entry.lastModified.toISOString() : null,\n })),\n ...(result.cursor ? { cursor: result.cursor } : {}),\n };\n }\n\n async objectDetail(disk: string, key: string): Promise<ObjectDetailResponse> {\n const driver = this.diskOrThrow(disk);\n const stat = await driver.stat(key);\n const url = driver.capabilities.presign\n ? await driver.temporaryUrl(key, URL_TTL_SECONDS)\n : await driver.url(key);\n return {\n key,\n size: stat.size,\n ...(stat.contentType ? { contentType: stat.contentType } : {}),\n ...(stat.lastModified ? { lastModified: stat.lastModified.toISOString() } : {}),\n url,\n };\n }\n\n /** The object's raw byte stream plus the metadata the controller needs to serve it inline. Used by\n * the console's same-origin preview proxy so text/PDF render in the browser instead of downloading\n * (and so a CORS-locked bucket is still previewable). */\n async objectStream(\n disk: string,\n key: string,\n ): Promise<{ stream: Readable; contentType: string; size: number }> {\n const driver = this.diskOrThrow(disk);\n const stat = await driver.stat(key);\n const stream = await driver.stream(key);\n return { stream, contentType: stat.contentType ?? 'application/octet-stream', size: stat.size };\n }\n\n /** Writes an object from a byte stream (a browser upload). The caller supplies the full key\n * (prefix + filename); an unknown disk 404s via {@link diskOrThrow}. Actions-gated.\n *\n * The request stream is buffered before the write: S3's PutObject needs a known Content-Length,\n * which a raw request stream doesn't carry, so passing the stream straight through fails. Bounded\n * at {@link MAX_UPLOAD_BYTES} so a runaway upload can't exhaust the pod's heap — larger files\n * belong on the resumable upload path, not this console convenience upload. */\n async putObject(disk: string, key: string, body: Readable, contentType?: string): Promise<void> {\n const driver = this.diskOrThrow(disk);\n const chunks: Buffer[] = [];\n let total = 0;\n for await (const chunk of body) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n total += buffer.length;\n if (total > MAX_UPLOAD_BYTES) {\n throw new PayloadTooLargeException(\n `Upload exceeds the ${MAX_UPLOAD_BYTES / (1024 * 1024)} MB console limit.`,\n );\n }\n chunks.push(buffer);\n }\n await driver.put(key, Buffer.concat(chunks), contentType ? { contentType } : undefined);\n }\n\n /** Creates a \"folder\" — a zero-byte marker object at `<prefix>/`, which S3-style listing surfaces\n * as a navigable folder. Normalizes to exactly one trailing slash; rejects an empty prefix. */\n async createFolder(disk: string, prefix: string): Promise<void> {\n const normalized = prefix.replace(/^\\/+/, '').replace(/\\/+$/, '');\n if (normalized === '') throw new BadRequestException('Folder name is required');\n await this.diskOrThrow(disk).put(`${normalized}/`, Buffer.alloc(0));\n }\n\n async deleteObject(disk: string, key: string): Promise<void> {\n await this.diskOrThrow(disk).delete(key);\n }\n\n /** Recursively deletes a \"folder\": every object under `<prefix>/` (nested included) plus the\n * zero-byte marker itself. Sweeps the prefix without a delimiter (flat listing) so it reaches\n * nested keys, paginating until the disk reports no more. Actions-gated. */\n async deleteFolder(disk: string, prefix: string): Promise<void> {\n const driver = this.diskOrThrow(disk);\n const normalized = prefix.replace(/^\\/+/, '').replace(/\\/+$/, '');\n if (normalized === '') throw new BadRequestException('Folder is required');\n const sweepPrefix = `${normalized}/`;\n let cursor: string | undefined;\n do {\n const result = await driver.list(sweepPrefix, {\n limit: DELETE_SWEEP_LIMIT,\n ...(cursor ? { cursor } : {}),\n });\n for (const entry of result.files) {\n await driver.delete(entry.key);\n }\n cursor = result.cursor;\n } while (cursor);\n }\n\n async copyObject(disk: string, from: string, to: string): Promise<void> {\n await this.diskOrThrow(disk).copy(from, to);\n }\n\n async moveObject(disk: string, from: string, to: string): Promise<void> {\n await this.diskOrThrow(disk).move(from, to);\n }\n\n async listUploads(filter: { disk?: string; prefix?: string }): Promise<UploadListResponse> {\n if (!this.uploads || typeof this.uploads.list !== 'function') return { uploads: [] };\n const sessions = await this.uploads.list({\n ...(filter.disk ? { disk: filter.disk } : {}),\n ...(filter.prefix ? { keyPrefix: filter.prefix } : {}),\n });\n return { uploads: sessions.map(mapUpload) };\n }\n\n async uploadDetail(id: string): Promise<UploadDetailResponse> {\n if (!this.uploads) throw new NotFoundException('No upload store configured');\n const session = await this.uploads.get(id);\n if (!session) throw new NotFoundException(`Unknown upload: ${id}`);\n const parts = this.uploads.listParts ? await this.uploads.listParts(id) : [];\n return { upload: mapUpload(session), parts };\n }\n\n /**\n * Cancels a resumable session by removing its record from the upload store, so it stops\n * appearing as in-progress. NOTE: this does NOT tear down an underlying native multipart upload\n * (e.g. an S3 multipart) or its temporary parts — the decoupled console resolves only the\n * `UploadSessionStore`, not the `ResumableUploadManager` that owns `abort()`. An incomplete\n * multipart is reaped by the bucket's lifecycle policy. Surfaced as \"Cancel session\" in the UI.\n */\n async abortUpload(id: string): Promise<void> {\n if (!this.uploads) throw new NotFoundException('No upload store configured');\n await this.uploads.delete(id);\n }\n\n async listCollections(): Promise<CollectionsResponse> {\n if (!this.store || typeof this.store.aggregate !== 'function') return { collections: [] };\n const buckets = await this.store.aggregate({ groupBy: 'collection', sum: 'size' });\n return { collections: buckets };\n }\n\n async listLibrary(filter: {\n collection?: string;\n disk?: string;\n cursor?: string;\n limit?: number;\n }): Promise<LibraryListResponse> {\n if (!this.store || typeof this.store.list !== 'function') return { records: [] };\n const page = await this.store.list(\n {\n ...(filter.collection ? { collection: filter.collection } : {}),\n ...(filter.disk ? { disk: filter.disk } : {}),\n },\n {\n limit: filter.limit ?? DEFAULT_PAGE_LIMIT,\n ...(filter.cursor ? { cursor: filter.cursor } : {}),\n },\n );\n return {\n records: page.records.map(mapRecord),\n ...(page.cursor ? { cursor: page.cursor } : {}),\n };\n }\n\n async libraryDetail(id: string): Promise<LibraryDetailResponse> {\n if (!this.store) throw new NotFoundException('No media store configured');\n const record = await this.store.find(id);\n if (!record) throw new NotFoundException(`Unknown media record: ${id}`);\n const variants = await Promise.all(\n Object.entries(record.conversions).map(async ([name, conversion]) => {\n const driver = this.storage?.diskNames().includes(conversion.disk)\n ? this.storage.disk(conversion.disk)\n : null;\n const url = driver?.capabilities.presign\n ? await driver.temporaryUrl(conversion.path, URL_TTL_SECONDS)\n : ((await driver?.url(conversion.path)) ?? '');\n return { name, url };\n }),\n );\n return { record: mapRecord(record), variants };\n }\n\n async deleteLibraryRecord(id: string): Promise<void> {\n if (!this.store) throw new NotFoundException('No media store configured');\n await this.store.delete(id);\n }\n\n topology(): Topology {\n return {\n hasStore: this.store !== null && typeof this.store.list === 'function',\n hasUploads: this.uploads !== null && typeof this.uploads.list === 'function',\n disks: this.storage ? this.storage.diskNames().length : 0,\n actions: this.actionsEnabled === true,\n };\n }\n}\n","import {\n BadRequestException,\n Body,\n Controller,\n Get,\n HttpCode,\n Inject,\n Logger,\n NotFoundException,\n Optional,\n Post,\n Req,\n Res,\n UnauthorizedException,\n} from '@nestjs/common';\nimport type { ConsoleAuthOptions, ResolvedConsoleAuth } from './auth/config.js';\nimport { parseCookieHeader } from './auth/cookie-header.js';\nimport { readCookieHeader } from './auth/request.js';\nimport {\n SESSION_COOKIE_NAME,\n clearSessionCookie,\n issueSessionCookie,\n} from './auth/session-cookie-io.js';\nimport { type ConsoleSessionUser, verifySessionCookie } from './auth/session-cookie.js';\nimport { MEDIA_CONSOLE_AUTH, MEDIA_CONSOLE_COOKIE_PATH } from './tokens.js';\n\ninterface LoginBody {\n username?: unknown;\n password?: unknown;\n}\n\n/** Response of `GET /me`: the console SPA renders the login screen or the console from this. */\ntype MeResponse =\n | { authRequired: false }\n | { user: { id: string; name?: string; roles: string[] } };\n\n/**\n * Mints/clears the console session cookie. Deliberately NOT decorated with `MediaConsoleGuard` —\n * these endpoints CREATE the session the gate checks for. When the host didn't configure `auth`,\n * `GET /me` reports `authRequired: false` (the SPA shows the console) and login/logout 404.\n */\n@Controller()\nexport class MediaConsoleAuthController {\n private readonly logger = new Logger(MediaConsoleAuthController.name);\n /** One warn per hook kind, so a flaky hook doesn't spam logs every request. */\n private readonly warnedHooks = new Set<string>();\n\n constructor(\n @Optional() @Inject(MEDIA_CONSOLE_AUTH) private readonly auth: ResolvedConsoleAuth | null,\n @Optional() @Inject(MEDIA_CONSOLE_COOKIE_PATH) private readonly cookiePath: string | null,\n ) {}\n\n @Get('me')\n me(@Req() request: unknown): MeResponse {\n if (!this.auth) return { authRequired: false };\n const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];\n const session =\n cookieValue !== undefined\n ? verifySessionCookie(cookieValue, { secret: this.auth.secret })\n : null;\n // The UNauthenticated SPA learns which login mode(s) to offer from this 401 body.\n if (!session) throw new UnauthorizedException({ auth: { modes: this.auth.modes } });\n return {\n user: {\n id: session.sub,\n ...(session.name !== undefined ? { name: session.name } : {}),\n roles: session.roles,\n },\n };\n }\n\n @Post('login')\n @HttpCode(200)\n async login(\n @Body() body: LoginBody,\n @Req() request: unknown,\n @Res({ passthrough: true }) response: unknown,\n ): Promise<MeResponse> {\n const auth = this.requireAuth();\n if (!auth.login) throw new NotFoundException();\n if (typeof body?.username !== 'string' || typeof body?.password !== 'string') {\n throw new BadRequestException('Body must include string `username` and `password`.');\n }\n const username = body.username;\n const password = body.password;\n // Uniform 401 for unknown user / bad password — no user-enumeration.\n const user = await this.runHook('login', () => auth.login?.(username, password) ?? null);\n if (!user) throw new UnauthorizedException({ message: 'Invalid credentials' });\n return this.mint(user, request, response);\n }\n\n @Post('session')\n @HttpCode(200)\n async session(\n @Req() request: unknown,\n @Res({ passthrough: true }) response: unknown,\n ): Promise<MeResponse> {\n const auth = this.requireAuth();\n if (!auth.session) throw new NotFoundException();\n const user = await this.runHook('session', () => auth.session?.(request) ?? null);\n if (!user) throw new UnauthorizedException();\n return this.mint(user, request, response);\n }\n\n @Post('logout')\n @HttpCode(204)\n logout(@Req() request: unknown, @Res({ passthrough: true }) response: unknown): void {\n // Best-effort: even without auth configured, clearing is harmless.\n clearSessionCookie({ cookiePath: this.cookiePath ?? '/', request, response });\n }\n\n private requireAuth(): ResolvedConsoleAuth {\n if (!this.auth) throw new NotFoundException();\n return this.auth;\n }\n\n private mint(user: ConsoleSessionUser, request: unknown, response: unknown): MeResponse {\n const auth = this.requireAuth();\n issueSessionCookie(user, {\n auth,\n cookiePath: this.cookiePath ?? '/',\n request,\n response,\n });\n return {\n user: {\n id: user.id,\n ...(user.name !== undefined ? { name: user.name } : {}),\n roles: user.roles ?? [],\n },\n };\n }\n\n /** Run a host hook defensively: a throw is a denial (null), warn-logged once per kind. */\n private async runHook(\n kind: string,\n run: () => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null,\n ): Promise<ConsoleSessionUser | null> {\n try {\n return (await run()) ?? null;\n } catch (error) {\n if (!this.warnedHooks.has(kind)) {\n this.warnedHooks.add(kind);\n this.logger.warn(`Console auth ${kind} hook threw; treating as denial. ${String(error)}`);\n }\n return null;\n }\n }\n}\n\nexport type { ConsoleAuthOptions };\n","import { Controller, Get, Inject, Param, Query, StreamableFile, UseGuards } from '@nestjs/common';\nimport type {\n CollectionsResponse,\n DiskListResponse,\n LibraryDetailResponse,\n LibraryListResponse,\n ObjectDetailResponse,\n ObjectListResponse,\n Topology,\n UploadDetailResponse,\n UploadListResponse,\n} from '../client/types.js';\nimport { MediaConsoleGuard } from './media-console.guard.js';\nimport { MediaConsoleService } from './media-console.service.js';\n\n/** Parse an optional numeric query param; undefined when absent or not a finite number. */\nfunction toLimit(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : undefined;\n}\n\n/**\n * Read-only JSON API for the /media console. Bare `@Controller()` — the path prefix is applied by\n * `RouterModule` (set in `MediaDashboardModule.forRoot({ apiBasePath })`). Always mounted.\n * `MediaConsoleGuard` gates it on a session cookie when the host configured `auth` (else a no-op).\n */\n@UseGuards(MediaConsoleGuard)\n@Controller()\nexport class MediaConsoleReadController {\n constructor(@Inject(MediaConsoleService) private readonly service: MediaConsoleService) {}\n\n @Get('disks')\n disks(): DiskListResponse {\n return this.service.listDisks();\n }\n\n @Get('disks/:disk/objects')\n objects(\n @Param('disk') disk: string,\n @Query('prefix') prefix?: string,\n @Query('cursor') cursor?: string,\n @Query('limit') limit?: string,\n ): Promise<ObjectListResponse> {\n const limitValue = toLimit(limit);\n return this.service.listObjects(disk, {\n ...(prefix ? { prefix } : {}),\n ...(cursor ? { cursor } : {}),\n ...(limitValue !== undefined ? { limit: limitValue } : {}),\n });\n }\n\n @Get('disks/:disk/object')\n object(@Param('disk') disk: string, @Query('key') key: string): Promise<ObjectDetailResponse> {\n return this.service.objectDetail(disk, key);\n }\n\n /** Streams the object's bytes inline (Content-Disposition: inline) from the same origin, so the SPA\n * can render text/PDF previews the browser would otherwise download, and read text past CORS. */\n @Get('disks/:disk/object/raw')\n async objectRaw(@Param('disk') disk: string, @Query('key') key: string): Promise<StreamableFile> {\n const { stream, contentType, size } = await this.service.objectStream(disk, key);\n return new StreamableFile(stream, {\n type: contentType,\n disposition: 'inline',\n ...(Number.isFinite(size) ? { length: size } : {}),\n });\n }\n\n @Get('uploads')\n uploads(\n @Query('disk') disk?: string,\n @Query('prefix') prefix?: string,\n ): Promise<UploadListResponse> {\n return this.service.listUploads({\n ...(disk ? { disk } : {}),\n ...(prefix ? { prefix } : {}),\n });\n }\n\n @Get('uploads/:id')\n upload(@Param('id') id: string): Promise<UploadDetailResponse> {\n return this.service.uploadDetail(id);\n }\n\n @Get('library/collections')\n collections(): Promise<CollectionsResponse> {\n return this.service.listCollections();\n }\n\n @Get('library')\n library(\n @Query('collection') collection?: string,\n @Query('disk') disk?: string,\n @Query('cursor') cursor?: string,\n @Query('limit') limit?: string,\n ): Promise<LibraryListResponse> {\n const limitValue = toLimit(limit);\n return this.service.listLibrary({\n ...(collection ? { collection } : {}),\n ...(disk ? { disk } : {}),\n ...(cursor ? { cursor } : {}),\n ...(limitValue !== undefined ? { limit: limitValue } : {}),\n });\n }\n\n @Get('library/:id')\n libraryRecord(@Param('id') id: string): Promise<LibraryDetailResponse> {\n return this.service.libraryDetail(id);\n }\n\n @Get('topology')\n topology(): Topology {\n return this.service.topology();\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { basename, extname, join, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n Controller,\n Get,\n Header,\n Inject,\n NotFoundException,\n Param,\n StreamableFile,\n} from '@nestjs/common';\nimport { MEDIA_DASHBOARD_API_PATH, MEDIA_DASHBOARD_BASE_PATH } from './tokens.js';\n\n/** The base the SPA bundle was built with (Vite `base`); rewritten to the configured base at serve time. */\nconst BUILD_BASE = '/media';\n\n/** dist/server/index.js -> ../spa (the Vite build output). */\nfunction spaDir(): string {\n return fileURLToPath(new URL('../spa', import.meta.url));\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.woff2': 'font/woff2',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Serves the bundled /media console SPA at the configured base (+ hashed assets at `<base>/assets`).\n * The path comes from `RouterModule` (set by `MediaDashboardModule.forRoot({ basePath })`), so the\n * controller routes are relative.\n */\n@Controller()\nexport class MediaDashboardUiController {\n private readonly dir = spaDir();\n\n constructor(\n @Inject(MEDIA_DASHBOARD_BASE_PATH) private readonly basePath: string,\n @Inject(MEDIA_DASHBOARD_API_PATH) private readonly apiBasePath: string,\n ) {}\n\n // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = \"stuck\n // loading after a deploy\"). The hashed assets below are immutable.\n @Get()\n @Header('Content-Type', 'text/html; charset=utf-8')\n @Header('Cache-Control', 'no-store, must-revalidate')\n index(): string {\n const indexPath = join(this.dir, 'index.html');\n if (!existsSync(indexPath)) {\n throw new NotFoundException('Console SPA is not built. Run the package build.');\n }\n // Built with Vite base `/media/`; rewrite asset URLs to the configured base so the SPA loads\n // from `<base>/assets` wherever it's mounted, and tell the client its API base.\n const html = readFileSync(indexPath, 'utf8').replaceAll(\n `=\"${BUILD_BASE}/`,\n `=\"${this.basePath}/`,\n );\n const inject = `<script>window.__MEDIA_BASE__='${this.basePath}';window.__MEDIA_API__='${this.apiBasePath}';</script>`;\n return html.includes('</head>') ? html.replace('</head>', `${inject}</head>`) : inject + html;\n }\n\n @Get('assets/:file')\n @Header('Cache-Control', 'public, max-age=31536000, immutable')\n asset(@Param('file') file: string): StreamableFile {\n const safe = basename(file);\n if (safe !== file) throw new NotFoundException();\n const root = resolve(this.dir, 'assets');\n const assetPath = resolve(root, safe);\n if (!assetPath.startsWith(root + sep) || !existsSync(assetPath)) {\n throw new NotFoundException();\n }\n const type = CONTENT_TYPES[extname(safe)] ?? 'application/octet-stream';\n return new StreamableFile(readFileSync(assetPath), { type });\n }\n}\n"],"mappings":"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","names":["getImportMetaUrl","document","URL","__filename","href","currentScript","src","baseURI","importMetaUrl","import_common","DEFAULT_TTL_MS","DURATION_UNITS","s","m","h","d","durationToMs","ttl","undefined","match","exec","trim","unit","Number","resolveConsoleAuth","options","secret","Error","modes","session","push","login","length","ttlMs","import_common","import_common","parseCookieHeader","header","cookies","segment","split","eq","indexOf","name","slice","trim","rawValue","length","startsWith","endsWith","decodeURIComponent","serializeSetCookie","value","options","parts","clear","encodeURIComponent","push","path","secure","maxAgeSeconds","join","isRecord","value","readHeaders","request","headers","headerToString","Array","isArray","every","part","join","undefined","readCookieHeader","cookie","isHttpsRequest","protocol","forwarded","split","map","proto","trim","toLowerCase","includes","attachSession","session","consoleSession","isRecord","value","isRawNodeResponse","getHeader","setHeader","resolveRawResponse","response","raw","existingSetCookies","current","Array","isArray","appendSetCookie","cookie","EXP_GRACE_MS","base64urlEncode","value","Buffer","from","toString","base64urlDecode","hmac","secret","payload","createHmac","update","digest","signSessionCookie","user","options","issuedAt","now","Date","session","sub","id","name","undefined","roles","iat","exp","ttlMs","encodedPayload","JSON","stringify","isSessionPayload","record","Array","isArray","every","role","Number","isFinite","verifySessionCookie","dot","indexOf","length","slice","provided","expected","timingSafeEqual","decoded","parse","SESSION_COOKIE_NAME","issueSessionCookie","user","context","value","signSessionCookie","secret","auth","ttlMs","now","undefined","appendSetCookie","response","serializeSetCookie","path","cookiePath","maxAgeSeconds","Math","floor","secure","isHttpsRequest","request","clearSessionCookie","clear","MEDIA_STORE","Symbol","for","MEDIA_UPLOAD_SESSIONS","MEDIA_STORAGE_SHARED","MEDIA_DASHBOARD_BASE_PATH","MEDIA_DASHBOARD_API_PATH","MEDIA_DASHBOARD_ACTIONS","MEDIA_CONSOLE_AUTH","MEDIA_CONSOLE_COOKIE_PATH","MediaConsoleGuard","auth","cookiePath","canActivate","context","http","switchToHttp","request","getRequest","session","verifyRequestSession","UnauthorizedException","attachSession","maybeRenew","getResponse","cookieValue","parseCookieHeader","readCookieHeader","SESSION_COOKIE_NAME","undefined","verifySessionCookie","secret","response","now","Date","iat","ttlMs","issueSessionCookie","id","sub","name","roles","import_common","URL_TTL_SECONDS","DEFAULT_PAGE_LIMIT","MAX_UPLOAD_BYTES","DELETE_SWEEP_LIMIT","lastSegment","prefix","trimmed","replace","slash","lastIndexOf","slice","mapUpload","session","percent","size","undefined","Math","min","round","offset","id","disk","key","parts","multipart","multipartUploadId","createdAt","toISOString","mapRecord","record","ownerType","ownerId","collection","name","fileName","mimeType","path","MediaConsoleService","storage","store","uploads","actionsEnabled","diskOrThrow","diskNames","includes","NotFoundException","listDisks","disks","defaultDisk","map","default","capabilities","listObjects","options","driver","result","list","delimiter","cursor","limit","folders","files","filter","entry","sizeBytes","lastModified","objectDetail","stat","url","presign","temporaryUrl","contentType","objectStream","stream","putObject","body","chunks","total","chunk","buffer","Buffer","isBuffer","from","length","PayloadTooLargeException","push","put","concat","createFolder","normalized","BadRequestException","alloc","deleteObject","delete","deleteFolder","sweepPrefix","copyObject","to","copy","moveObject","move","listUploads","sessions","keyPrefix","uploadDetail","get","listParts","upload","abortUpload","listCollections","aggregate","collections","buckets","groupBy","sum","listLibrary","records","page","libraryDetail","find","variants","Promise","all","Object","entries","conversions","conversion","deleteLibraryRecord","topology","hasStore","hasUploads","actions","MediaConsoleActionsController","service","deleteObject","disk","key","copyObject","body","from","to","moveObject","uploadObject","request","type","putObject","createFolder","prefix","deleteFolder","abortUpload","id","deleteLibraryRecord","import_common","MediaConsoleAuthController","logger","Logger","name","warnedHooks","Set","auth","cookiePath","me","request","authRequired","cookieValue","parseCookieHeader","readCookieHeader","SESSION_COOKIE_NAME","session","undefined","verifySessionCookie","secret","UnauthorizedException","modes","user","id","sub","roles","login","body","response","requireAuth","NotFoundException","username","password","BadRequestException","runHook","message","mint","logout","clearSessionCookie","issueSessionCookie","kind","run","error","has","add","warn","String","passthrough","import_common","toLimit","value","undefined","parsed","Number","isFinite","Math","floor","MediaConsoleReadController","service","disks","listDisks","objects","disk","prefix","cursor","limit","limitValue","listObjects","object","key","objectDetail","objectRaw","stream","contentType","size","objectStream","StreamableFile","type","disposition","length","uploads","listUploads","upload","id","uploadDetail","collections","listCollections","library","collection","listLibrary","libraryRecord","libraryDetail","topology","MediaConsoleApiModule","register","options","module","imports","controllers","MediaConsoleReadController","MediaConsoleAuthController","actions","MediaConsoleActionsController","providers","MediaConsoleService","MediaConsoleGuard","provide","MEDIA_DASHBOARD_ACTIONS","useValue","MEDIA_CONSOLE_COOKIE_PATH","cookiePath","authProvider","exports","import_common","BUILD_BASE","spaDir","fileURLToPath","URL","CONTENT_TYPES","MediaDashboardUiController","dir","basePath","apiBasePath","index","indexPath","join","existsSync","NotFoundException","html","readFileSync","replaceAll","inject","includes","replace","asset","file","safe","basename","root","resolve","assetPath","startsWith","sep","type","extname","StreamableFile","normalize","path","replace","MediaDashboardModule","forRoot","options","apiBasePath","basePath","build","provide","MEDIA_CONSOLE_AUTH","useValue","resolveConsoleAuth","auth","forRootAsync","authProvider","inject","useFactory","deps","useAuth","imports","actions","module","MediaConsoleApiModule","register","cookiePath","RouterModule","controllers","MediaDashboardUiController","providers","MEDIA_DASHBOARD_BASE_PATH","MEDIA_DASHBOARD_API_PATH","exports"]}