Skip to content

Commit

Permalink
Merge pull request #4 from ultracodez/auth
Browse files Browse the repository at this point in the history
bugfixes as preparation for `Auth`
  • Loading branch information
ultracodez committed Aug 6, 2023
2 parents 2b03d16 + 2a2f46d commit accf3b3
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 54 deletions.
4 changes: 2 additions & 2 deletions electron/BackendServer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { spawnSync } = require("child_process");
const { spawn } = require("child_process");
const { kill } = require("process");

function setupBackendServer(path) {
const process = spawnSync(path);
const process = spawn(path);
const pid = process.pid;
return {
kill: () => {
Expand Down
34 changes: 19 additions & 15 deletions electron/FrontendServer.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
var finalhandler = require("finalhandler");
var http = require("http");
var serveStatic = require("serve-static");
const { fork } = require("child_process");
const path = require("path");
let { getPorts } = require("./helpers/get-port");

function setupFrontendServer(servePath) {
// Serve up public/ftp folder
var serve = serveStatic(servePath, {
index: ["index.html", "index.htm"],
extensions: ["html", "htm"],
async function setupFrontendServer(servePath) {
const port = await getPorts();
const process = fork(servePath, {
env: {
PORT: port,
},
stdio: "pipe",
});

// Create server
var server = http.createServer(function onRequest(req, res) {
serve(req, res, finalhandler(req, res));
});
console.log("Waiting for UI thread to start...");

// Listen
server.listen(0);
await new Promise((resolve, reject) => {
process.stdout.on("data", (chunk) => {
line = chunk.toString();
console.log("Recieved data from child process:", line);
if (line.includes("Listening on port")) resolve();
});
});

return server;
return port;
}

module.exports = { setupFrontendServer };
11 changes: 8 additions & 3 deletions electron/Updates.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ async function setupMvmlUpdates(mvmlUpdate, window, app, opt = {}, x = 0) {
msg: "Created dist directory...",
type: "log",
});
}
if (fsSync.existsSync(mvmlUpdateDir)) {
} else if (fsSync.existsSync(mvmlUpdateDir)) {
await fs.rm(mvmlUpdateDir, { recursive: true, force: true });
await fs.mkdir(mvmlUpdateDir);

Expand Down Expand Up @@ -161,6 +160,12 @@ async function setupMvmlUpdates(mvmlUpdate, window, app, opt = {}, x = 0) {
type: "success",
});

fs.appendFile(path.join(mvmlUpdateDir, ".unzip-finished"), "true");
fs.rm(path.join(mvmlUpdateDir, "tmp.zip"), {
recursive: true,
force: true,
});

window.webContents.send("log-update", {
msg: "Update completed, now starting server...",
type: "prepare",
Expand Down Expand Up @@ -406,7 +411,7 @@ async function checkMvmlUpdates() {
const updateURL = mlUpdateURL + `mvml-${platform}-` + mlVersion + ".zip";
console.log("Checking for new Mvml at:", updateURL);
if (fsSync.existsSync(currentVersionPath))
currentVersion = await fs.readFile(currentVersionPath);
currentVersion = (await fs.readFile(currentVersionPath)).toString();

if (!fsSync.existsSync(path.join(__dirname, "../dist/.unzip-finished"))) {
// unzip did not complete
Expand Down
192 changes: 192 additions & 0 deletions electron/helpers/get-port.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
const net = require("node:net");
const os = require("node:os");

class Locked extends Error {
constructor(port) {
super(`${port} is locked`);
}
}

const lockedPorts = {
old: new Set(),
young: new Set(),
};

// On this interval, the old locked ports are discarded,
// the young locked ports are moved to old locked ports,
// and a new young set for locked ports are created.
const releaseOldLockedPortsIntervalMs = 1000 * 15;

const minPort = 1024;
const maxPort = 65_535;

// Lazily create timeout on first use
let timeout;

const getLocalHosts = () => {
const interfaces = os.networkInterfaces();

// Add undefined value for createServer function to use default host,
// and default IPv4 host in case createServer defaults to IPv6.
const results = new Set([undefined, "0.0.0.0"]);

for (const _interface of Object.values(interfaces)) {
for (const config of _interface) {
results.add(config.address);
}
}

return results;
};

const checkAvailablePort = (options) =>
new Promise((resolve, reject) => {
const server = net.createServer();
server.unref();
server.on("error", reject);

server.listen(options, () => {
const { port } = server.address();
server.close(() => {
resolve(port);
});
});
});

const getAvailablePort = async (options, hosts) => {
if (options.host || options.port === 0) {
return checkAvailablePort(options);
}

for (const host of hosts) {
try {
await checkAvailablePort({ port: options.port, host }); // eslint-disable-line no-await-in-loop
} catch (error) {
if (!["EADDRNOTAVAIL", "EINVAL"].includes(error.code)) {
throw error;
}
}
}

return options.port;
};

const portCheckSequence = function* (ports) {
if (ports) {
yield* ports;
}

yield 0; // Fall back to 0 if anything else failed
};

async function getPorts(options) {
let ports;
let exclude = new Set();

if (options) {
if (options.port) {
ports = typeof options.port === "number" ? [options.port] : options.port;
}

if (options.exclude) {
const excludeIterable = options.exclude;

if (typeof excludeIterable[Symbol.iterator] !== "function") {
throw new TypeError("The `exclude` option must be an iterable.");
}

for (const element of excludeIterable) {
if (typeof element !== "number") {
throw new TypeError(
"Each item in the `exclude` option must be a number corresponding to the port you want excluded."
);
}

if (!Number.isSafeInteger(element)) {
throw new TypeError(
`Number ${element} in the exclude option is not a safe integer and can't be used`
);
}
}

exclude = new Set(excludeIterable);
}
}

if (timeout === undefined) {
timeout = setTimeout(() => {
timeout = undefined;

lockedPorts.old = lockedPorts.young;
lockedPorts.young = new Set();
}, releaseOldLockedPortsIntervalMs);

// Does not exist in some environments (Electron, Jest jsdom env, browser, etc).
if (timeout.unref) {
timeout.unref();
}
}

const hosts = getLocalHosts();

for (const port of portCheckSequence(ports)) {
try {
if (exclude.has(port)) {
continue;
}

let availablePort = await getAvailablePort({ ...options, port }, hosts); // eslint-disable-line no-await-in-loop
while (
lockedPorts.old.has(availablePort) ||
lockedPorts.young.has(availablePort)
) {
if (port !== 0) {
throw new Locked(port);
}

availablePort = await getAvailablePort({ ...options, port }, hosts); // eslint-disable-line no-await-in-loop
}

lockedPorts.young.add(availablePort);

return availablePort;
} catch (error) {
if (
!["EADDRINUSE", "EACCES"].includes(error.code) &&
!(error instanceof Locked)
) {
throw error;
}
}
}

throw new Error("No available ports found");
}

function portNumbers(from, to) {
if (!Number.isInteger(from) || !Number.isInteger(to)) {
throw new TypeError("`from` and `to` must be integer numbers");
}

if (from < minPort || from > maxPort) {
throw new RangeError(`'from' must be between ${minPort} and ${maxPort}`);
}

if (to < minPort || to > maxPort) {
throw new RangeError(`'to' must be between ${minPort} and ${maxPort}`);
}

if (from > to) {
throw new RangeError("`to` must be greater than or equal to `from`");
}

const generator = function* (from, to) {
for (let port = from; port <= to; port++) {
yield port;
}
};

return generator(from, to);
}

module.exports = { getPorts, portNumbers };
52 changes: 26 additions & 26 deletions forge.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,32 @@ module.exports = {
},
packagerConfig: {
ignore: [
"renderer/src",
"renderer/public",
"renderer/.next",
"renderer/tailwind.config.js",
"renderer/README.md",
"renderer/postcss.config.js",
"renderer/.eslintrc.json",
"renderer/next.config.js",
"renderer/next-env.d.ts",
"renderer/out",
"ai/",
"common/",
"librosa/",
"_soundfile_data/",
"forge.config.js",
".gitignore",
"deploy_electron.py",
"deploy_ml.py",
"deploy_remove.py",
"pyinstallerbuild/",
"Makefile",
"Makefile.variable",
"tsconfig.json",
"tracking/",
"server/",
"dist/",
"^/renderer/src",
"^/renderer/public",
"^/renderer/.next",
"^/renderer/tailwind.config.js",
"^/renderer/README.md",
"^/renderer/postcss.config.js",
"^/renderer/.eslintrc.json",
"^/renderer/next.config.js",
"^/renderer/next-env.d.ts",
"^/renderer/out",
"^/ai",
"^/common",
"^/librosa",
"^/_soundfile_data",
"^/forge.config.js",
"^/.gitignore",
"^/deploy_electron.py",
"^/deploy_ml.py",
"^/deploy_remove.py",
"^/pyinstallerbuild/",
"^/Makefile",
"^/Makefile.variable",
"^/tsconfig.json",
"^/tracking",
"^/server",
"^/dist",
],
},
makers: [
Expand Down
9 changes: 4 additions & 5 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ app.whenReady().then(async () => {

frontendPort = IS_DEV ? 3000 : undefined;
if (!IS_DEV) {
frontendServer = setupFrontendServer(
path.join(__dirname, "renderer/build")
frontendPort = await setupFrontendServer(
path.join(__dirname, "renderer/build/standalone/renderer/server.js")
);
frontendPort = frontendServer.address().port;

if (!mvmlUpdate && !update) {
console.log(path.join(__dirname, "dist/metavoice/metavoice.exe"));
Expand All @@ -64,8 +63,8 @@ app.whenReady().then(async () => {
backendServer = setupBackendServer(
path.join(__dirname, "dist/metavoice/metavoice.exe")
);
console.log("Loading:", "http://localhost:" + port);
window.loadURL("http://localhost:" + port);
console.log("Loading:", "http://localhost:" + frontendPort);
window.loadURL("http://localhost:" + frontendPort);
},
});
}
Expand Down
Loading

0 comments on commit accf3b3

Please sign in to comment.