223 lines
5.1 KiB
JavaScript
223 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const net = require("net");
|
|
|
|
const PORTS_FILE = path.join(__dirname, "..", ".dev-ports.json");
|
|
const DEV_ASSETS_SEED = path.join(__dirname, "..", "dev_assets_seed");
|
|
const DEV_ASSETS = path.join(__dirname, "..", "dev_assets");
|
|
|
|
/**
|
|
* Check if a port is available
|
|
*/
|
|
function isPortAvailable(port) {
|
|
return new Promise((resolve) => {
|
|
const sock = net.createConnection({ port, host: "localhost" });
|
|
sock.on("connect", () => {
|
|
sock.destroy();
|
|
resolve(false);
|
|
});
|
|
sock.on("error", () => resolve(true));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Find a free port starting from a given port
|
|
*/
|
|
async function findFreePort(startPort = 3000) {
|
|
let port = startPort;
|
|
while (!(await isPortAvailable(port))) {
|
|
port++;
|
|
if (port > 65535) {
|
|
throw new Error("No available ports found");
|
|
}
|
|
}
|
|
return port;
|
|
}
|
|
|
|
/**
|
|
* Load existing ports from file
|
|
*/
|
|
function loadPorts() {
|
|
try {
|
|
if (fs.existsSync(PORTS_FILE)) {
|
|
const data = fs.readFileSync(PORTS_FILE, "utf8");
|
|
return JSON.parse(data);
|
|
}
|
|
} catch (error) {
|
|
console.warn("Failed to load existing ports:", error.message);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Save ports to file
|
|
*/
|
|
function savePorts(ports) {
|
|
try {
|
|
fs.writeFileSync(PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
} catch (error) {
|
|
console.error("Failed to save ports:", error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify that saved ports are still available
|
|
*/
|
|
async function verifyPorts(ports) {
|
|
const frontendAvailable = await isPortAvailable(ports.frontend);
|
|
const backendAvailable = await isPortAvailable(ports.backend);
|
|
|
|
if (process.argv[2] === "get" && (!frontendAvailable || !backendAvailable)) {
|
|
console.log(
|
|
`Port availability check failed: frontend:${ports.frontend}=${frontendAvailable}, backend:${ports.backend}=${backendAvailable}`
|
|
);
|
|
}
|
|
|
|
return frontendAvailable && backendAvailable;
|
|
}
|
|
|
|
/**
|
|
* Allocate ports for development
|
|
*/
|
|
async function allocatePorts() {
|
|
// Try to load existing ports first
|
|
const existingPorts = loadPorts();
|
|
|
|
if (existingPorts) {
|
|
// Verify existing ports are still available
|
|
if (await verifyPorts(existingPorts)) {
|
|
if (process.argv[2] === "get") {
|
|
console.log("Reusing existing dev ports:");
|
|
console.log(`Frontend: ${existingPorts.frontend}`);
|
|
console.log(`Backend: ${existingPorts.backend}`);
|
|
}
|
|
return existingPorts;
|
|
} else {
|
|
if (process.argv[2] === "get") {
|
|
console.log(
|
|
"Existing ports are no longer available, finding new ones..."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find new free ports
|
|
const frontendPort = await findFreePort(3000);
|
|
const backendPort = await findFreePort(frontendPort + 1);
|
|
|
|
const ports = {
|
|
frontend: frontendPort,
|
|
backend: backendPort,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
|
|
savePorts(ports);
|
|
|
|
if (process.argv[2] === "get") {
|
|
console.log("Allocated new dev ports:");
|
|
console.log(`Frontend: ${ports.frontend}`);
|
|
console.log(`Backend: ${ports.backend}`);
|
|
}
|
|
|
|
return ports;
|
|
}
|
|
|
|
/**
|
|
* Get ports (allocate if needed)
|
|
*/
|
|
async function getPorts() {
|
|
const ports = await allocatePorts();
|
|
copyDevAssets();
|
|
return ports;
|
|
}
|
|
|
|
/**
|
|
* Copy dev_assets_seed to dev_assets
|
|
*/
|
|
function copyDevAssets() {
|
|
try {
|
|
if (!fs.existsSync(DEV_ASSETS)) {
|
|
// Copy dev_assets_seed to dev_assets
|
|
fs.cpSync(DEV_ASSETS_SEED, DEV_ASSETS, { recursive: true });
|
|
|
|
if (process.argv[2] === "get") {
|
|
console.log("Copied dev_assets_seed to dev_assets");
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to copy dev assets:", error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear saved ports
|
|
*/
|
|
function clearPorts() {
|
|
try {
|
|
if (fs.existsSync(PORTS_FILE)) {
|
|
fs.unlinkSync(PORTS_FILE);
|
|
console.log("Cleared saved dev ports");
|
|
} else {
|
|
console.log("No saved ports to clear");
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to clear ports:", error.message);
|
|
}
|
|
}
|
|
|
|
// CLI interface
|
|
if (require.main === module) {
|
|
const command = process.argv[2];
|
|
|
|
switch (command) {
|
|
case "get":
|
|
getPorts()
|
|
.then((ports) => {
|
|
console.log(JSON.stringify(ports));
|
|
})
|
|
.catch(console.error);
|
|
break;
|
|
|
|
case "clear":
|
|
clearPorts();
|
|
break;
|
|
|
|
case "frontend":
|
|
getPorts()
|
|
.then((ports) => {
|
|
console.log(ports.frontend);
|
|
})
|
|
.catch(console.error);
|
|
break;
|
|
|
|
case "backend":
|
|
getPorts()
|
|
.then((ports) => {
|
|
console.log(ports.backend);
|
|
})
|
|
.catch(console.error);
|
|
break;
|
|
|
|
default:
|
|
console.log("Usage:");
|
|
console.log(
|
|
" node setup-dev-environment.js get - Setup dev environment (ports + assets)"
|
|
);
|
|
console.log(
|
|
" node setup-dev-environment.js frontend - Get frontend port only"
|
|
);
|
|
console.log(
|
|
" node setup-dev-environment.js backend - Get backend port only"
|
|
);
|
|
console.log(
|
|
" node setup-dev-environment.js clear - Clear saved ports"
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
module.exports = { getPorts, clearPorts, findFreePort };
|