const assert = require("assert"); const fs = require("fs"); const path = require("path"); const root = path.join(__dirname, ".."); const sandbox = fs.mkdtempSync(path.join(root, ".tmp-lumi-content-library-")); const serviceDir = path.join(sandbox, "src", "services"); fs.mkdirSync(serviceDir, { recursive: true }); for (const file of [ "config.js", "db.js", "settings.js", "upload-security.js", "content-library.js", "content-routes.js" ]) { fs.copyFileSync(path.join(root, "src", "services", file), path.join(serviceDir, file)); } let database = null; (async () => { try { database = require(path.join(serviceDir, "db.js")); // Core imports the Web server before main() runs migrate(). The content // service must therefore be safe to load without touching database tables. const library = require(path.join(serviceDir, "content-library.js")); const routes = require(path.join(serviceDir, "content-routes.js")); database.migrate(); const settings = require(path.join(serviceDir, "settings.js")); settings.ensureDefaults(); settings.setSetting("session_secret", "content-library-verification-secret"); assert(database.db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'content_resources'").get()); library.ensureContentLibrary(); assert(library.supportedExtensions().includes(".mp4")); assert(library.supportedExtensions().includes(".mp3")); assert(library.supportedExtensions().includes(".webm")); assert(library.supportedExtensions().includes(".png")); assert(library.supportedExtensions().includes(".woff2")); const pngPath = path.join(library.INCOMING_DIR, "verify.png.upload"); fs.writeFileSync( pngPath, Buffer.from( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9ZK1sAAAAASUVORK5CYII=", "base64" ) ); const [created] = await library.importUploadedFiles([ { path: pngPath, originalname: "Alert Graphic.png", mimetype: "image/png" } ], { access_level: "locked", uploaded_by: "verify-admin" }); assert.strictEqual(created.access_level, "locked"); assert.strictEqual(created.category, "image"); assert.strictEqual(created.preview_kind, "image"); assert.strictEqual(library.publicUrl(created, "https://lumi.example"), null); assert(fs.existsSync(library.resourceFilePath(created))); assert.match(library.serializeResource(created, { base_url: "https://lumi.example" }).download_url, /\/admin\/resources\/[^/]+\/download\/Alert%20Graphic\.png$/); const signed = new URL(library.createSignedUrl(created.id, { base_url: "https://lumi.example", ttl_seconds: 60 })); const signedResource = library.verifySignedResource( created.id, signed.searchParams.get("expires"), signed.searchParams.get("sig") ); assert.strictEqual(signedResource.id, created.id); const exposed = library.setResourceAccess(created.id, "exposed"); const publicUrl = new URL(library.publicUrl(exposed, "https://lumi.example")); const publicToken = publicUrl.pathname.split("/")[2]; assert(publicToken.length >= 40); assert.strictEqual(library.getResourceByExposedToken(publicToken).id, created.id); const renamed = library.updateResourceName(created.id, "Follower alert"); assert.strictEqual(renamed.display_name, "Follower alert"); assert.strictEqual(library.listResources({ category: "image" }).length, 1); assert.deepStrictEqual(routes.parseByteRange("bytes=0-9", 100), { start: 0, end: 9 }); assert.deepStrictEqual(routes.parseByteRange("bytes=-10", 100), { start: 90, end: 99 }); assert.strictEqual(routes.parseByteRange("bytes=200-300", 100).invalid, true); assert.strictEqual(routes.parseByteRange("bytes=0-1,4-5", 100).invalid, true); const stats = library.getStorageStats(); assert.strictEqual(stats.file_count, 1); assert(stats.used_bytes > 0); assert(stats.max_file_bytes > 0); const configuredStorage = library.updateStorageSettings({ limit_gib: "2.5", reserve_gib: "0.25", max_file_mib: "32", max_files: "7" }); assert.strictEqual(configuredStorage.limit_bytes, Math.floor(2.5 * 1024 ** 3)); assert.strictEqual(configuredStorage.reserve_bytes, Math.floor(0.25 * 1024 ** 3)); assert.strictEqual(configuredStorage.max_file_bytes, 32 * 1024 ** 2); assert.strictEqual(configuredStorage.max_files, 7); assert.deepStrictEqual(library.uploadLimits(), { max_file_bytes: 32 * 1024 ** 2, max_files: 7 }); assert.throws( () => library.updateStorageSettings({}), /Complete every storage-limit field before saving/ ); assert.throws( () => library.updateStorageSettings({ limit_gib: "2.5", reserve_gib: "-1", max_file_mib: "32", max_files: "7" }), /Storage values must be positive numbers/ ); assert.deepStrictEqual(library.uploadLimits(), { max_file_bytes: 32 * 1024 ** 2, max_files: 7 }); const lockedAgain = library.setResourceAccess(created.id, "locked"); assert.strictEqual(library.getResourceByExposedToken(publicToken), null); assert.strictEqual(lockedAgain.public_token, ""); const storedPath = library.resourceFilePath(created); library.deleteResource(created.id); assert.strictEqual(fs.existsSync(storedPath), false); assert.strictEqual(library.getResource(created.id), null); assert.strictEqual(library.getStorageStats().file_count, 0); const serverSource = fs.readFileSync(path.join(root, "src", "web", "server.js"), "utf8"); assert(serverSource.includes("registerPublicContentRoutes(app)")); assert(serverSource.includes("registerContentAdminRoutes(app")); assert(serverSource.includes('path: "/admin/resources"')); assert(serverSource.includes("global.lumiFrameworks.resources")); const overlayRouteSource = fs.readFileSync(path.join(root, "src", "services", "overlay-routes.js"), "utf8"); assert(overlayRouteSource.includes("media-src https: http: data: blob:"), "OBS overlay CSP must allow configured media sources"); const viewSource = fs.readFileSync(path.join(root, "src", "web", "views", "admin-resources.ejs"), "utf8"); assert(viewSource.includes("data-resource-dropzone")); assert(viewSource.includes("data-resource-preview-modal")); assert(viewSource.includes("data-resource-media-toggle") && viewSource.includes("data-resource-media-seek")); assert(viewSource.includes("data-resource-rename-submit") && viewSource.includes("resource-more-actions")); assert(viewSource.includes("Available to Lumi")); assert(viewSource.includes("data-resource-access-form") && viewSource.includes("data-confirm-mode=\"modal\"") && viewSource.includes("data-no-destructive-confirm")); assert(viewSource.includes("data-resource-settings-summary")); for (const field of ["limit_gib", "reserve_gib", "max_file_mib", "max_files"]) { assert(new RegExp(`name="${field}"[^\\n]*required`).test(viewSource)); } const contentRouteSource = fs.readFileSync(path.join(root, "src", "services", "content-routes.js"), "utf8"); assert(contentRouteSource.includes('/admin/resources/:id/revoke')); assert(contentRouteSource.includes('/admin/resources/:id/download/:filename')); const clientSource = fs.readFileSync(path.join(root, "src", "web", "public", "content-library.js"), "utf8"); assert(clientSource.includes("window.LumiConfirm?.destructiveToken"), "Resource actions must use the shared timed confirmation API"); assert(clientSource.includes('headers["X-Confirmation-Token"] = token'), "Resource actions must send the issued confirmation token directly"); assert(clientSource.includes("data-resource-upload-limit-copy") && clientSource.includes("selectedFilesError"), "Saved upload limits must be reflected and enforced in the UI"); assert(clientSource.includes('"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"')); assert(clientSource.includes("const body = new URLSearchParams()"), "Resource action forms must use an encoding Express parses without upload middleware"); const cssSource = fs.readFileSync(path.join(root, "src", "web", "public", "content-library.css"), "utf8"); assert(cssSource.includes(".resource-empty-state[hidden]") && cssSource.includes("display: none !important")); assert(cssSource.includes(".resource-preview-tile:focus-within .resource-media-controls")); const coreKnowledge = fs.readFileSync(path.join(root, "knowledge", "core", "lumi-core.md"), "utf8"); assert(coreKnowledge.includes("GET /admin/resources") && coreKnowledge.includes("GET /media/:token/:filename")); console.log("Content-library verification passed."); } finally { try { database?.db?.close(); } catch {} fs.rmSync(sandbox, { recursive: true, force: true }); } })().catch((error) => { console.error(error); process.exitCode = 1; });