Add runner ci (test)

This commit is contained in:
2026-03-10 23:56:53 +01:00
parent c3fbd7d4fe
commit f5bf18b739
21 changed files with 1372 additions and 39 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -3,7 +3,9 @@
"version": "1.0.0",
"description": "Signaling server for MetoYou P2P chat application",
"main": "dist/index.js",
"bin": "dist/index.js",
"scripts": {
"prebuild": "node ../tools/sync-server-build-version.js",
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node-dev --respawn src/index.ts"
@@ -27,5 +29,13 @@
"@types/ws": "^8.5.8",
"ts-node-dev": "^2.0.0",
"typescript": "^5.2.2"
},
"pkg": {
"assets": [
"node_modules/ansis/**/*"
],
"scripts": [
"dist/**/*.js"
]
}
}

View File

@@ -1,12 +1,13 @@
import fs from 'fs';
import path from 'path';
import { resolveRuntimePath } from '../runtime-paths';
export interface ServerVariablesConfig {
klipyApiKey: string;
releaseManifestUrl: string;
}
const DATA_DIR = path.join(process.cwd(), 'data');
const DATA_DIR = resolveRuntimePath('data');
const VARIABLES_FILE = path.join(DATA_DIR, 'variables.json');
function normalizeKlipyApiKey(value: unknown): string {

View File

@@ -1,4 +1,4 @@
import { getDataSource } from '../db';
import { getDataSource } from '../db/database';
import {
CommandType,
QueryType,

View File

@@ -6,12 +6,27 @@ import {
ServerEntity,
JoinRequestEntity
} from '../entities';
import { serverMigrations } from '../migrations';
import { findExistingPath, resolveRuntimePath } from '../runtime-paths';
const DATA_DIR = path.join(process.cwd(), 'data');
const DATA_DIR = resolveRuntimePath('data');
const DB_FILE = path.join(DATA_DIR, 'metoyou.sqlite');
let applicationDataSource: DataSource | undefined;
function resolveSqlJsConfig(): { locateFile: (file: string) => string } {
return {
locateFile: (file) => {
const bundledBinaryPath = path.join(__dirname, '..', '..', 'node_modules', 'sql.js', 'dist', file);
return findExistingPath(
resolveRuntimePath(file),
bundledBinaryPath
) ?? bundledBinaryPath;
}
};
}
export function getDataSource(): DataSource {
if (!applicationDataSource?.isInitialized) {
throw new Error('DataSource not initialised');
@@ -29,22 +44,34 @@ export async function initDatabase(): Promise<void> {
if (fs.existsSync(DB_FILE))
database = fs.readFileSync(DB_FILE);
applicationDataSource = new DataSource({
type: 'sqljs',
database,
entities: [
AuthUserEntity,
ServerEntity,
JoinRequestEntity
],
migrations: [path.join(__dirname, '..', 'migrations', '*.js'), path.join(__dirname, '..', 'migrations', '*.ts')],
synchronize: false,
logging: false,
autoSave: true,
location: DB_FILE
});
try {
applicationDataSource = new DataSource({
type: 'sqljs',
database,
entities: [
AuthUserEntity,
ServerEntity,
JoinRequestEntity
],
migrations: serverMigrations,
synchronize: false,
logging: false,
autoSave: true,
location: DB_FILE,
sqlJsConfig: resolveSqlJsConfig()
});
} catch (error) {
console.error('[DB] Failed to configure the sql.js data source', error);
throw error;
}
try {
await applicationDataSource.initialize();
} catch (error) {
console.error('[DB] Failed to initialise the sql.js data source', error);
throw error;
}
await applicationDataSource.initialize();
console.log('[DB] Connection initialised at:', DB_FILE);
await applicationDataSource.runMigrations();

View File

@@ -0,0 +1 @@
export const SERVER_BUILD_VERSION = "1.0.0";

View File

@@ -4,11 +4,15 @@ import path from 'path';
import fs from 'fs';
import { createServer as createHttpServer } from 'http';
import { createServer as createHttpsServer } from 'https';
import {
resolveCertificateDirectory,
resolveEnvFilePath
} from './runtime-paths';
// Load .env from project root (one level up from server/)
dotenv.config({ path: path.resolve(__dirname, '..', '..', '.env') });
dotenv.config({ path: resolveEnvFilePath() });
import { initDatabase } from './db';
import { initDatabase } from './db/database';
import { deleteStaleJoinRequests } from './cqrs';
import { createApp } from './app';
import {
@@ -23,7 +27,7 @@ const PORT = process.env.PORT || 3001;
function buildServer(app: ReturnType<typeof createApp>) {
if (USE_SSL) {
const certDir = path.resolve(__dirname, '..', '..', '.certs');
const certDir = resolveCertificateDirectory();
const certFile = path.join(certDir, 'localhost.crt');
const keyFile = path.join(certDir, 'localhost.key');

View File

@@ -0,0 +1,3 @@
import { InitialSchema1000000000000 } from './1000000000000-InitialSchema';
export const serverMigrations = [InitialSchema1000000000000];

View File

@@ -1,24 +1,15 @@
import { Router } from 'express';
import fs from 'fs';
import path from 'path';
import { getAllPublicServers } from '../cqrs';
import { getReleaseManifestUrl } from '../config/variables';
import { SERVER_BUILD_VERSION } from '../generated/build-version';
import { connectedUsers } from '../websocket/state';
const router = Router();
function getServerProjectVersion(): string {
try {
const packageJsonPath = path.join(process.cwd(), 'package.json');
const rawContents = fs.readFileSync(packageJsonPath, 'utf8');
const parsed = JSON.parse(rawContents) as { version?: unknown };
return typeof parsed.version === 'string' && parsed.version.trim().length > 0
? parsed.version.trim()
: '0.0.0';
} catch {
return '0.0.0';
}
return typeof process.env.METOYOU_SERVER_VERSION === 'string' && process.env.METOYOU_SERVER_VERSION.trim().length > 0
? process.env.METOYOU_SERVER_VERSION.trim()
: SERVER_BUILD_VERSION;
}
router.get('/health', async (_req, res) => {

View File

@@ -0,0 +1,58 @@
import fs from 'fs';
import path from 'path';
type PackagedProcess = NodeJS.Process & { pkg?: unknown };
function uniquePaths(paths: string[]): string[] {
return [...new Set(paths.map((candidate) => path.resolve(candidate)))];
}
export function isPackagedRuntime(): boolean {
return Boolean((process as PackagedProcess).pkg);
}
export function getRuntimeBaseDir(): string {
return isPackagedRuntime()
? path.dirname(process.execPath)
: process.cwd();
}
export function resolveRuntimePath(...segments: string[]): string {
return path.join(getRuntimeBaseDir(), ...segments);
}
export function resolveProjectRootPath(...segments: string[]): string {
return path.resolve(__dirname, '..', '..', ...segments);
}
export function findExistingPath(...candidates: string[]): string | null {
for (const candidate of uniquePaths(candidates)) {
if (fs.existsSync(candidate)) {
return candidate;
}
}
return null;
}
export function resolveEnvFilePath(): string {
if (isPackagedRuntime()) {
return resolveRuntimePath('.env');
}
return findExistingPath(
resolveRuntimePath('.env'),
resolveProjectRootPath('.env')
) ?? resolveProjectRootPath('.env');
}
export function resolveCertificateDirectory(): string {
if (isPackagedRuntime()) {
return resolveRuntimePath('.certs');
}
return findExistingPath(
resolveRuntimePath('.certs'),
resolveProjectRootPath('.certs')
) ?? resolveRuntimePath('.certs');
}