refactor(hosts): group host modules into per-feature directories

file-manager/, metrics/ (incl. widgets, managers, alert-engine),
terminal/, tmux/ and tunnel/ each own their files; docker/ gains
container-runtime. Genuinely shared helpers (jump-host chain, host
resolver, connection pool, opkssh, vault, serial) stay at hosts/ root.
Pure file moves with import path updates; mirrored test paths follow.
This commit is contained in:
LukeGus
2026-07-16 04:34:22 -05:00
parent 7f43932429
commit 9c8528e4d2
82 changed files with 233 additions and 232 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import express from "express";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { databaseLogger } from "../../utils/logger.js"; import { databaseLogger } from "../../utils/logger.js";
import { AuthManager } from "../../utils/auth-manager.js"; import { AuthManager } from "../../utils/auth-manager.js";
import { sessionManager } from "../../hosts/terminal-session-manager.js"; import { sessionManager } from "../../hosts/terminal/session-manager.js";
import { import {
getCurrentSettingValue, getCurrentSettingValue,
createCurrentOpenTabRepository, createCurrentOpenTabRepository,
+1 -1
View File
@@ -10,7 +10,7 @@ import {
containerCommand, containerCommand,
getContainerRuntimeConfig, getContainerRuntimeConfig,
type ContainerRuntime, type ContainerRuntime,
} from "../container-runtime.js"; } from "./container-runtime.js";
import { resolveSshConnectConfigHost } from "../ssh-dns.js"; import { resolveSshConnectConfigHost } from "../ssh-dns.js";
const sshLogger = systemLogger; const sshLogger = systemLogger;
+1 -1
View File
@@ -3,7 +3,7 @@ import { logger } from "../../utils/logger.js";
import { import {
containerCommand, containerCommand,
type ContainerRuntime, type ContainerRuntime,
} from "../container-runtime.js"; } from "./container-runtime.js";
const sshLogger = logger; const sshLogger = logger;
+1 -1
View File
@@ -28,7 +28,7 @@ import {
containerCommand, containerCommand,
getContainerRuntimeConfig, getContainerRuntimeConfig,
getRuntimeLabel, getRuntimeLabel,
} from "../container-runtime.js"; } from "./container-runtime.js";
import { resolveSshConnectConfigHost } from "../ssh-dns.js"; import { resolveSshConnectConfigHost } from "../ssh-dns.js";
import { import {
type SSHSession, type SSHSession,
+1 -1
View File
@@ -1,6 +1,6 @@
import { Client as SSHClient } from "ssh2"; import { Client as SSHClient } from "ssh2";
import { logger } from "../../utils/logger.js"; import { logger } from "../../utils/logger.js";
import type { ContainerRuntime } from "../container-runtime.js"; import type { ContainerRuntime } from "./container-runtime.js";
const sshLogger = logger; const sshLogger = logger;
@@ -1,7 +1,7 @@
import type { Express } from "express"; import type { Express } from "express";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { fileLogger } from "../utils/logger.js"; import { fileLogger } from "../../utils/logger.js";
import { execChannel, type SSHSession } from "./file-manager-session.js"; import { execChannel, type SSHSession } from "./session.js";
type FileActionRoutesDeps = { type FileActionRoutesDeps = {
sshSessions: Record<string, SSHSession>; sshSessions: Record<string, SSHSession>;
@@ -1,15 +1,15 @@
import type { Express, Request, Response } from "express"; import type { Express, Request, Response } from "express";
import Busboy from "busboy"; import Busboy from "busboy";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { fileLogger } from "../utils/logger.js"; import { fileLogger } from "../../utils/logger.js";
import { import {
execChannel, execChannel,
execWithSudo, execWithSudo,
execWithSudoBuffer, execWithSudoBuffer,
getSessionSftp, getSessionSftp,
type SSHSession, type SSHSession,
} from "./file-manager-session.js"; } from "./session.js";
import { detectBinary } from "./file-manager-utils.js"; import { detectBinary } from "./utils.js";
type FileContentRoutesDeps = { type FileContentRoutesDeps = {
sshSessions: Record<string, SSHSession>; sshSessions: Record<string, SSHSession>;
@@ -1,12 +1,8 @@
import type { Express } from "express"; import type { Express } from "express";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { fileLogger } from "../utils/logger.js"; import { fileLogger } from "../../utils/logger.js";
import { getMimeType } from "./file-manager-utils.js"; import { getMimeType } from "./utils.js";
import { import { execChannel, getSessionSftp, type SSHSession } from "./session.js";
execChannel,
getSessionSftp,
type SSHSession,
} from "./file-manager-session.js";
type FileDownloadRoutesDeps = { type FileDownloadRoutesDeps = {
sshSessions: Record<string, SSHSession>; sshSessions: Record<string, SSHSession>;
@@ -1,21 +1,24 @@
import express from "express"; import express from "express";
import { createCorsMiddleware } from "../utils/cors-config.js"; import { createCorsMiddleware } from "../../utils/cors-config.js";
import cookieParser from "cookie-parser"; import cookieParser from "cookie-parser";
import axios from "axios"; import axios from "axios";
import { Client as SSHClient } from "ssh2"; import { Client as SSHClient } from "ssh2";
import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js"; import { SSH_ALGORITHMS } from "../../utils/ssh-algorithms.js";
import { createCurrentHostResolutionRepository } from "../database/repositories/factory.js"; import { createCurrentHostResolutionRepository } from "../../database/repositories/factory.js";
import { fileLogger } from "../utils/logger.js"; import { fileLogger } from "../../utils/logger.js";
import { AuthManager } from "../utils/auth-manager.js"; import { AuthManager } from "../../utils/auth-manager.js";
import type { AuthenticatedRequest, ProxyNode } from "../../types/index.js"; import type { AuthenticatedRequest, ProxyNode } from "../../../types/index.js";
import { import {
createSocks5Connection, createSocks5Connection,
type SOCKS5Config, type SOCKS5Config,
} from "../utils/socks5-helper.js"; } from "../../utils/socks5-helper.js";
import type { LogEntry, ConnectionStage } from "../../types/connection-log.js"; import type {
import { SSHHostKeyVerifier } from "./host-key-verifier.js"; LogEntry,
import { resolveHostById } from "./host-resolver.js"; ConnectionStage,
import type { SSHHost } from "../../types/index.js"; } from "../../../types/connection-log.js";
import { SSHHostKeyVerifier } from "../host-key-verifier.js";
import { resolveHostById } from "../host-resolver.js";
import type { SSHHost } from "../../../types/index.js";
import { import {
startHostTransfer, startHostTransfer,
getTransferStatus, getTransferStatus,
@@ -26,24 +29,24 @@ import {
retryHostTransfer, retryHostTransfer,
previewArchiveTransferMethod, previewArchiveTransferMethod,
type HostTransferDeps, type HostTransferDeps,
} from "./host-transfer.js"; } from "./transfer-engine.js";
import { registerFileContentRoutes } from "./file-manager-content-routes.js"; import { registerFileContentRoutes } from "./content-routes.js";
import { createConnectionLog } from "./connection-log.js"; import { createConnectionLog } from "../connection-log.js";
import { createJumpHostChain } from "./jump-host-chain.js"; import { createJumpHostChain } from "../jump-host-chain.js";
import { preparePrivateKeyForSSH2 } from "../utils/ssh-key-utils.js"; import { preparePrivateKeyForSSH2 } from "../../utils/ssh-key-utils.js";
import { import {
ChannelOpenSerializer, ChannelOpenSerializer,
execChannel, execChannel,
getSessionSftp, getSessionSftp,
type PendingTOTPSession, type PendingTOTPSession,
type SSHSession, type SSHSession,
} from "./file-manager-session.js"; } from "./session.js";
import { registerFileListingRoutes } from "./file-manager-list-routes.js"; import { registerFileListingRoutes } from "./list-routes.js";
import { registerFileOperationRoutes } from "./file-manager-operation-routes.js"; import { registerFileOperationRoutes } from "./operation-routes.js";
import { resolveSshConnectConfigHost } from "./ssh-dns.js"; import { resolveSshConnectConfigHost } from "../ssh-dns.js";
import { registerFileDownloadRoutes } from "./file-manager-download-routes.js"; import { registerFileDownloadRoutes } from "./download-routes.js";
import { registerFileActionRoutes } from "./file-manager-action-routes.js"; import { registerFileActionRoutes } from "./action-routes.js";
import { applyAgentAuth } from "./terminal-auth-helpers.js"; import { applyAgentAuth } from "../terminal-auth-helpers.js";
const app = express(); const app = express();
@@ -209,14 +212,14 @@ async function buildDedicatedTransferConnectConfig(
} }
config.password = host.password; config.password = host.password;
} else if (authType === "opkssh") { } else if (authType === "opkssh") {
const { getOPKSSHToken } = await import("./opkssh-auth.js"); const { getOPKSSHToken } = await import("../opkssh-auth.js");
const token = await getOPKSSHToken(userId, host.id); const token = await getOPKSSHToken(userId, host.id);
if (!token) { if (!token) {
throw new Error( throw new Error(
"OPKSSH authentication required. Open a Terminal connection to this host first.", "OPKSSH authentication required. Open a Terminal connection to this host first.",
); );
} }
const { setupOPKSSHCertAuth } = await import("./opkssh-cert-auth.js"); const { setupOPKSSHCertAuth } = await import("../opkssh-cert-auth.js");
await setupOPKSSHCertAuth( await setupOPKSSHCertAuth(
config as import("ssh2").ConnectConfig, config as import("ssh2").ConnectConfig,
client, client,
@@ -747,7 +750,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
let resolvedSocks5ProxyChain = socks5ProxyChain; let resolvedSocks5ProxyChain = socks5ProxyChain;
if (hostId && userId && !password && !sshKey) { if (hostId && userId && !password && !sshKey) {
try { try {
const { resolveHostById } = await import("./host-resolver.js"); const { resolveHostById } = await import("../host-resolver.js");
const resolvedHost = await resolveHostById(hostId, userId); const resolvedHost = await resolveHostById(hostId, userId);
if (resolvedHost) { if (resolvedHost) {
resolvedIp = resolvedHost.ip; resolvedIp = resolvedHost.ip;
@@ -806,7 +809,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
} else if (credentialId && hostId && userId) { } else if (credentialId && hostId && userId) {
// Legacy: credential resolution from credentialId // Legacy: credential resolution from credentialId
try { try {
const { resolveHostById } = await import("./host-resolver.js"); const { resolveHostById } = await import("../host-resolver.js");
const resolvedHost = await resolveHostById(hostId, userId); const resolvedHost = await resolveHostById(hostId, userId);
if (resolvedHost) { if (resolvedHost) {
resolvedIp = resolvedHost.ip; resolvedIp = resolvedHost.ip;
@@ -998,7 +1001,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
); );
} else if (resolvedCredentials.authType === "opkssh") { } else if (resolvedCredentials.authType === "opkssh") {
try { try {
const { getOPKSSHToken } = await import("./opkssh-auth.js"); const { getOPKSSHToken } = await import("../opkssh-auth.js");
const token = await getOPKSSHToken(userId, hostId); const token = await getOPKSSHToken(userId, hostId);
if (!token) { if (!token) {
@@ -1017,7 +1020,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
}); });
} }
const { setupOPKSSHCertAuth } = await import("./opkssh-cert-auth.js"); const { setupOPKSSHCertAuth } = await import("../opkssh-cert-auth.js");
await setupOPKSSHCertAuth( await setupOPKSSHCertAuth(
config as import("ssh2").ConnectConfig, config as import("ssh2").ConnectConfig,
client, client,
@@ -1,17 +1,13 @@
import type { Express } from "express"; import type { Express } from "express";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { fileLogger } from "../utils/logger.js"; import { fileLogger } from "../../utils/logger.js";
import { import { execChannel, getSessionSftp, type SSHSession } from "./session.js";
execChannel,
getSessionSftp,
type SSHSession,
} from "./file-manager-session.js";
import { import {
formatMtime, formatMtime,
isExecutableFile, isExecutableFile,
modeToPermissions, modeToPermissions,
parseLsDateToTimestamp, parseLsDateToTimestamp,
} from "./file-manager-utils.js"; } from "./utils.js";
type FileListingRoutesDeps = { type FileListingRoutesDeps = {
sshSessions: Record<string, SSHSession>; sshSessions: Record<string, SSHSession>;
@@ -1,4 +1,4 @@
import { isWindowsSftpPath, sftpPathToLocalPath } from "./transfer-paths.js"; import { isWindowsSftpPath, sftpPathToLocalPath } from "../transfer-paths.js";
export interface DeleteCommand { export interface DeleteCommand {
command: string; command: string;
@@ -1,12 +1,8 @@
import type { Express } from "express"; import type { Express } from "express";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { fileLogger } from "../utils/logger.js"; import { fileLogger } from "../../utils/logger.js";
import { import { execChannel, execWithSudo, type SSHSession } from "./session.js";
execChannel, import { buildDeleteCommand } from "./operation-commands.js";
execWithSudo,
type SSHSession,
} from "./file-manager-session.js";
import { buildDeleteCommand } from "./file-manager-operation-commands.js";
type FileOperationRoutesDeps = { type FileOperationRoutesDeps = {
sshSessions: Record<string, SSHSession>; sshSessions: Record<string, SSHSession>;
@@ -2,7 +2,7 @@ import { randomUUID } from "crypto";
import { networkInterfaces } from "os"; import { networkInterfaces } from "os";
import { performance } from "node:perf_hooks"; import { performance } from "node:perf_hooks";
import type { ClientChannel } from "ssh2"; import type { ClientChannel } from "ssh2";
import { fileLogger } from "../utils/logger.js"; import { fileLogger } from "../../utils/logger.js";
import { import {
basename, basename,
buildPathFromSegments, buildPathFromSegments,
@@ -15,7 +15,7 @@ import {
sftpPathToLocalPath, sftpPathToLocalPath,
splitPathSegments, splitPathSegments,
type TransferPlatform, type TransferPlatform,
} from "./transfer-paths.js"; } from "../transfer-paths.js";
import { import {
buildTransferScanSummary, buildTransferScanSummary,
getArchiveTransferReasonKey, getArchiveTransferReasonKey,
@@ -1,4 +1,4 @@
import type { TransferPlatform } from "./transfer-paths.js"; import type { TransferPlatform } from "../transfer-paths.js";
export type TransferMethodPreference = "auto" | "tar" | "item_sftp"; export type TransferMethodPreference = "auto" | "tar" | "item_sftp";
@@ -1,10 +1,10 @@
import { createCurrentAlertRepository } from "../database/repositories/factory.js"; import { createCurrentAlertRepository } from "../../database/repositories/factory.js";
import { statsLogger } from "../utils/logger.js"; import { statsLogger } from "../../utils/logger.js";
import { import {
sendNotification, sendNotification,
type AlertPayload, type AlertPayload,
type NotificationChannel, type NotificationChannel,
} from "../utils/notification-sender.js"; } from "../../utils/notification-sender.js";
type AlertTriggerType = type AlertTriggerType =
| "host_offline" | "host_offline"
@@ -1,7 +1,7 @@
import type { Express, RequestHandler } from "express"; import type { Express, RequestHandler } from "express";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { createCurrentHostMetricsHistoryRepository } from "../database/repositories/factory.js"; import { createCurrentHostMetricsHistoryRepository } from "../../database/repositories/factory.js";
import { statsLogger } from "../utils/logger.js"; import { statsLogger } from "../../utils/logger.js";
type HistoryRoutesDeps = { type HistoryRoutesDeps = {
validateHostId: RequestHandler; validateHostId: RequestHandler;
@@ -1,24 +1,27 @@
import express from "express"; import express from "express";
import net from "net"; import net from "net";
import { createCorsMiddleware } from "../utils/cors-config.js"; import { createCorsMiddleware } from "../../utils/cors-config.js";
import cookieParser from "cookie-parser"; import cookieParser from "cookie-parser";
import { Client, type ConnectConfig } from "ssh2"; import { Client, type ConnectConfig } from "ssh2";
import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js"; import { SSH_ALGORITHMS } from "../../utils/ssh-algorithms.js";
import { import {
pickResolvedPassword, pickResolvedPassword,
pickResolvedUsername, pickResolvedUsername,
} from "./credential-username.js"; } from "../credential-username.js";
import { import {
getCurrentSettingValue, getCurrentSettingValue,
createCurrentHostMetricsHistoryRepository, createCurrentHostMetricsHistoryRepository,
createCurrentHostResolutionRepository, createCurrentHostResolutionRepository,
} from "../database/repositories/factory.js"; } from "../../database/repositories/factory.js";
import { statsLogger } from "../utils/logger.js"; import { statsLogger } from "../../utils/logger.js";
import { DataCrypto } from "../utils/data-crypto.js"; import { DataCrypto } from "../../utils/data-crypto.js";
import { AuthManager } from "../utils/auth-manager.js"; import { AuthManager } from "../../utils/auth-manager.js";
import { PermissionManager } from "../utils/permission-manager.js"; import { PermissionManager } from "../../utils/permission-manager.js";
import type { AuthenticatedRequest, ProxyNode } from "../../types/index.js"; import type { AuthenticatedRequest, ProxyNode } from "../../../types/index.js";
import type { LogEntry, ConnectionStage } from "../../types/connection-log.js"; import type {
LogEntry,
ConnectionStage,
} from "../../../types/connection-log.js";
import { collectCpuMetrics } from "./widgets/cpu-collector.js"; import { collectCpuMetrics } from "./widgets/cpu-collector.js";
import { collectMemoryMetrics } from "./widgets/memory-collector.js"; import { collectMemoryMetrics } from "./widgets/memory-collector.js";
import { collectDiskMetrics } from "./widgets/disk-collector.js"; import { collectDiskMetrics } from "./widgets/disk-collector.js";
@@ -32,26 +35,26 @@ import { collectFirewallMetrics } from "./widgets/firewall-collector.js";
import { import {
createSocks5Connection, createSocks5Connection,
type SOCKS5Config, type SOCKS5Config,
} from "../utils/socks5-helper.js"; } from "../../utils/socks5-helper.js";
import { preparePrivateKeyForSSH2 } from "../utils/ssh-key-utils.js"; import { preparePrivateKeyForSSH2 } from "../../utils/ssh-key-utils.js";
import { SSHHostKeyVerifier } from "./host-key-verifier.js"; import { SSHHostKeyVerifier } from "../host-key-verifier.js";
import { connectionPool, withConnection } from "./ssh-connection-pool.js"; import { connectionPool, withConnection } from "../ssh-connection-pool.js";
import { registerHostMetricsSettingsRoutes } from "./host-metrics-settings-routes.js"; import { registerHostMetricsSettingsRoutes } from "./settings-routes.js";
import { registerHostMetricsViewerRoutes } from "./host-metrics-viewer-routes.js"; import { registerHostMetricsViewerRoutes } from "./viewer-routes.js";
import { registerHostMetricsPreferencesRoutes } from "./host-metrics-preferences-routes.js"; import { registerHostMetricsPreferencesRoutes } from "./preferences-routes.js";
import { registerHostMetricsHistoryRoutes } from "./host-metrics-history-routes.js"; import { registerHostMetricsHistoryRoutes } from "./history-routes.js";
import { AlertEngine } from "./alert-engine.js"; import { AlertEngine } from "./alert-engine.js";
import { registerManagerRoutes } from "./managers/index.js"; import { registerManagerRoutes } from "./managers/index.js";
import { resolveSshConnectConfigHost } from "./ssh-dns.js"; import { resolveSshConnectConfigHost } from "../ssh-dns.js";
import { AccessDeniedError } from "./managers/route-helpers.js"; import { AccessDeniedError } from "./managers/route-helpers.js";
import type { ManagerHost } from "./managers/types.js"; import type { ManagerHost } from "./managers/types.js";
import { createJumpHostChain } from "./jump-host-chain.js"; import { createJumpHostChain } from "../jump-host-chain.js";
import { import {
isTcpPingEnabled, isTcpPingEnabled,
supportsMetrics, supportsMetrics,
tcpPingThroughJumpHost, tcpPingThroughJumpHost,
} from "./host-metrics-helpers.js"; } from "./helpers.js";
import { createConnectionLog } from "./connection-log.js"; import { createConnectionLog } from "../connection-log.js";
import { import {
cleanupMetricsSession, cleanupMetricsSession,
getSessionKey, getSessionKey,
@@ -59,7 +62,7 @@ import {
pendingTOTPSessions, pendingTOTPSessions,
scheduleMetricsSessionCleanup, scheduleMetricsSessionCleanup,
type MetricsViewer, type MetricsViewer,
} from "./host-metrics-sessions.js"; } from "./sessions.js";
import { import {
authFailureTracker, authFailureTracker,
hostPollCache, hostPollCache,
@@ -68,7 +71,7 @@ import {
pollingBackoff, pollingBackoff,
requestQueue, requestQueue,
statusPollLimiter, statusPollLimiter,
} from "./host-metrics-state.js"; } from "./state.js";
const authManager = AuthManager.getInstance(); const authManager = AuthManager.getInstance();
const permissionManager = PermissionManager.getInstance(); const permissionManager = PermissionManager.getInstance();
@@ -989,7 +992,7 @@ async function resolveHostCredentials(
if (isSharedHost) { if (isSharedHost) {
const { SharedCredentialManager } = const { SharedCredentialManager } =
await import("../utils/shared-credential-manager.js"); await import("../../utils/shared-credential-manager.js");
const sharedCredManager = SharedCredentialManager.getInstance(); const sharedCredManager = SharedCredentialManager.getInstance();
const sharedCred = await sharedCredManager.getSharedCredentialForUser( const sharedCred = await sharedCredManager.getSharedCredentialForUser(
host.id as number, host.id as number,
@@ -1255,18 +1258,18 @@ function createSshFactory(host: SSHHostWithCredentials): () => Promise<Client> {
// Set up OPKSSH cert auth if needed (requires client instance) // Set up OPKSSH cert auth if needed (requires client instance)
if (host.authType === "opkssh" && host.userId) { if (host.authType === "opkssh" && host.userId) {
const { getOPKSSHToken } = await import("./opkssh-auth.js"); const { getOPKSSHToken } = await import("../opkssh-auth.js");
const token = await getOPKSSHToken(host.userId, host.id); const token = await getOPKSSHToken(host.userId, host.id);
if (!token) { if (!token) {
throw new Error( throw new Error(
"OPKSSH authentication required. Please open a Terminal connection first.", "OPKSSH authentication required. Please open a Terminal connection first.",
); );
} }
const { setupOPKSSHCertAuth } = await import("./opkssh-cert-auth.js"); const { setupOPKSSHCertAuth } = await import("../opkssh-cert-auth.js");
await setupOPKSSHCertAuth(config, client, token, host.username); await setupOPKSSHCertAuth(config, client, token, host.username);
} else if (host.authType === "vault") { } else if (host.authType === "vault") {
const { setupVaultSshSignerAuth } = const { setupVaultSshSignerAuth } =
await import("./vault-ssh-connect.js"); await import("../vault-ssh-connect.js");
await setupVaultSshSignerAuth(config, client, host); await setupVaultSshSignerAuth(config, client, host);
} }
@@ -2123,7 +2126,7 @@ app.post("/metrics/start/:id", validateHostId, async (req, res) => {
const client = new Client(); const client = new Client();
if (host.authType === "opkssh" && host.userId) { if (host.authType === "opkssh" && host.userId) {
const { getOPKSSHToken } = await import("./opkssh-auth.js"); const { getOPKSSHToken } = await import("../opkssh-auth.js");
const token = await getOPKSSHToken(host.userId, host.id); const token = await getOPKSSHToken(host.userId, host.id);
if (!token) { if (!token) {
connectionLogs.push( connectionLogs.push(
@@ -2139,11 +2142,11 @@ app.post("/metrics/start/:id", validateHostId, async (req, res) => {
connectionLogs, connectionLogs,
}); });
} }
const { setupOPKSSHCertAuth } = await import("./opkssh-cert-auth.js"); const { setupOPKSSHCertAuth } = await import("../opkssh-cert-auth.js");
await setupOPKSSHCertAuth(config, client, token, host.username); await setupOPKSSHCertAuth(config, client, token, host.username);
} else if (host.authType === "vault") { } else if (host.authType === "vault") {
const { setupVaultSshSignerAuth } = const { setupVaultSshSignerAuth } =
await import("./vault-ssh-connect.js"); await import("../vault-ssh-connect.js");
await setupVaultSshSignerAuth(config, client, host); await setupVaultSshSignerAuth(config, client, host);
} }
@@ -2792,7 +2795,8 @@ app.post("/internal/login-alert", async (req, res) => {
) { ) {
return res.status(403).json({ error: "Forbidden" }); return res.status(403).json({ error: "Forbidden" });
} }
const systemCrypto = (await import("../utils/system-crypto.js")).SystemCrypto; const systemCrypto = (await import("../../utils/system-crypto.js"))
.SystemCrypto;
const expectedToken = await systemCrypto.getInstance().getInternalAuthToken(); const expectedToken = await systemCrypto.getInstance().getInternalAuthToken();
const token = req.headers["x-internal-auth"]; const token = req.headers["x-internal-auth"];
if (!token || token !== expectedToken) { if (!token || token !== expectedToken) {
@@ -1,8 +1,8 @@
import type { Express } from "express"; import type { Express } from "express";
import type { Client } from "ssh2"; import type { Client } from "ssh2";
import type { AuthenticatedRequest } from "../../../types/index.js"; import type { AuthenticatedRequest } from "../../../../types/index.js";
import { execCommand } from "../widgets/common-utils.js"; import { execCommand } from "../widgets/common-utils.js";
import { createCurrentHostHealthRepository } from "../../database/repositories/factory.js"; import { createCurrentHostHealthRepository } from "../../../database/repositories/factory.js";
import { managerHandler, ManagerInputError } from "./route-helpers.js"; import { managerHandler, ManagerInputError } from "./route-helpers.js";
import { shellSingleQuote } from "./exec-elevated.js"; import { shellSingleQuote } from "./exec-elevated.js";
import { isValidPort } from "./validation.js"; import { isValidPort } from "./validation.js";
@@ -1,7 +1,7 @@
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import type { Client } from "ssh2"; import type { Client } from "ssh2";
import type { AuthenticatedRequest } from "../../../types/index.js"; import type { AuthenticatedRequest } from "../../../../types/index.js";
import { statsLogger } from "../../utils/logger.js"; import { statsLogger } from "../../../utils/logger.js";
import { ElevationError } from "./exec-elevated.js"; import { ElevationError } from "./exec-elevated.js";
import type { ManagerHost, RunOnHost } from "./types.js"; import type { ManagerHost, RunOnHost } from "./types.js";
@@ -1,12 +1,12 @@
import type { Express, RequestHandler } from "express"; import type { Express, RequestHandler } from "express";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { createCurrentHostMetricsPreferenceRepository } from "../database/repositories/factory.js"; import { createCurrentHostMetricsPreferenceRepository } from "../../database/repositories/factory.js";
import { statsLogger } from "../utils/logger.js"; import { statsLogger } from "../../utils/logger.js";
import { import {
deriveEnabledWidgets, deriveEnabledWidgets,
defaultLayoutFromWidgets, defaultLayoutFromWidgets,
type HostMetricsLayout, type HostMetricsLayout,
} from "../../types/host-metrics.js"; } from "../../../types/host-metrics.js";
interface PrefStatsConfig { interface PrefStatsConfig {
enabledWidgets?: string[]; enabledWidgets?: string[];
@@ -1,5 +1,5 @@
import type { Client, ConnectConfig } from "ssh2"; import type { Client, ConnectConfig } from "ssh2";
import { statsLogger } from "../utils/logger.js"; import { statsLogger } from "../../utils/logger.js";
export interface MetricsSession { export interface MetricsSession {
client: Client; client: Client;
@@ -1,6 +1,6 @@
import type { Express, RequestHandler } from "express"; import type { Express, RequestHandler } from "express";
import { statsLogger } from "../utils/logger.js"; import { statsLogger } from "../../utils/logger.js";
import { createCurrentSettingsRepository } from "../database/repositories/factory.js"; import { createCurrentSettingsRepository } from "../../database/repositories/factory.js";
type HostMetricsSettingsConfig = { type HostMetricsSettingsConfig = {
statusCheckInterval: number; statusCheckInterval: number;
@@ -1,7 +1,7 @@
import type { Express } from "express"; import type { Express } from "express";
import type { AuthenticatedRequest } from "../../types/index.js"; import type { AuthenticatedRequest } from "../../../types/index.js";
import { statsLogger } from "../utils/logger.js"; import { statsLogger } from "../../utils/logger.js";
import { DataCrypto } from "../utils/data-crypto.js"; import { DataCrypto } from "../../utils/data-crypto.js";
type ViewerStatsConfig = { type ViewerStatsConfig = {
metricsEnabled: boolean; metricsEnabled: boolean;
@@ -4,7 +4,7 @@ import type {
FirewallMetrics, FirewallMetrics,
FirewallChain, FirewallChain,
FirewallRule, FirewallRule,
} from "../../../types/stats-widgets.js"; } from "../../../../types/stats-widgets.js";
function parseIptablesRule(line: string): FirewallRule | null { function parseIptablesRule(line: string): FirewallRule | null {
if (!line.startsWith("-A ")) return null; if (!line.startsWith("-A ")) return null;
@@ -3,7 +3,7 @@ import { execCommand } from "./common-utils.js";
import type { import type {
PortsMetrics, PortsMetrics,
ListeningPort, ListeningPort,
} from "../../../types/stats-widgets.js"; } from "../../../../types/stats-widgets.js";
function parseSsOutput(output: string): ListeningPort[] { function parseSsOutput(output: string): ListeningPort[] {
const ports: ListeningPort[] = []; const ports: ListeningPort[] = [];
@@ -5,36 +5,36 @@ import ssh2Pkg, {
type PseudoTtyOptions, type PseudoTtyOptions,
} from "ssh2"; } from "ssh2";
const { Client, utils: ssh2Utils } = ssh2Pkg; const { Client, utils: ssh2Utils } = ssh2Pkg;
import { buildSSHAlgorithms } from "../utils/ssh-algorithms.js"; import { buildSSHAlgorithms } from "../../utils/ssh-algorithms.js";
import axios from "axios"; import axios from "axios";
import { createCurrentHostResolutionRepository } from "../database/repositories/factory.js"; import { createCurrentHostResolutionRepository } from "../../database/repositories/factory.js";
import { sshLogger, authLogger } from "../utils/logger.js"; import { sshLogger, authLogger } from "../../utils/logger.js";
import { logAudit } from "../utils/audit-logger.js"; import { logAudit } from "../../utils/audit-logger.js";
import { AuthManager } from "../utils/auth-manager.js"; import { AuthManager } from "../../utils/auth-manager.js";
import { DataCrypto } from "../utils/data-crypto.js"; import { DataCrypto } from "../../utils/data-crypto.js";
import { import {
createSocks5Connection, createSocks5Connection,
type SOCKS5Config, type SOCKS5Config,
} from "../utils/socks5-helper.js"; } from "../../utils/socks5-helper.js";
import { SSHAuthManager } from "./auth-manager.js"; import { SSHAuthManager } from "../auth-manager.js";
import type { ProxyNode } from "../../types/index.js"; import type { ProxyNode } from "../../../types/index.js";
import { SSHHostKeyVerifier } from "./host-key-verifier.js"; import { SSHHostKeyVerifier } from "../host-key-verifier.js";
import { createJumpHostChain } from "./jump-host-chain.js"; import { createJumpHostChain } from "../jump-host-chain.js";
import { sessionManager } from "./terminal-session-manager.js"; import { sessionManager } from "./session-manager.js";
import { import {
detectTmux, detectTmux,
attachOrCreateTmuxSession, attachOrCreateTmuxSession,
waitForTmuxSession, waitForTmuxSession,
} from "./tmux-helper.js"; } from "../tmux/helper.js";
import { import {
MemoryAgent, MemoryAgent,
performPortKnocking, performPortKnocking,
resolveAgentSocket, resolveAgentSocket,
} from "./terminal-auth-helpers.js"; } from "../terminal-auth-helpers.js";
import { isWindowsSftpPath, sftpPathToLocalPath } from "./transfer-paths.js"; import { isWindowsSftpPath, sftpPathToLocalPath } from "../transfer-paths.js";
import { preparePrivateKeyForSSH2 } from "../utils/ssh-key-utils.js"; import { preparePrivateKeyForSSH2 } from "../../utils/ssh-key-utils.js";
import { triggerLoginAlert } from "../utils/alert-trigger.js"; import { triggerLoginAlert } from "../../utils/alert-trigger.js";
import { isRetriableDnsError, resolveHostForSshConnect } from "./ssh-dns.js"; import { isRetriableDnsError, resolveHostForSshConnect } from "../ssh-dns.js";
interface ConnectToHostData { interface ConnectToHostData {
cols: number; cols: number;
@@ -781,9 +781,9 @@ wss.on("connection", async (ws: WebSocket, req) => {
case "opkssh_start_auth": { case "opkssh_start_auth": {
const opksshData = data as { hostId: number }; const opksshData = data as { hostId: number };
try { try {
const { startOPKSSHAuth } = await import("./opkssh-auth.js"); const { startOPKSSHAuth } = await import("../opkssh-auth.js");
const { getRequestOrigin } = const { getRequestOrigin } =
await import("../utils/request-origin.js"); await import("../../utils/request-origin.js");
const host = const host =
await createCurrentHostResolutionRepository().findHostById( await createCurrentHostResolutionRepository().findHostById(
opksshData.hostId, opksshData.hostId,
@@ -836,7 +836,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
case "opkssh_cancel": { case "opkssh_cancel": {
const cancelData = data as { requestId: string }; const cancelData = data as { requestId: string };
try { try {
const { cancelAuthSession } = await import("./opkssh-auth.js"); const { cancelAuthSession } = await import("../opkssh-auth.js");
cancelAuthSession(cancelData.requestId); cancelAuthSession(cancelData.requestId);
resetConnectionState(); resetConnectionState();
} catch (error) { } catch (error) {
@@ -898,9 +898,9 @@ wss.on("connection", async (ws: WebSocket, req) => {
const vaultData = data as { hostId: number }; const vaultData = data as { hostId: number };
try { try {
const { loadVaultProfileForHost, startVaultAuth } = const { loadVaultProfileForHost, startVaultAuth } =
await import("./vault-oidc-auth.js"); await import("../vault-oidc-auth.js");
const { getRequestOrigin } = const { getRequestOrigin } =
await import("../utils/request-origin.js"); await import("../../utils/request-origin.js");
const profile = await loadVaultProfileForHost( const profile = await loadVaultProfileForHost(
vaultData.hostId, vaultData.hostId,
userId, userId,
@@ -947,7 +947,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
const cancelData = data as { hostId: number }; const cancelData = data as { hostId: number };
try { try {
const { cancelVaultAuthByHost } = const { cancelVaultAuthByHost } =
await import("./vault-oidc-auth.js"); await import("../vault-oidc-auth.js");
cancelVaultAuthByHost(userId, cancelData.hostId); cancelVaultAuthByHost(userId, cancelData.hostId);
resetConnectionState(); resetConnectionState();
} catch (error) { } catch (error) {
@@ -1155,7 +1155,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
if (id && userId) { if (id && userId) {
try { try {
const { resolveHostById } = await import("./host-resolver.js"); const { resolveHostById } = await import("../host-resolver.js");
resolvedHostData = (await resolveHostById( resolvedHostData = (await resolveHostById(
id, id,
userId, userId,
@@ -1892,7 +1892,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
(async () => { (async () => {
try { try {
const { invalidateOPKSSHToken } = await import("./opkssh-auth.js"); const { invalidateOPKSSHToken } = await import("../opkssh-auth.js");
await invalidateOPKSSHToken(userId, id, "SSH auth failed"); await invalidateOPKSSHToken(userId, id, "SSH auth failed");
} catch (invalidateError) { } catch (invalidateError) {
sshLogger.error("Failed to invalidate OPKSSH token", { sshLogger.error("Failed to invalidate OPKSSH token", {
@@ -1945,7 +1945,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
)?.id; )?.id;
if (profileId) { if (profileId) {
const { deleteVaultCert } = const { deleteVaultCert } =
await import("./vault-signer-auth.js"); await import("../vault-signer-auth.js");
await deleteVaultCert(userId, profileId); await deleteVaultCert(userId, profileId);
} }
} catch (invalidateError) { } catch (invalidateError) {
@@ -2377,7 +2377,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
resolvedCredentials.certPublicKey.trim() resolvedCredentials.certPublicKey.trim()
) { ) {
try { try {
const { setupCACertAuth } = await import("./opkssh-cert-auth.js"); const { setupCACertAuth } = await import("../opkssh-cert-auth.js");
await setupCACertAuth( await setupCACertAuth(
connectConfig, connectConfig,
sshConn, sshConn,
@@ -2440,7 +2440,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
} else if (resolvedCredentials.authType === "opkssh") { } else if (resolvedCredentials.authType === "opkssh") {
sendLog("auth", "info", "Using OPKSSH certificate authentication"); sendLog("auth", "info", "Using OPKSSH certificate authentication");
try { try {
const { getOPKSSHToken } = await import("./opkssh-auth.js"); const { getOPKSSHToken } = await import("../opkssh-auth.js");
const token = await getOPKSSHToken(userId, id); const token = await getOPKSSHToken(userId, id);
if (!token) { if (!token) {
@@ -2460,7 +2460,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
sendLog("auth", "info", "Using cached OPKSSH certificate"); sendLog("auth", "info", "Using cached OPKSSH certificate");
const { setupOPKSSHCertAuth } = await import("./opkssh-cert-auth.js"); const { setupOPKSSHCertAuth } = await import("../opkssh-cert-auth.js");
await setupOPKSSHCertAuth(connectConfig, sshConn, token, username); await setupOPKSSHCertAuth(connectConfig, sshConn, token, username);
} catch (opksshError) { } catch (opksshError) {
sshLogger.error("OPKSSH authentication error", opksshError, { sshLogger.error("OPKSSH authentication error", opksshError, {
@@ -2490,7 +2490,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
throw new Error("Host has no Vault signer profile configured"); throw new Error("Host has no Vault signer profile configured");
} }
const { getVaultCert } = await import("./vault-signer-auth.js"); const { getVaultCert } = await import("../vault-signer-auth.js");
const cert = await getVaultCert(userId, vaultProfile.id); const cert = await getVaultCert(userId, vaultProfile.id);
if (!cert) { if (!cert) {
@@ -2510,7 +2510,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
sendLog("auth", "info", "Using cached Vault-signed certificate"); sendLog("auth", "info", "Using cached Vault-signed certificate");
const { setupOPKSSHCertAuth } = await import("./opkssh-cert-auth.js"); const { setupOPKSSHCertAuth } = await import("../opkssh-cert-auth.js");
await setupOPKSSHCertAuth( await setupOPKSSHCertAuth(
connectConfig, connectConfig,
sshConn, sshConn,
@@ -2,11 +2,11 @@ import { type Client, type ClientChannel } from "ssh2";
import { WebSocket } from "ws"; import { WebSocket } from "ws";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { sshLogger } from "../utils/logger.js"; import { sshLogger } from "../../utils/logger.js";
import { import {
getCurrentSettingValue, getCurrentSettingValue,
createCurrentSessionRecordingRepository, createCurrentSessionRecordingRepository,
} from "../database/repositories/factory.js"; } from "../../database/repositories/factory.js";
const MAX_BUFFER_BYTES = 512 * 1024; const MAX_BUFFER_BYTES = 512 * 1024;
const DATA_DIR = process.env.DATA_DIR ?? "./db/data"; const DATA_DIR = process.env.DATA_DIR ?? "./db/data";
@@ -1,5 +1,5 @@
import type { Client, ClientChannel } from "ssh2"; import type { Client, ClientChannel } from "ssh2";
import { sshLogger } from "../utils/logger.js"; import { sshLogger } from "../../utils/logger.js";
export interface TmuxSessionInfo { export interface TmuxSessionInfo {
name: string; name: string;
@@ -1,28 +1,28 @@
import express from "express"; import express from "express";
import cookieParser from "cookie-parser"; import cookieParser from "cookie-parser";
import { Client, type ConnectConfig } from "ssh2"; import { Client, type ConnectConfig } from "ssh2";
import { createCorsMiddleware } from "../utils/cors-config.js"; import { createCorsMiddleware } from "../../utils/cors-config.js";
import { AuthManager } from "../utils/auth-manager.js"; import { AuthManager } from "../../utils/auth-manager.js";
import { DataCrypto } from "../utils/data-crypto.js"; import { DataCrypto } from "../../utils/data-crypto.js";
import { import {
createCurrentTmuxSessionTagRepository, createCurrentTmuxSessionTagRepository,
createCurrentUserRepository, createCurrentUserRepository,
} from "../database/repositories/factory.js"; } from "../../database/repositories/factory.js";
import { logAudit, getRequestMeta } from "../utils/audit-logger.js"; import { logAudit, getRequestMeta } from "../../utils/audit-logger.js";
import { sshLogger } from "../utils/logger.js"; import { sshLogger } from "../../utils/logger.js";
import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js"; import { SSH_ALGORITHMS } from "../../utils/ssh-algorithms.js";
import { preparePrivateKeyForSSH2 } from "../utils/ssh-key-utils.js"; import { preparePrivateKeyForSSH2 } from "../../utils/ssh-key-utils.js";
import { SSHHostKeyVerifier } from "./host-key-verifier.js"; import { SSHHostKeyVerifier } from "../host-key-verifier.js";
import { resolveHostById, checkHostAccess } from "./host-resolver.js"; import { resolveHostById, checkHostAccess } from "../host-resolver.js";
import { createJumpHostChain } from "./jump-host-chain.js"; import { createJumpHostChain } from "../jump-host-chain.js";
import { applyAgentAuth } from "./terminal-auth-helpers.js"; import { applyAgentAuth } from "../terminal-auth-helpers.js";
import { import {
createSocks5Connection, createSocks5Connection,
type SOCKS5Config, type SOCKS5Config,
} from "../utils/socks5-helper.js"; } from "../../utils/socks5-helper.js";
import { withConnection } from "./ssh-connection-pool.js"; import { withConnection } from "../ssh-connection-pool.js";
import { resolveSshConnectConfigHost } from "./ssh-dns.js"; import { resolveSshConnectConfigHost } from "../ssh-dns.js";
import { execCommand, tmuxCommand } from "./tmux-helper.js"; import { execCommand, tmuxCommand } from "./helper.js";
import { import {
SEP, SEP,
parseSessions, parseSessions,
@@ -37,8 +37,8 @@ import {
type TmuxSessionSummary, type TmuxSessionSummary,
type TmuxWindow, type TmuxWindow,
type PaneMetrics, type PaneMetrics,
} from "./tmux-monitor-helpers.js"; } from "./monitor-helpers.js";
import type { SSHHost, AuthenticatedRequest } from "../../types/index.js"; import type { SSHHost, AuthenticatedRequest } from "../../../types/index.js";
const PANE_ID_RE = /^%\d+$/; const PANE_ID_RE = /^%\d+$/;
// tmux session names cannot contain ":" or "."; keep to a conservative // tmux session names cannot contain ":" or "."; keep to a conservative
@@ -124,7 +124,7 @@ export function connectToHost(host: SSHHost): () => Promise<Client> {
if (host.authType === "vault") { if (host.authType === "vault") {
const { setupVaultSshSignerAuth } = const { setupVaultSshSignerAuth } =
await import("./vault-ssh-connect.js"); await import("../vault-ssh-connect.js");
await setupVaultSshSignerAuth(config, client, host); await setupVaultSshSignerAuth(config, client, host);
} }
@@ -1,9 +1,9 @@
import { Client, type ClientChannel } from "ssh2"; import { Client, type ClientChannel } from "ssh2";
import type { WebSocket } from "ws"; import type { WebSocket } from "ws";
import type { TunnelConfig } from "../../types/index.js"; import type { TunnelConfig } from "../../../types/index.js";
import { createSocks5Connection } from "../utils/socks5-helper.js"; import { createSocks5Connection } from "../../utils/socks5-helper.js";
import { tunnelLogger } from "../utils/logger.js"; import { tunnelLogger } from "../../utils/logger.js";
import { PermissionManager } from "../utils/permission-manager.js"; import { PermissionManager } from "../../utils/permission-manager.js";
import { import {
applyAuthOptions, applyAuthOptions,
bindForwardIn, bindForwardIn,
@@ -11,12 +11,9 @@ import {
forwardOut, forwardOut,
getManagedTunnelAlgorithms, getManagedTunnelAlgorithms,
unbindForwardIn, unbindForwardIn,
} from "./tunnel-ssh-primitives.js"; } from "./ssh-primitives.js";
import { import { sendC2SMessage, writeC2SRemoteChunk } from "./c2s-relay-utils.js";
sendC2SMessage, import { getTunnelMode } from "./utils.js";
writeC2SRemoteChunk,
} from "./tunnel-c2s-relay-utils.js";
import { getTunnelMode } from "./tunnel-utils.js";
export type C2SOpenMessage = { export type C2SOpenMessage = {
type: "open" | "test"; type: "open" | "test";
@@ -45,7 +42,7 @@ async function resolveC2STunnelSource(
throw new Error("Access denied to this host"); throw new Error("Access denied to this host");
} }
const { resolveHostById } = await import("./host-resolver.js"); const { resolveHostById } = await import("../host-resolver.js");
const resolvedHost = await resolveHostById(tunnelConfig.sourceHostId, userId); const resolvedHost = await resolveHostById(tunnelConfig.sourceHostId, userId);
if (!resolvedHost) { if (!resolvedHost) {
throw new Error("Endpoint SSH host not found"); throw new Error("Endpoint SSH host not found");
+2 -2
View File
@@ -12,12 +12,12 @@ import {
describeC2SRelayError, describeC2SRelayError,
extractRequestToken, extractRequestToken,
sendC2SError, sendC2SError,
} from "../tunnel-c2s-relay-utils.js"; } from "./c2s-relay-utils.js";
import { import {
handleC2SRelayOpen, handleC2SRelayOpen,
handleC2SRelayTest, handleC2SRelayTest,
type C2SOpenMessage, type C2SOpenMessage,
} from "../tunnel-c2s-relay.js"; } from "./c2s-relay.js";
import { registerTunnelRoutes } from "./routes.js"; import { registerTunnelRoutes } from "./routes.js";
import { initializeAutoStartTunnels } from "./manager.js"; import { initializeAutoStartTunnels } from "./manager.js";
+3 -3
View File
@@ -31,7 +31,7 @@ import {
getManagedTunnelAlgorithms, getManagedTunnelAlgorithms,
pipeTunnelStreams, pipeTunnelStreams,
unbindForwardIn, unbindForwardIn,
} from "../tunnel-ssh-primitives.js"; } from "./ssh-primitives.js";
import { import {
classifyTunnelError, classifyTunnelError,
getTunnelBindHost, getTunnelBindHost,
@@ -39,10 +39,10 @@ import {
getTunnelMode, getTunnelMode,
getTunnelScope, getTunnelScope,
normalizeTunnelName, normalizeTunnelName,
} from "../tunnel-utils.js"; } from "./utils.js";
import { resolveSshConnectConfigHost } from "../ssh-dns.js"; import { resolveSshConnectConfigHost } from "../ssh-dns.js";
import { handleSocks5Connect } from "../tunnel-socks5-relay.js"; import { handleSocks5Connect } from "./socks5-relay.js";
export const activeTunnels = new Map<string, Client>(); export const activeTunnels = new Map<string, Client>();
export const retryCounters = new Map<string, number>(); export const retryCounters = new Map<string, number>();
+1 -1
View File
@@ -12,7 +12,7 @@ import { SystemCrypto } from "../../utils/system-crypto.js";
import { AuthManager } from "../../utils/auth-manager.js"; import { AuthManager } from "../../utils/auth-manager.js";
import { PermissionManager } from "../../utils/permission-manager.js"; import { PermissionManager } from "../../utils/permission-manager.js";
import { getTunnelMode, validateTunnelConfig } from "../tunnel-utils.js"; import { getTunnelMode, validateTunnelConfig } from "./utils.js";
import { import {
tunnelConfigs, tunnelConfigs,
@@ -1,5 +1,5 @@
import type { Duplex } from "stream"; import type { Duplex } from "stream";
import { tunnelLogger } from "../utils/logger.js"; import { tunnelLogger } from "../../utils/logger.js";
function parseSocksAddress(buffer: Buffer): { function parseSocksAddress(buffer: Buffer): {
address: string; address: string;
@@ -1,7 +1,7 @@
import { Client, type ClientChannel } from "ssh2"; import { Client, type ClientChannel } from "ssh2";
import type { Duplex } from "stream"; import type { Duplex } from "stream";
import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js"; import { SSH_ALGORITHMS } from "../../utils/ssh-algorithms.js";
import { tunnelLogger } from "../utils/logger.js"; import { tunnelLogger } from "../../utils/logger.js";
export function getManagedTunnelAlgorithms() { export function getManagedTunnelAlgorithms() {
return { return {
@@ -1,5 +1,5 @@
import type { ErrorType, TunnelConfig } from "../../types/index.js"; import type { ErrorType, TunnelConfig } from "../../../types/index.js";
import { tunnelLogger } from "../utils/logger.js"; import { tunnelLogger } from "../../utils/logger.js";
export function classifyTunnelError(errorMessage: string): ErrorType { export function classifyTunnelError(errorMessage: string): ErrorType {
if (!errorMessage) return "UNKNOWN"; if (!errorMessage) return "UNKNOWN";
+4 -4
View File
@@ -154,13 +154,13 @@ import {
const dbServer = await import("./database/database.js"); const dbServer = await import("./database/database.js");
await (dbServer as unknown as { serverReady: Promise<void> }).serverReady; await (dbServer as unknown as { serverReady: Promise<void> }).serverReady;
await import("./hosts/terminal.js"); await import("./hosts/terminal/index.js");
await import("./hosts/tunnel/index.js"); await import("./hosts/tunnel/index.js");
await import("./hosts/file-manager.js"); await import("./hosts/file-manager/index.js");
await import("./hosts/host-metrics.js"); await import("./hosts/metrics/index.js");
await import("./hosts/docker/index.js"); await import("./hosts/docker/index.js");
await import("./hosts/docker/console.js"); await import("./hosts/docker/console.js");
await import("./hosts/tmux-monitor.js"); // --- tmux-monitor --- await import("./hosts/tmux/index.js"); // --- tmux-monitor ---
await import("./hosts/serial.js"); await import("./hosts/serial.js");
await import("./services/dashboard.js"); await import("./services/dashboard.js");
await import("./services/homepage.js"); await import("./services/homepage.js");
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { buildDeleteCommand } from "../../hosts/file-manager-operation-commands.js"; import { buildDeleteCommand } from "../../../hosts/file-manager/operation-commands.js";
describe("buildDeleteCommand", () => { describe("buildDeleteCommand", () => {
it("builds a PowerShell 5.1 compatible delete command for Windows files", () => { it("builds a PowerShell 5.1 compatible delete command for Windows files", () => {
@@ -6,7 +6,7 @@ import {
getMimeType, getMimeType,
detectBinary, detectBinary,
parseLsDateToTimestamp, parseLsDateToTimestamp,
} from "../../hosts/file-manager-utils.js"; } from "../../../hosts/file-manager/utils.js";
describe("isExecutableFile", () => { describe("isExecutableFile", () => {
it("flags scripts with execute permission", () => { it("flags scripts with execute permission", () => {
@@ -2,8 +2,8 @@ import { describe, it, expect } from "vitest";
import { import {
buildSudoCommand, buildSudoCommand,
shellSingleQuote, shellSingleQuote,
} from "../../../hosts/managers/exec-elevated.js"; } from "../../../hosts/metrics/managers/exec-elevated.js";
import { parsePlatformProbe } from "../../../hosts/managers/platform.js"; import { parsePlatformProbe } from "../../../hosts/metrics/managers/platform.js";
import { import {
isValidSystemdUnit, isValidSystemdUnit,
isValidPid, isValidPid,
@@ -15,47 +15,53 @@ import {
isValidSignal, isValidSignal,
isValidServiceAction, isValidServiceAction,
isAllowedPath, isAllowedPath,
} from "../../../hosts/managers/validation.js"; } from "../../../hosts/metrics/managers/validation.js";
import { import {
parseServiceList, parseServiceList,
buildServiceActionCommand, buildServiceActionCommand,
} from "../../../hosts/managers/services.js"; } from "../../../hosts/metrics/managers/services.js";
import { import {
parseProcessList, parseProcessList,
buildKillCommand, buildKillCommand,
} from "../../../hosts/managers/processes.js"; } from "../../../hosts/metrics/managers/processes.js";
import { import {
parseDfMounts, parseDfMounts,
parseTopMemory, parseTopMemory,
} from "../../../hosts/managers/simple-reads.js"; } from "../../../hosts/metrics/managers/simple-reads.js";
import { import {
parseCrontab, parseCrontab,
serializeCrontab, serializeCrontab,
isValidCronSchedule, isValidCronSchedule,
buildApplyCrontabCommand, buildApplyCrontabCommand,
} from "../../../hosts/managers/cron.js"; } from "../../../hosts/metrics/managers/cron.js";
import { import {
buildPackageActionCommand, buildPackageActionCommand,
parseUpgradable, parseUpgradable,
buildListUpgradableCommand, buildListUpgradableCommand,
} from "../../../hosts/managers/packages.js"; } from "../../../hosts/metrics/managers/packages.js";
import { import {
buildIssueCommand, buildIssueCommand,
buildRenewCommand, buildRenewCommand,
buildRevokeCommand, buildRevokeCommand,
isValidCertName, isValidCertName,
parseCertbotCertificates, parseCertbotCertificates,
} from "../../../hosts/managers/ssl.js"; } from "../../../hosts/metrics/managers/ssl.js";
import { import {
buildIptablesRuleCommand, buildIptablesRuleCommand,
buildNftRuleCommand, buildNftRuleCommand,
} from "../../../hosts/managers/firewall.js"; } from "../../../hosts/metrics/managers/firewall.js";
import { parsePasswd, parseSudoers } from "../../../hosts/managers/users.js"; import {
parsePasswd,
parseSudoers,
} from "../../../hosts/metrics/managers/users.js";
import { import {
buildHealthCheckCommand, buildHealthCheckCommand,
parseHealthResult, parseHealthResult,
} from "../../../hosts/managers/health.js"; } from "../../../hosts/metrics/managers/health.js";
import { buildTailCommand, clampLines } from "../../../hosts/managers/logs.js"; import {
buildTailCommand,
clampLines,
} from "../../../hosts/metrics/managers/logs.js";
describe("exec-elevated", () => { describe("exec-elevated", () => {
it("single-quotes and escapes for the shell", () => { it("single-quotes and escapes for the shell", () => {
@@ -4,8 +4,8 @@ import {
supportsMetrics, supportsMetrics,
isTcpPingEnabled, isTcpPingEnabled,
tcpPingThroughJumpHost, tcpPingThroughJumpHost,
} from "../../hosts/host-metrics-helpers.js"; } from "../../../hosts/metrics/helpers.js";
import { createConnectionLog } from "../../hosts/connection-log.js"; import { createConnectionLog } from "../../../hosts/connection-log.js";
describe("supportsMetrics", () => { describe("supportsMetrics", () => {
it("supports plain ssh hosts", () => { it("supports plain ssh hosts", () => {
@@ -1,14 +1,14 @@
import { describe, it, expect, vi, beforeEach } from "vitest"; import { describe, it, expect, vi, beforeEach } from "vitest";
const execCommand = vi.fn(); const execCommand = vi.fn();
vi.mock("../../../hosts/widgets/common-utils.js", () => ({ vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
execCommand: (...args: unknown[]) => execCommand(...args), execCommand: (...args: unknown[]) => execCommand(...args),
})); }));
import { import {
execElevated, execElevated,
ElevationError, ElevationError,
} from "../../../hosts/managers/exec-elevated.js"; } from "../../../../hosts/metrics/managers/exec-elevated.js";
import type { Client } from "ssh2"; import type { Client } from "ssh2";
const fakeClient = {} as Client; const fakeClient = {} as Client;
@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest";
import { import {
ConcurrentLimiter, ConcurrentLimiter,
HostPollCache, HostPollCache,
} from "../../hosts/host-metrics-state.js"; } from "../../../hosts/metrics/state.js";
describe("ConcurrentLimiter", () => { describe("ConcurrentLimiter", () => {
it("never exceeds max concurrent runners", async () => { it("never exceeds max concurrent runners", async () => {
@@ -1,5 +1,8 @@
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { toFixedNum, kibToGiB } from "../../../hosts/widgets/common-utils.js"; import {
toFixedNum,
kibToGiB,
} from "../../../../hosts/metrics/widgets/common-utils.js";
describe("toFixedNum", () => { describe("toFixedNum", () => {
it("rounds to the requested digit count", () => { it("rounds to the requested digit count", () => {
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { parseCpuLine } from "../../../hosts/widgets/cpu-collector.js"; import { parseCpuLine } from "../../../../hosts/metrics/widgets/cpu-collector.js";
describe("parseCpuLine", () => { describe("parseCpuLine", () => {
it("parses a standard /proc/stat cpu line", () => { it("parses a standard /proc/stat cpu line", () => {
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import { import {
parseSensorsOutput, parseSensorsOutput,
parseSysfsThermalOutput, parseSysfsThermalOutput,
} from "../../../hosts/widgets/temperature-collector.js"; } from "../../../../hosts/metrics/widgets/temperature-collector.js";
describe("temperature collectors", () => { describe("temperature collectors", () => {
it("parses sysfs thermal zone output", () => { it("parses sysfs thermal zone output", () => {
@@ -4,18 +4,18 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
const mockCreate = vi.fn().mockResolvedValue({ id: 1 }); const mockCreate = vi.fn().mockResolvedValue({ id: 1 });
const mockUpdateEnded = vi.fn().mockResolvedValue(undefined); const mockUpdateEnded = vi.fn().mockResolvedValue(undefined);
vi.mock("../../database/db/index.js", () => ({ vi.mock("../../../database/db/index.js", () => ({
getDb: () => ({}), getDb: () => ({}),
})); }));
vi.mock("../../database/repositories/factory.js", () => ({ vi.mock("../../../database/repositories/factory.js", () => ({
createCurrentSessionRecordingRepository: () => ({ createCurrentSessionRecordingRepository: () => ({
create: mockCreate, create: mockCreate,
updateEnded: mockUpdateEnded, updateEnded: mockUpdateEnded,
}), }),
})); }));
vi.mock("../../utils/logger.js", () => ({ vi.mock("../../../utils/logger.js", () => ({
sshLogger: { sshLogger: {
info: vi.fn(), info: vi.fn(),
warn: vi.fn(), warn: vi.fn(),
@@ -50,7 +50,7 @@ vi.mock("fs", () => ({
})); }));
const { sessionManager } = const { sessionManager } =
await import("../../hosts/terminal-session-manager.js"); await import("../../../hosts/terminal/session-manager.js");
describe("TerminalSessionManager - session logging", () => { describe("TerminalSessionManager - session logging", () => {
beforeEach(() => { beforeEach(() => {
@@ -5,7 +5,7 @@ import {
detectTmux, detectTmux,
tmuxCommand, tmuxCommand,
withTmuxPath, withTmuxPath,
} from "../../hosts/tmux-helper.js"; } from "../../../hosts/tmux/helper.js";
describe("tmux command path handling", () => { describe("tmux command path handling", () => {
it("adds common non-login shell tmux paths", () => { it("adds common non-login shell tmux paths", () => {
@@ -9,7 +9,7 @@ import {
buildPaneMetrics, buildPaneMetrics,
attachPanesToWindows, attachPanesToWindows,
shellEscape, shellEscape,
} from "../../hosts/tmux-monitor-helpers.js"; } from "../../../hosts/tmux/monitor-helpers.js";
function join(...fields: (string | number)[]): string { function join(...fields: (string | number)[]): string {
return fields.join(SEP); return fields.join(SEP);