diff --git a/Dockerfile b/Dockerfile index 46976c5..f01ad4e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -109,9 +109,12 @@ RUN if [ -f dist-clients/BackstageCapture.x64.dll ]; then \ echo "WARNING: BackstageCapture DLL not available (build with MSVC on Windows)"; \ fi -# Tailwind CSS + vendored frontend assets -RUN bun run build:css && bun run vendor \ - && test -s ./public/assets/tailwind.css && test -d ./public/vendor/fontawesome +# Tailwind CSS, vendored frontend assets, and bundled Bun server runtime. +RUN bun run build \ + && test -s ./public/assets/tailwind.css \ + && test -d ./public/vendor/fontawesome \ + && test -s ./dist/index.js \ + && test -s ./dist/server/plugin-runtime/worker-host.js # ============================================================ @@ -151,10 +154,8 @@ COPY Overlord-Server/package.json Overlord-Server/bun.lock* ./ RUN --mount=type=cache,target=/root/.bun/install/cache \ bun install --production --frozen-lockfile -# Server source. -COPY Overlord-Server/ ./ - -# Built artifacts from the builder stage (overwrites anything from the source copy). +# Built runtime artifacts from the builder stage. +COPY --from=builder /app/dist ./dist COPY --from=builder /app/public ./public COPY --from=builder /app/dist-clients ./dist-clients @@ -180,7 +181,4 @@ ENV NODE_ENV=production ENV OVERLORD_ROOT=/app ENV NODE_PATH=/app/node_modules -# NOTE: We intentionally do NOT use `bun build --compile` here. -# The compiled standalone binary runs from a virtual bunfs filesystem that -# cannot reliably load native modules like `sharp` from /app/node_modules. -CMD ["bun", "run", "src/index.ts"] +CMD ["bun", "dist/index.js"] diff --git a/Overlord-Server/bun.lock b/Overlord-Server/bun.lock index f7f7148..05f624b 100644 --- a/Overlord-Server/bun.lock +++ b/Overlord-Server/bun.lock @@ -12,7 +12,6 @@ "geoip-lite": "latest", "jose": "^6.1.3", "msgpackr": "1.11.8", - "sharp": "^0.34.5", "uuid": "^9.0.1", "web-push": "^3.6.7", "winston": "^3.11.0", @@ -38,7 +37,9 @@ "highlight.js": "11.9.0", "html-minifier-terser": "^7.2.0", "leaflet": "1.9.4", + "monaco-editor": "^0.55.1", "postcss": "^8.5.6", + "tabulator-tables": "^6.4.0", "tailwindcss": "^4.1.18", "terser": "^5.46.1", "typescript": "^5.6.3", @@ -52,64 +53,12 @@ "@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="], - "@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], - "@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="], "@fontsource/jetbrains-mono": ["@fontsource/jetbrains-mono@5.2.8", "", {}, "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ=="], "@fortawesome/fontawesome-free": ["@fortawesome/fontawesome-free@6.5.2", "", {}, "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q=="], - "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], - - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], - - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], - - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], - - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], - - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], - - "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], - - "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], - - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], - - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], - - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], - - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], - - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], - - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], - - "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], - - "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], - - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], - - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], - - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], - - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], - - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], - - "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], - - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], - - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -224,6 +173,8 @@ "@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="], "@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], @@ -310,6 +261,8 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "dompurify": ["dompurify@3.2.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw=="], + "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], @@ -426,10 +379,14 @@ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "marked": ["marked@14.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="], + "minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "monaco-editor": ["monaco-editor@0.55.1", "", { "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" } }, "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A=="], + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -480,10 +437,6 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - - "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -504,6 +457,8 @@ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "tabulator-tables": ["tabulator-tables@6.4.0", "", {}, "sha512-Lxh+leFNoBo/Yyr4USs6gxqbfo8anYUaUMmoT91pfVLtoUgl/dE+qV7ahnFrKVMCYYqGG33aIMPR7FzpPBaNYA=="], + "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], @@ -546,8 +501,6 @@ "yauzl": ["yauzl@3.2.1", "", { "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" } }, "sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A=="], - "@emnapi/runtime/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "@solana/errors/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "@solana/errors/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], diff --git a/Overlord-Server/package.json b/Overlord-Server/package.json index ca41f68..a953c03 100644 --- a/Overlord-Server/package.json +++ b/Overlord-Server/package.json @@ -5,11 +5,12 @@ "type": "module", "scripts": { "dev": "bun --watch src/index.ts", - "start": "bun run build:css && bun run vendor && bun run src/index.ts", - "build": "bun run build:css && bun run vendor && bun build --production --external sharp src/index.ts --target bun --outdir dist", + "start": "bun run build && bun dist/index.js", + "build": "bun run build:css && bun run vendor && bun run build:bundle", + "build:bundle": "bun build --production src/index.ts --target bun --outfile dist/index.js && bun build --production src/server/plugin-runtime/worker-host.ts --target bun --outfile dist/server/plugin-runtime/worker-host.js", "vendor": "bun run scripts/vendor.ts", - "build:prod:win": "bun build --production --external sharp src/index.ts --compile --target bun-windows-x64 --outfile dist/overlord-server.exe", - "build:prod:linux": "bun build --production --external sharp src/index.ts --compile --target bun-linux-x64 --outfile dist/overlord-server-linux-x64", + "build:prod:win": "bun build --production src/index.ts --compile --target bun-windows-x64 --outfile dist/overlord-server.exe", + "build:prod:linux": "bun build --production src/index.ts --compile --target bun-linux-x64 --outfile dist/overlord-server-linux-x64", "build:css": "tailwindcss -i ./src/styles.css -o ./public/assets/tailwind.css --minify", "watch:css": "tailwindcss -i ./src/styles.css -o ./public/assets/tailwind.css --watch", "minify": "bun run scripts/minify-public.ts" @@ -22,7 +23,6 @@ "geoip-lite": "latest", "jose": "^6.1.3", "msgpackr": "1.11.8", - "sharp": "^0.34.5", "uuid": "^9.0.1", "web-push": "^3.6.7", "winston": "^3.11.0" @@ -48,7 +48,9 @@ "highlight.js": "11.9.0", "html-minifier-terser": "^7.2.0", "leaflet": "1.9.4", + "monaco-editor": "^0.55.1", "postcss": "^8.5.6", + "tabulator-tables": "^6.4.0", "tailwindcss": "^4.1.18", "terser": "^5.46.1", "typescript": "^5.6.3" diff --git a/Overlord-Server/public/assets/main.css b/Overlord-Server/public/assets/main.css index 5dea6d2..0182d00 100644 --- a/Overlord-Server/public/assets/main.css +++ b/Overlord-Server/public/assets/main.css @@ -35,6 +35,60 @@ --color-danger: #ef4444; } +.monaco-editor, +.monaco-editor .margin, +.monaco-editor-background { + --vscode-editor-background: #020617; +} + +.overlord-tabulator.tabulator { + background: #020617; + border: 1px solid rgb(30 41 59); + border-radius: 0.75rem; + color: #e2e8f0; + min-height: 12rem; + overflow: hidden; +} + +.overlord-tabulator .tabulator-header { + background: #0f172a; + border-bottom-color: rgb(30 41 59); + color: #94a3b8; +} + +.overlord-tabulator .tabulator-header .tabulator-col { + background: #0f172a; + border-right-color: rgb(30 41 59); +} + +.overlord-tabulator .tabulator-header .tabulator-col:hover { + background: #162033; +} + +.overlord-tabulator .tabulator-row { + background: #020617; + border-bottom-color: rgba(30, 41, 59, 0.85); + color: #e2e8f0; +} + +.overlord-tabulator .tabulator-row.tabulator-row-even { + background: #07101f; +} + +.overlord-tabulator .tabulator-row:hover { + background: #111827; +} + +.overlord-tabulator .tabulator-cell { + border-right-color: rgba(30, 41, 59, 0.75); + padding: 0.5rem 0.75rem; +} + +.overlord-tabulator .tabulator-placeholder { + background: #020617; + color: #94a3b8; +} + :root:not(.app-ready) #app-loader { opacity: 1; visibility: visible; diff --git a/Overlord-Server/public/assets/monaco-loader.js b/Overlord-Server/public/assets/monaco-loader.js new file mode 100644 index 0000000..33ee97d --- /dev/null +++ b/Overlord-Server/public/assets/monaco-loader.js @@ -0,0 +1,56 @@ +let monacoPromise = null; + +export function loadMonaco() { + if (window.monaco?.editor) return Promise.resolve(window.monaco); + if (monacoPromise) return monacoPromise; + + monacoPromise = new Promise((resolve, reject) => { + const configure = () => { + if (typeof window.require !== "function") { + reject(new Error("Monaco loader is unavailable")); + return; + } + window.require.config({ paths: { vs: "/vendor/monaco/vs" } }); + window.require(["vs/editor/editor.main"], () => resolve(window.monaco), reject); + }; + + if (typeof window.require === "function") { + configure(); + return; + } + + const script = document.createElement("script"); + script.src = "/vendor/monaco/vs/loader.js"; + script.defer = true; + script.onload = configure; + script.onerror = () => reject(new Error("Failed to load Monaco")); + document.head.appendChild(script); + }); + + return monacoPromise; +} + +export function createMonacoEditorAdapter(editor, monaco) { + return { + getValue: () => editor.getValue(), + setValue(value) { + editor.setValue(value || ""); + }, + setLanguage(language) { + const model = editor.getModel(); + if (model) monaco.editor.setModelLanguage(model, language); + }, + setSize(_width, height) { + const domNode = editor.getDomNode(); + const host = domNode?.parentElement; + if (host && height) host.style.height = height; + editor.layout(); + }, + refresh() { + editor.layout(); + }, + focus() { + editor.focus(); + }, + }; +} diff --git a/Overlord-Server/public/assets/notifications.js b/Overlord-Server/public/assets/notifications.js index 28719f9..8106c7f 100644 --- a/Overlord-Server/public/assets/notifications.js +++ b/Overlord-Server/public/assets/notifications.js @@ -11,13 +11,14 @@ import { requestDesktopNotificationPermission, setDesktopNotificationsEnabled, } from "./notify-client.js"; +import { createMonacoEditorAdapter, loadMonaco } from "./monaco-loader.js"; +import { TabulatorFull as Tabulator } from "/vendor/tabulator/tabulator_esm.min.js"; // ── DOM refs ────────────────────────────────────────────────────────────────── const wsStatus = document.getElementById("ws-status"); -const listEl = document.getElementById("notification-list"); +const tableEl = document.getElementById("notification-table"); const emptyState = document.getElementById("empty-state"); const notificationScopeHint = document.getElementById("notification-scope-hint"); -const tableHead = document.getElementById("notification-thead"); const searchInput = document.getElementById("notification-search"); const resultHint = document.getElementById("notification-result-hint"); @@ -83,6 +84,7 @@ const MAX_ROWS = 200; let defaultWebhookTemplate = ""; let defaultTelegramTemplate = ""; let webhookTemplateEditor = null; +let notificationTable = null; function tryFormatJsonText(text) { const source = String(text || "").trim(); @@ -94,26 +96,57 @@ function tryFormatJsonText(text) { } } -function initWebhookTemplateEditor() { - if (!webhookTemplateInput || typeof window.ace === "undefined") return; +async function initWebhookTemplateEditor() { + if (!webhookTemplateInput) return; try { - webhookTemplateEditor = window.ace.edit("webhook-template"); - webhookTemplateEditor.setTheme("ace/theme/tomorrow_night"); - webhookTemplateEditor.session.setMode("ace/mode/json"); - webhookTemplateEditor.session.setUseWrapMode(true); - webhookTemplateEditor.setShowPrintMargin(false); - webhookTemplateEditor.setOption("fontSize", "12px"); - webhookTemplateEditor.setOption("tabSize", 2); - webhookTemplateEditor.setOption("useSoftTabs", true); - webhookTemplateEditor.on("blur", () => { - const formatted = tryFormatJsonText(webhookTemplateEditor.getValue()); - if (formatted !== null && formatted !== webhookTemplateEditor.getValue()) { - const position = webhookTemplateEditor.getCursorPosition(); - webhookTemplateEditor.setValue(formatted, -1); - webhookTemplateEditor.moveCursorToPosition(position); + const monaco = await loadMonaco(); + const host = document.createElement("div"); + host.className = "w-full rounded-lg border border-slate-800 overflow-hidden"; + host.style.height = "180px"; + webhookTemplateInput.classList.add("hidden"); + webhookTemplateInput.insertAdjacentElement("afterend", host); + + monaco.editor.defineTheme("overlord-json", { + base: "vs-dark", + inherit: true, + rules: [], + colors: { + "editor.background": "#020617", + "editor.foreground": "#e2e8f0", + "editor.lineHighlightBackground": "#0f172a", + "editorCursor.foreground": "#38bdf8", + "editor.selectionBackground": "#2563eb66", + "editorGutter.background": "#020617", + }, + }); + + const editor = monaco.editor.create(host, { + value: webhookTemplateInput.value || "", + language: "json", + theme: "overlord-json", + automaticLayout: true, + fontFamily: '"JetBrains Mono", Consolas, "Courier New", monospace', + fontSize: 12, + minimap: { enabled: false }, + scrollBeyondLastLine: false, + tabSize: 2, + insertSpaces: true, + wordWrap: "on", + padding: { top: 8, bottom: 8 }, + }); + + webhookTemplateEditor = createMonacoEditorAdapter(editor, monaco); + editor.onDidBlurEditorWidget(() => { + const current = webhookTemplateEditor.getValue(); + const formatted = tryFormatJsonText(current); + if (formatted !== null && formatted !== current) { + const position = editor.getPosition(); + webhookTemplateEditor.setValue(formatted); + if (position) editor.setPosition(position); } }); - } catch { + } catch (err) { + console.warn("Monaco webhook editor unavailable; falling back to textarea", err); webhookTemplateEditor = null; } } @@ -247,6 +280,35 @@ function compareEntries(a, b) { return tableState.sortDir === "asc" ? cmp : -cmp; } +function sourceHtml(entry) { + if (entry.kind === "event") { + return CLIENT_EVENT_BADGE[entry.item.event] || + `${escapeHtml(entry.item.event)}`; + } + const isClipboard = entry.item.category === "clipboard"; + return isClipboard + ? ` clipboard` + : ` window`; +} + +function entryToRow(entry) { + const item = entry.item; + return { + uid: entry.uid, + kind: entry.kind, + item, + isLive: entry.isLive, + ts: Number(item.ts) || 0, + clientId: item.clientId || "", + user: item.user || "-", + title: entry.kind === "event" ? "client event" : (item.title || "-"), + process: entry.kind === "event" ? (item.os || "-") : (item.process || "-"), + keyword: entry.kind === "event" ? "-" : (item.keyword || "-"), + source: entry.kind === "event" ? (item.event || "") : (item.category || "window"), + sourceHtml: sourceHtml(entry), + }; +} + function entryMatchesSearch(entry, query) { if (!query) return true; const item = entry.item; @@ -268,16 +330,12 @@ function entryMatchesSearch(entry, query) { } function applyTableView() { - if (!listEl) return; const q = tableState.search.trim().toLowerCase(); const all = Array.from(tableState.entries.values()); const filtered = q ? all.filter((entry) => entryMatchesSearch(entry, q)) : all; filtered.sort(compareEntries); - - listEl.innerHTML = ""; - for (const entry of filtered) { - listEl.appendChild(entry.element); - } + const rows = filtered.map(entryToRow); + if (notificationTable) notificationTable.replaceData(rows); if (emptyState) { if (all.length === 0) { @@ -317,11 +375,7 @@ function upsertEntry(item, kind, isLive) { existing.item = { ...existing.item, ...item }; return existing; } - const element = - kind === "event" - ? createClientEventRowElement(item) - : createNotificationRowElement(item, isLive); - const entry = { item, kind, element, isLive }; + const entry = { uid, item, kind, isLive }; tableState.entries.set(uid, entry); trimEntries(); return entry; @@ -370,6 +424,95 @@ function clientIdCellHtml(clientId) { return `
Variables: {title}
{keyword}
@@ -415,37 +415,7 @@
-
| - Time - | -- Client - | -- User - | -- Title / Content - | -- Process - | -- Keyword - | -- Source - | -Preview | -
|---|
| - - | -Host | -User | -OS | -CPU | -GPU | -RAM | -IP | -Country | -Key Fingerprint | -Last Seen | -Status | -Actions | -
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| - Loading... - | -||||||||||||
No clients in this category.
@@ -183,17 +157,7 @@| IP Address | -Reason | -Banned At | -Actions | -
|---|
No banned IPs.
diff --git a/Overlord-Server/public/scripts.html b/Overlord-Server/public/scripts.html index cf91e3d..16aabe0 100644 --- a/Overlord-Server/public/scripts.html +++ b/Overlord-Server/public/scripts.html @@ -12,13 +12,6 @@ - - - @@ -258,10 +251,6 @@ - - - -