webssh2-server
Version:
A Websocket to SSH2 gateway using xterm.js, socket.io, ssh2
187 lines (186 loc) • 6.07 kB
JavaScript
/**
* 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
};
}