Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt standard monorepo conventions (apps/ for runnable apps, packages/ for reusable libraries). Update all config files, shared package imports, test fixtures, and documentation to reflect new paths. Key fixes: - Update workspace config to ["apps/*", "packages/*"] - Update tsconfig.json rootDir/include for apps/server/ - Add apps/web/** to vitest exclude list - Update drizzle.config.ts schema path - Fix ensure-schema.ts migration path detection (3 levels up in dev, 2 levels up in dist) - Fix tests/integration/cli-server.test.ts import paths - Update packages/shared imports to apps/server/ paths - Update all docs/ files with new paths
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
/**
|
|
* Port Allocator
|
|
*
|
|
* Finds the next available port for a preview deployment.
|
|
* Queries running preview containers and performs a bind test.
|
|
*/
|
|
|
|
import { createServer } from 'node:net';
|
|
import { getPreviewPorts } from './docker-client.js';
|
|
import { createModuleLogger } from '../logger/index.js';
|
|
|
|
const log = createModuleLogger('preview:port');
|
|
|
|
/** Starting port for preview deployments */
|
|
const BASE_PORT = 9100;
|
|
|
|
/** Maximum port to try before giving up */
|
|
const MAX_PORT = 9200;
|
|
|
|
/**
|
|
* Allocate the next available port for a preview deployment.
|
|
*
|
|
* 1. Queries running preview containers for their cw.port labels
|
|
* 2. Finds the next port >= BASE_PORT that isn't in use
|
|
* 3. Performs a bind test to verify no external conflict
|
|
*
|
|
* @returns An available port number
|
|
* @throws If no port is available in the range
|
|
*/
|
|
export async function allocatePort(): Promise<number> {
|
|
const usedPorts = new Set(await getPreviewPorts());
|
|
log.debug({ usedPorts: Array.from(usedPorts) }, 'ports in use by previews');
|
|
|
|
for (let port = BASE_PORT; port < MAX_PORT; port++) {
|
|
if (usedPorts.has(port)) continue;
|
|
|
|
if (await isPortAvailable(port)) {
|
|
log.info({ port }, 'allocated port');
|
|
return port;
|
|
}
|
|
}
|
|
|
|
throw new Error(`No available ports in range ${BASE_PORT}-${MAX_PORT}`);
|
|
}
|
|
|
|
/**
|
|
* Test if a port is available by attempting to bind to it.
|
|
*/
|
|
async function isPortAvailable(port: number): Promise<boolean> {
|
|
return new Promise((resolve) => {
|
|
const server = createServer();
|
|
|
|
server.once('error', () => {
|
|
resolve(false);
|
|
});
|
|
|
|
server.listen(port, '127.0.0.1', () => {
|
|
server.close(() => {
|
|
resolve(true);
|
|
});
|
|
});
|
|
});
|
|
}
|