quine-anaconda-webui/build.js
Bruno 66c31db454 Wire up PIN authentication for remote installation
Integrate the login page, auth config, and packaging needed for
PIN-based remote access. Cockpit's CustomLoginPage only loads
login.html and po.js from the package directory; all other static
assets must be served from /usr/share/cockpit/static/, so the built
login.js and login.css are symlinked there as a separate entrypoint.

The conf.d drop-in (50-remote-auth.conf) is shipped to a staging
path and only activated at runtime by the anaconda backend when
inst.webui.remote is set with a PIN (rhinstaller/anaconda#7228).

The coproc wrapper now conditionally passes --local-session=- only
when auth is disabled, allowing cockpit-ws to delegate authentication
to the PIN handler when auth is active.

Resolves: INSTALLER-3373
2026-08-12 15:18:28 -03:00

124 lines
4 KiB
JavaScript
Executable file

#!/usr/bin/env node
import esbuild from "esbuild";
import copy from "esbuild-plugin-copy";
import { sassPlugin } from "esbuild-sass-plugin";
import fs from "fs";
import path from "path";
import { cockpitPoEsbuildPlugin } from "./pkg/lib/cockpit-po-plugin.js";
import { cockpitRsyncEsbuildPlugin } from "./pkg/lib/cockpit-rsync-plugin.js";
import { cleanPlugin } from "./pkg/lib/esbuild-cleanup-plugin.js";
import { cockpitCompressPlugin } from "./pkg/lib/esbuild-compress-plugin.js";
const production = process.env.NODE_ENV === "production";
const watchMode = process.env.ESBUILD_WATCH === "true" || false;
/* List of directories to use when resolving import statements */
const nodePaths = ["pkg/lib"];
const outdir = "dist";
// Obtain package name from package.json
const packageJson = JSON.parse(fs.readFileSync("package.json"));
const getTime = () => new Date().toTimeString()
.split(" ")[0];
// similar to fs.watch(), but recursively watches all subdirectories
function watchDirs (dir, onChange) {
const callback = (ev, dir, fname) => {
// only listen for "change" events, as renames are noisy
// ignore hidden files and the "4913" temporary file created by vim
const isHidden = /^\./.test(fname);
if (ev !== "change" || isHidden || fname === "4913") {
return;
}
onChange(path.join(dir, fname));
};
fs.watch(dir, {}, (ev, path) => callback(ev, dir, path));
// watch all subdirectories in dir
const d = fs.opendirSync(dir);
let dirent;
while ((dirent = d.readSync()) !== null) {
if (dirent.isDirectory()) {
watchDirs(path.join(dir, dirent.name), onChange);
}
}
d.closeSync();
}
const context = await esbuild.context({
bundle: true,
entryPoints: ["./src/index.js", "./src/anacondaLogin.js"],
external: ["*.woff", "*.woff2", "*.jpg", "*.svg", "../../assets*"],
legalComments: "external", // Move all legal comments to a .LEGAL.txt file
loader: {
".js": "jsx",
".py": "text",
".txt": "text",
},
minify: production,
nodePaths,
outdir,
plugins: [
cleanPlugin(),
// Esbuild will only copy assets that are explicitly imported and used
// in the code. This is a problem for index.html and manifest.json which are not imported
copy({
assets: [
{ from: ["./images/qr-code-feedback.svg"], to: ["./qr-code-feedback.svg"] },
{ from: ["./src/manifest.json"], to: ["./manifest.json"] },
{ from: ["./src/index.html"], to: ["./index.html"] },
{ from: ["./src/login.html"], to: ["./login.html"] },
{ from: ["./VERSION.txt"], to: ["./VERSION.txt"] },
]
}),
sassPlugin({
loadPaths: [...nodePaths, "node_modules"],
quietDeps: true,
}),
cockpitPoEsbuildPlugin(),
// Login assets are served via /cockpit/static/ which does not
// support .gz transparent serving, causing MIME type mismatch.
cockpitCompressPlugin({ exclude: /anacondaLogin\.(js|css)$/ }),
cockpitRsyncEsbuildPlugin({ dest: packageJson.name }),
{
name: "notify-end",
setup (build) {
build.onEnd(() => {
// eslint-disable-next-line no-console
console.log(`${getTime()}: Build finished`);
});
}
},
],
sourcemap: "linked",
target: ["es2020"],
});
try {
await context.rebuild();
} catch (e) {
if (!watchMode) {
process.exit(1);
}
// ignore errors in watch mode
}
if (watchMode) {
const onChange = async path => {
// eslint-disable-next-line no-console
console.log("change detected:", path);
await context.cancel();
try {
await context.rebuild();
} catch (e) {} // ignore in watch mode
};
watchDirs("src", onChange);
// wait forever until Control-C
await new Promise(() => {});
}
context.dispose();