Fix production TypeScript builds and add DEPLOY.md so the app can be hosted from this repo. Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
2.7 KiB
JavaScript
101 lines
2.7 KiB
JavaScript
/**
|
|
* Prepare local dev: free backend port and ensure MySQL is reachable.
|
|
* Runs automatically via npm "predev" before `npm run dev`.
|
|
*/
|
|
import { execSync } from 'node:child_process';
|
|
import net from 'node:net';
|
|
import { platform } from 'node:os';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const MYSQL_PORT = 3306;
|
|
const BACKEND_LISTEN_PORT = 3001;
|
|
const FRONTEND_PORT = 5173;
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const rootDir = join(__dirname, '..');
|
|
const backendDir = join(rootDir, 'backend');
|
|
|
|
function log(msg) {
|
|
console.log(`[prepare-dev] ${msg}`);
|
|
}
|
|
|
|
function isPortOpen(port, host = '127.0.0.1') {
|
|
return new Promise((resolve) => {
|
|
const socket = net.createConnection({ port, host }, () => {
|
|
socket.destroy();
|
|
resolve(true);
|
|
});
|
|
socket.on('error', () => resolve(false));
|
|
socket.setTimeout(1500, () => {
|
|
socket.destroy();
|
|
resolve(false);
|
|
});
|
|
});
|
|
}
|
|
|
|
function freePort(port) {
|
|
if (platform() === 'win32') {
|
|
try {
|
|
const out = execSync(`netstat -ano | findstr :${port}`, { encoding: 'utf8' });
|
|
const pids = new Set();
|
|
for (const line of out.split('\n')) {
|
|
if (!line.includes('LISTENING')) continue;
|
|
const parts = line.trim().split(/\s+/);
|
|
const pid = parts[parts.length - 1];
|
|
if (pid && pid !== '0') pids.add(pid);
|
|
}
|
|
for (const pid of pids) {
|
|
log(`Stopping process ${pid} on port ${port}`);
|
|
execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
|
|
}
|
|
} catch {
|
|
// nothing listening
|
|
}
|
|
return;
|
|
}
|
|
|
|
try {
|
|
execSync(`lsof -ti:${port} | xargs kill -9 2>/dev/null`, { stdio: 'ignore', shell: true });
|
|
} catch {
|
|
// nothing listening
|
|
}
|
|
}
|
|
|
|
async function ensureMysql() {
|
|
if (await isPortOpen(MYSQL_PORT)) {
|
|
log(`MySQL is reachable on port ${MYSQL_PORT}`);
|
|
return;
|
|
}
|
|
|
|
log(`MySQL not reachable on port ${MYSQL_PORT}; starting Docker Compose...`);
|
|
try {
|
|
execSync('docker compose -f docker-compose.dev.yml up -d', {
|
|
cwd: backendDir,
|
|
stdio: 'inherit',
|
|
});
|
|
} catch {
|
|
console.error(
|
|
'[prepare-dev] ERROR: MySQL is not running. Start it with:\n' +
|
|
' cd backend && docker compose -f docker-compose.dev.yml up -d',
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
for (let i = 0; i < 30; i += 1) {
|
|
if (await isPortOpen(MYSQL_PORT)) {
|
|
log('MySQL is ready');
|
|
return;
|
|
}
|
|
await new Promise((r) => setTimeout(r, 1000));
|
|
}
|
|
|
|
console.error('[prepare-dev] ERROR: MySQL did not become ready in time.');
|
|
process.exit(1);
|
|
}
|
|
|
|
freePort(BACKEND_LISTEN_PORT);
|
|
freePort(FRONTEND_PORT);
|
|
await ensureMysql();
|
|
log(`Ready — backend will listen on port ${BACKEND_LISTEN_PORT}`);
|