FreeLLMAPI / desktop /src /server-host.ts
Nryn215's picture
Upload folder using huggingface_hub
077865a verified
Raw
History Blame Contribute Delete
2.72 kB
// Bundled by scripts/bundle-server.mjs into build/server.mjs (ESM, only
// better-sqlite3 external). This is the ONLY module allowed to touch server
// internals: the db singleton lives inside this bundle, so anything stateful
// (auth bootstrap, getDb) must be exported from here rather than imported
// from server/src by the main bundle (which would get a second, empty copy).
//
// The server sources live in this same repo (../../server). Keep these
// relative imports in sync with the repo-root default in main.ts.
import '../../server/src/env.js';
import crypto from 'node:crypto';
import type { Server } from 'node:http';
import { createApp } from '../../server/src/app.js';
import { initDb, getDb, getUnifiedApiKey } from '../../server/src/db/index.js';
import { startHealthChecker } from '../../server/src/services/health.js';
import { userCount, createUser, createSession } from '../../server/src/services/auth.js';
export { getDb, getUnifiedApiKey };
export interface StartOptions {
dbPath: string;
clientDist: string;
host: string;
preferredPort: number;
}
export interface ServerHandle {
server: Server;
port: number;
}
export async function startServer(opts: StartOptions): Promise<ServerHandle> {
process.env.CLIENT_DIST = opts.clientDist;
initDb(opts.dbPath);
const app = createApp();
const { server, port } = await listenWithScan(app, opts.host, opts.preferredPort);
startHealthChecker();
return { server, port };
}
// The dashboard window authenticates as a hidden machine user. The password
// is random and never shown — sessions are minted directly against the DB.
export function ensureSessionToken(): string {
if (userCount() === 0) {
createUser('desktop@localhost', crypto.randomBytes(24).toString('hex'));
}
const first = getDb().prepare('SELECT id FROM users ORDER BY id ASC LIMIT 1').get() as { id: number };
return createSession(first.id);
}
async function listenWithScan(
app: ReturnType<typeof createApp>,
host: string,
start: number,
attempts = 50,
): Promise<{ server: Server; port: number }> {
for (let port = start; port < start + attempts; port++) {
const server = await tryListen(app, host, port);
if (server) return { server, port };
}
throw new Error(`No free port found in ${start}${start + attempts - 1}`);
}
function tryListen(app: ReturnType<typeof createApp>, host: string, port: number): Promise<Server | null> {
return new Promise((resolve) => {
const server = app.listen(port, host);
server.once('listening', () => resolve(server));
server.once('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EADDRINUSE') resolve(null);
else resolve(null);
});
});
}