overhaul
This commit is contained in:
66 files changed
+9227
-3679
No files matched your search
+83
-32
@@ -3,14 +3,19 @@ import { createDb } from './db/client.js';
|
||||
import { isPterodactylConfigured, loadEnv } from './env.js';
|
||||
import { createLogger } from './lib/logger.js';
|
||||
import { SessionService } from './modules/auth/session-service.js';
|
||||
import { ConfigEditorService } from './modules/config/config-editor-service.js';
|
||||
import { ConfigFileGateway } from './modules/config/config-file-gateway.js';
|
||||
import { ConfigSyncService } from './modules/config/config-sync.js';
|
||||
import { ServerModsService } from './modules/config/mods-service.js';
|
||||
import { PerformanceSettingsService } from './modules/config/performance-service.js';
|
||||
import { ServerMetricsService } from './modules/servers/metrics-service.js';
|
||||
import { ResourceHistoryService } from './modules/servers/resource-history.js';
|
||||
import { MissionCatalog } from './modules/reforger-logs/missions-catalog.js';
|
||||
import { MissionCatalog, MissionsService } from './modules/reforger-logs/missions-catalog.js';
|
||||
import type { ConsoleHub } from './modules/pterodactyl/console-hub.js';
|
||||
import { MockConsoleHub } from './modules/pterodactyl/mock-console-hub.js';
|
||||
import { MockGameServerProvider } from './modules/pterodactyl/mock-provider.js';
|
||||
import { PterodactylProvider } from './modules/pterodactyl/pterodactyl-provider.js';
|
||||
import { WingsConsoleHub } from './modules/pterodactyl/wings-socket.js';
|
||||
import type { GameServerProvider } from './modules/pterodactyl/types.js';
|
||||
import { DrizzleIngestionStore } from './modules/reforger-logs/ingestion/drizzle-store.js';
|
||||
import { LogIngestionService } from './modules/reforger-logs/ingestion/ingestion-service.js';
|
||||
@@ -18,6 +23,7 @@ import { createLogPathResolver } from './modules/reforger-logs/ingestion/log-pat
|
||||
import { PterodactylLogSource } from './modules/reforger-logs/ingestion/pterodactyl-log-source.js';
|
||||
import { IngestionScheduler } from './modules/reforger-logs/ingestion/scheduler.js';
|
||||
import { ServerService } from './modules/servers/server-service.js';
|
||||
import { WorkshopCache } from './modules/workshop/workshop-cache.js';
|
||||
import { WorkshopClient } from './modules/workshop/workshop-client.js';
|
||||
|
||||
const logger = createLogger();
|
||||
@@ -26,34 +32,72 @@ const env = loadEnv();
|
||||
const { db, pool } = createDb(env.DATABASE_URL);
|
||||
|
||||
const mockLogPath = env.REFORGER_ADMIN_LOG_PATH || '/profile/logs/console.log';
|
||||
const provider: GameServerProvider = env.USE_MOCK_PTERODACTYL
|
||||
const mockProvider = env.USE_MOCK_PTERODACTYL
|
||||
? new MockGameServerProvider({ logPath: mockLogPath })
|
||||
: new PterodactylProvider({
|
||||
baseUrl: env.PTERODACTYL_BASE_URL,
|
||||
apiKey: env.PTERODACTYL_CLIENT_API_KEY,
|
||||
});
|
||||
: null;
|
||||
const provider: GameServerProvider =
|
||||
mockProvider ??
|
||||
new PterodactylProvider({
|
||||
baseUrl: env.PTERODACTYL_BASE_URL,
|
||||
apiKey: env.PTERODACTYL_CLIENT_API_KEY,
|
||||
});
|
||||
|
||||
const sessions = new SessionService(db, env.OWNER_DISCORD_ID);
|
||||
const servers = new ServerService(db);
|
||||
const workshop = new WorkshopClient({ baseUrl: env.REFORGER_WORKSHOP_API_BASE_URL });
|
||||
const configSync = isPterodactylConfigured(env)
|
||||
? new ConfigSyncService(provider, servers, logger, env.REFORGER_CONFIG_PATH)
|
||||
: null;
|
||||
const gateway = new ConfigFileGateway(provider, env.REFORGER_CONFIG_PATH);
|
||||
const mods = configSync ? new ServerModsService(gateway, configSync, logger) : null;
|
||||
const performance = configSync ? new PerformanceSettingsService(gateway, configSync, logger) : null;
|
||||
const resourceHistory = new ResourceHistoryService(provider, logger);
|
||||
|
||||
// Log ingestion runs when a backend is configured and we know where logs live.
|
||||
const logsConfigured =
|
||||
isPterodactylConfigured(env) &&
|
||||
Boolean(env.REFORGER_ADMIN_LOG_PATH || env.REFORGER_LOG_DIRECTORY || env.USE_MOCK_PTERODACTYL);
|
||||
const workshop = new WorkshopCache(
|
||||
new WorkshopClient({
|
||||
baseUrl: env.REFORGER_WORKSHOP_API_BASE_URL,
|
||||
apiKey: env.REFORGER_WORKSHOP_API_KEY,
|
||||
}),
|
||||
logger,
|
||||
);
|
||||
|
||||
const primaryServer = (await servers.listServers())[0] ?? null;
|
||||
const providerServerId = primaryServer
|
||||
? (primaryServer.pterodactylServerId ?? primaryServer.slug)
|
||||
: '';
|
||||
|
||||
/**
|
||||
* The live console feed. Under mock mode it is simulated; against a real
|
||||
* Pterodactyl it is the Wings websocket, which is the only source that shows
|
||||
* install/update/mod-download output as it happens.
|
||||
*/
|
||||
let consoleHub: ConsoleHub | null = null;
|
||||
if (mockProvider) {
|
||||
consoleHub = new MockConsoleHub(mockProvider);
|
||||
} else if (isPterodactylConfigured(env) && env.PTERODACTYL_WEBSOCKET_ENABLED && providerServerId) {
|
||||
consoleHub = new WingsConsoleHub({
|
||||
baseUrl: env.PTERODACTYL_BASE_URL,
|
||||
apiKey: env.PTERODACTYL_CLIENT_API_KEY,
|
||||
serverId: providerServerId,
|
||||
logger,
|
||||
});
|
||||
} else {
|
||||
logger.info('live console disabled (websocket off or backend not configured)');
|
||||
}
|
||||
|
||||
const gateway = new ConfigFileGateway(provider, env.REFORGER_CONFIG_PATH);
|
||||
const configured = isPterodactylConfigured(env);
|
||||
const configSync = configured ? new ConfigSyncService(gateway, servers, logger) : null;
|
||||
const configEditor = configSync
|
||||
? new ConfigEditorService(gateway, provider, configSync, logger)
|
||||
: null;
|
||||
const mods = configured ? new ServerModsService(gateway, workshop, logger) : null;
|
||||
const performance = configEditor
|
||||
? new PerformanceSettingsService(gateway, configEditor, logger)
|
||||
: null;
|
||||
|
||||
const metrics = new ServerMetricsService(provider, consoleHub);
|
||||
const resourceHistory = new ResourceHistoryService(metrics, logger);
|
||||
|
||||
// Log ingestion runs when a backend is configured and we know where logs live.
|
||||
// It no longer powers the console view — only player sessions and killfeed,
|
||||
// which are parsed out of the game's own log file.
|
||||
const logsConfigured =
|
||||
configured &&
|
||||
Boolean(env.REFORGER_ADMIN_LOG_PATH || env.REFORGER_LOG_DIRECTORY || env.USE_MOCK_PTERODACTYL);
|
||||
|
||||
const resolveLogPath =
|
||||
logsConfigured && primaryServer
|
||||
? createLogPathResolver({
|
||||
@@ -65,10 +109,11 @@ const resolveLogPath =
|
||||
})
|
||||
: null;
|
||||
|
||||
const missions =
|
||||
const missionCatalog =
|
||||
resolveLogPath && primaryServer
|
||||
? new MissionCatalog(provider, resolveLogPath, providerServerId)
|
||||
: null;
|
||||
const missions = new MissionsService(workshop, missionCatalog);
|
||||
|
||||
let scheduler: IngestionScheduler | null = null;
|
||||
if (resolveLogPath) {
|
||||
@@ -94,10 +139,13 @@ const app = createApp({
|
||||
sessions,
|
||||
servers,
|
||||
provider,
|
||||
metrics,
|
||||
consoleHub,
|
||||
workshop,
|
||||
scheduler,
|
||||
resolveLogPath,
|
||||
configSync,
|
||||
configEditor,
|
||||
mods,
|
||||
performance,
|
||||
resourceHistory,
|
||||
@@ -111,6 +159,8 @@ const httpServer = app.listen(env.PORT, () => {
|
||||
);
|
||||
});
|
||||
|
||||
consoleHub?.start();
|
||||
|
||||
if (scheduler && resolveLogPath && primaryServer) {
|
||||
scheduler.start([
|
||||
{
|
||||
@@ -121,20 +171,21 @@ if (scheduler && resolveLogPath && primaryServer) {
|
||||
]);
|
||||
}
|
||||
|
||||
if (primaryServer && isPterodactylConfigured(env)) {
|
||||
if (primaryServer && configured) {
|
||||
resourceHistory.start([{ serverId: primaryServer.id, providerServerId }]);
|
||||
}
|
||||
|
||||
// Import the real config.json at startup and on an interval so the panel
|
||||
// always reflects what the server actually runs.
|
||||
let configSyncTimer: ReturnType<typeof setInterval> | null = null;
|
||||
if (configSync) {
|
||||
void configSync.syncAllQuietly();
|
||||
configSyncTimer = setInterval(
|
||||
() => void configSync.syncAllQuietly(),
|
||||
env.REFORGER_CONFIG_SYNC_INTERVAL_SECONDS * 1000,
|
||||
);
|
||||
configSyncTimer.unref();
|
||||
// Import the real config.json once at startup so the panel reflects what the
|
||||
// server actually runs. Writes trigger their own sync, and every read is live,
|
||||
// so there is deliberately no background polling loop here.
|
||||
if (configSync && primaryServer) {
|
||||
void configSync
|
||||
.syncAllQuietly()
|
||||
.then(() => mods?.getMods(primaryServer))
|
||||
// Prime the Workshop cache with the installed mod list so the first visit
|
||||
// to the Mods page renders without waiting on the network.
|
||||
.then((installed) => workshop.warm((installed?.mods ?? []).map((mod) => mod.modId)))
|
||||
.catch(() => undefined);
|
||||
}
|
||||
|
||||
// Hourly cleanup of expired sessions.
|
||||
@@ -151,10 +202,10 @@ async function shutdown(signal: string) {
|
||||
logger.info({ signal }, 'shutting down');
|
||||
httpServer.close();
|
||||
clearInterval(sessionCleanup);
|
||||
if (configSyncTimer) clearInterval(configSyncTimer);
|
||||
resourceHistory.stop();
|
||||
await consoleHub?.stop();
|
||||
if (scheduler) await scheduler.stop();
|
||||
if (provider instanceof MockGameServerProvider) provider.dispose();
|
||||
if (mockProvider) mockProvider.dispose();
|
||||
await pool.end();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user