UNPKG

webssh2-server

Version:

A Websocket to SSH2 gateway using xterm.js, socket.io, ssh2

187 lines (186 loc) 6.07 kB
/** * Service factory for creating and wiring up all services */ import { AuthServiceImpl } from './auth/auth-service.js'; import { SSHServiceImpl } from './ssh/ssh-service.js'; import { TerminalServiceImpl } from './terminal/terminal-service.js'; import { SessionServiceImpl } from './session/session-service.js'; import { SessionStore } from '../state/store.js'; import { createSessionId, createUserId, createConnectionId } from '../types/branded.js'; import { createInitialState } from '../state/types.js'; import { Client as SSH2Client } from 'ssh2'; import debug from 'debug'; const factoryLogger = debug('webssh2:services:factory'); /** * Default logger implementation */ export class DefaultLogger { debugLogger; constructor(namespace = 'webssh2') { this.debugLogger = debug(namespace); } debug(message, meta) { if (meta === undefined) { this.debugLogger(message); } else { this.debugLogger(message, meta); } } info(message, meta) { if (meta === undefined) { this.debugLogger('[INFO]', message); } else { this.debugLogger('[INFO]', message, meta); } } warn(message, meta) { if (meta === undefined) { this.debugLogger('[WARN]', message); } else { this.debugLogger('[WARN]', message, meta); } } error(message, error, meta) { if (error === undefined && meta === undefined) { this.debugLogger('[ERROR]', message); } else if (error === undefined) { this.debugLogger('[ERROR]', message, meta); } else if (meta === undefined) { this.debugLogger('[ERROR]', message, error.message, error.stack); } else { this.debugLogger('[ERROR]', message, error.message, error.stack, meta); } } } /** * Create all services with dependencies */ export function createServices(deps) { factoryLogger('Creating services'); // Create service implementations const auth = new AuthServiceImpl(deps, deps.store); const ssh = new SSHServiceImpl(deps, deps.store); const terminal = new TerminalServiceImpl(deps, deps.store); const session = new SessionServiceImpl(deps, deps.store); const services = { auth, ssh, terminal, session }; factoryLogger('Services created successfully'); return services; } /** * Create a logger instance */ export function createLogger(config, namespace) { // Use provided namespace or get from config const logNamespace = namespace ?? config.logging?.namespace ?? 'webssh2'; return new DefaultLogger(logNamespace); } /** * Create a session store */ export function createSessionStore(config) { const maxHistorySize = config.session.maxHistorySize ?? 100; return new SessionStore({ maxHistorySize }); } /** * Bootstrap all services and dependencies */ export function bootstrapServices(config) { factoryLogger('Bootstrapping services'); // Create core dependencies const logger = createLogger(config); const store = createSessionStore(config); // Create extended dependencies const deps = { config, logger, store }; // Create services const services = createServices(deps); factoryLogger('Bootstrap complete'); return { services, store, logger }; } /** * Create mock services for testing * Note: Import vi from vitest when using this function */ export function createMockServices() { const mockAuthResult = { sessionId: createSessionId('test-session'), userId: createUserId('test-user'), username: 'testuser', method: 'manual', expiresAt: Date.now() + 86400000 }; const mockSSHConnection = { id: createConnectionId('test-conn'), sessionId: createSessionId('test-session'), client: new SSH2Client(), status: 'connected', createdAt: Date.now(), lastActivity: Date.now() }; const mockTerminal = { id: 'test-terminal', sessionId: createSessionId('test-session'), term: 'xterm-256color', rows: 24, cols: 80, env: {} }; const mockSession = { id: createSessionId('test-session'), state: createInitialState(createSessionId('test-session')), createdAt: Date.now(), updatedAt: Date.now() }; const mockStream = {}; const mockAuthService = { authenticate: () => Promise.resolve({ ok: true, value: mockAuthResult }), validateSession: () => ({ ok: true, value: true }), revokeSession: () => Promise.resolve({ ok: true, value: undefined }), getSessionInfo: () => ({ ok: true, value: null }) }; const mockSSHService = { connect: () => Promise.resolve({ ok: true, value: mockSSHConnection }), shell: () => Promise.resolve({ ok: true, value: mockStream }), exec: () => Promise.resolve({ ok: true, value: { stdout: '', stderr: '', code: 0 } }), disconnect: () => Promise.resolve({ ok: true, value: undefined }), getConnectionStatus: () => ({ ok: true, value: null }) }; const mockTerminalService = { create: () => ({ ok: true, value: mockTerminal }), resize: () => ({ ok: true, value: undefined }), write: () => ({ ok: true, value: undefined }), destroy: () => ({ ok: true, value: undefined }), getTerminal: () => ({ ok: true, value: null }) }; const mockSessionService = { create: () => ({ ok: true, value: mockSession }), get: () => ({ ok: true, value: null }), update: () => ({ ok: true, value: mockSession }), delete: () => ({ ok: true, value: undefined }), list: () => ({ ok: true, value: [] }) }; return { auth: mockAuthService, ssh: mockSSHService, terminal: mockTerminalService, session: mockSessionService }; }