fix: replace explicit any types with proper TypeScript types

- Create explicit interfaces for Request extensions (AuthenticatedRequest, RequestWithHeaders)
- Add type definitions for WebSocket messages and SSH connection data
- Use generic types in DataCrypto methods instead of any return types
- Define proper interfaces for file manager data structures
- Replace catch block any types with unknown and proper type assertions
- Add HostConfig and TabData interfaces for Server component

Fixes 32 @typescript-eslint/no-explicit-any violations across 5 files
This commit is contained in:
ZacharyZcR
2025-10-09 18:23:16 +08:00
parent d7e98cda04
commit aa6473fb48
5 changed files with 295 additions and 165 deletions

View File

@@ -1,14 +1,55 @@
import { WebSocketServer, WebSocket, type RawData } from "ws";
import { Client, type ClientChannel, type PseudoTtyOptions } from "ssh2";
import {
Client,
type ClientChannel,
type PseudoTtyOptions,
type ConnectConfig,
} from "ssh2";
import { parse as parseUrl } from "url";
import { getDb } from "../database/db/index.js";
import { sshCredentials } from "../database/db/schema.js";
import { eq, and } from "drizzle-orm";
import { sshLogger } from "../utils/logger.js";
import { SimpleDBOps } from "../utils/simple-db-ops.js";
import { AuthManager } from "../utils/auth-manager.js";
import { AuthManager, type JWTPayload } from "../utils/auth-manager.js";
import { UserCrypto } from "../utils/user-crypto.js";
interface ConnectToHostData {
cols: number;
rows: number;
hostConfig: {
id: number;
ip: string;
port: number;
username: string;
password?: string;
key?: string;
keyPassword?: string;
keyType?: string;
authType?: string;
credentialId?: number;
userId?: string;
};
initialPath?: string;
executeCommand?: string;
}
interface ResizeData {
cols: number;
rows: number;
}
interface TOTPResponseData {
code?: string;
}
interface WebSocketMessage {
type: string;
data?: ConnectToHostData | ResizeData | TOTPResponseData | string | unknown;
code?: string;
[key: string]: unknown;
}
const authManager = AuthManager.getInstance();
const userCrypto = UserCrypto.getInstance();
@@ -79,7 +120,7 @@ const wss = new WebSocketServer({
wss.on("connection", async (ws: WebSocket, req) => {
let userId: string | undefined;
let userPayload: any;
let userPayload: JWTPayload | undefined;
try {
const url = parseUrl(req.url!, true);
@@ -187,9 +228,9 @@ wss.on("connection", async (ws: WebSocket, req) => {
return;
}
let parsed: any;
let parsed: WebSocketMessage;
try {
parsed = JSON.parse(msg.toString());
parsed = JSON.parse(msg.toString()) as WebSocketMessage;
} catch (e) {
sshLogger.error("Invalid JSON received", e, {
operation: "websocket_message_invalid_json",
@@ -203,16 +244,17 @@ wss.on("connection", async (ws: WebSocket, req) => {
const { type, data } = parsed;
switch (type) {
case "connectToHost":
if (data.hostConfig) {
data.hostConfig.userId = userId;
case "connectToHost": {
const connectData = data as ConnectToHostData;
if (connectData.hostConfig) {
connectData.hostConfig.userId = userId;
}
handleConnectToHost(data).catch((error) => {
handleConnectToHost(connectData).catch((error) => {
sshLogger.error("Failed to connect to host", error, {
operation: "ssh_connect",
userId,
hostId: data.hostConfig?.id,
ip: data.hostConfig?.ip,
hostId: connectData.hostConfig?.id,
ip: connectData.hostConfig?.ip,
});
ws.send(
JSON.stringify({
@@ -224,43 +266,52 @@ wss.on("connection", async (ws: WebSocket, req) => {
);
});
break;
}
case "resize":
handleResize(data);
case "resize": {
const resizeData = data as ResizeData;
handleResize(resizeData);
break;
}
case "disconnect":
cleanupSSH();
break;
case "input":
case "input": {
const inputData = data as string;
if (sshStream) {
if (data === "\t") {
sshStream.write(data);
} else if (data.startsWith("\x1b")) {
sshStream.write(data);
if (inputData === "\t") {
sshStream.write(inputData);
} else if (
typeof inputData === "string" &&
inputData.startsWith("\x1b")
) {
sshStream.write(inputData);
} else {
try {
sshStream.write(Buffer.from(data, "utf8"));
sshStream.write(Buffer.from(inputData, "utf8"));
} catch (error) {
sshLogger.error("Error writing input to SSH stream", error, {
operation: "ssh_input_encoding",
userId,
dataLength: data.length,
dataLength: inputData.length,
});
sshStream.write(Buffer.from(data, "latin1"));
sshStream.write(Buffer.from(inputData, "latin1"));
}
}
}
break;
}
case "ping":
ws.send(JSON.stringify({ type: "pong" }));
break;
case "totp_response":
if (keyboardInteractiveFinish && data?.code) {
const totpCode = data.code;
case "totp_response": {
const totpData = data as TOTPResponseData;
if (keyboardInteractiveFinish && totpData?.code) {
const totpCode = totpData.code;
sshLogger.info("TOTP code received from user", {
operation: "totp_response",
userId,
@@ -274,7 +325,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
operation: "totp_response_error",
userId,
hasCallback: !!keyboardInteractiveFinish,
hasCode: !!data?.code,
hasCode: !!totpData?.code,
});
ws.send(
JSON.stringify({
@@ -284,6 +335,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
);
}
break;
}
default:
sshLogger.warn("Unknown message type received", {
@@ -294,25 +346,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
}
});
async function handleConnectToHost(data: {
cols: number;
rows: number;
hostConfig: {
id: number;
ip: string;
port: number;
username: string;
password?: string;
key?: string;
keyPassword?: string;
keyType?: string;
authType?: string;
credentialId?: number;
userId?: string;
};
initialPath?: string;
executeCommand?: string;
}) {
async function handleConnectToHost(data: ConnectToHostData) {
const { cols, rows, hostConfig, initialPath, executeCommand } = data;
const {
id,
@@ -642,7 +676,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
},
);
const connectConfig: any = {
const connectConfig: ConnectConfig = {
host: ip,
port,
username,
@@ -650,21 +684,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
keepaliveInterval: 30000,
keepaliveCountMax: 3,
readyTimeout: 60000,
tcpKeepAlive: true,
tcpKeepAliveInitialDelay: 30000,
env: {
TERM: "xterm-256color",
LANG: "en_US.UTF-8",
LC_ALL: "en_US.UTF-8",
LC_CTYPE: "en_US.UTF-8",
LC_MESSAGES: "en_US.UTF-8",
LC_MONETARY: "en_US.UTF-8",
LC_NUMERIC: "en_US.UTF-8",
LC_TIME: "en_US.UTF-8",
LC_COLLATE: "en_US.UTF-8",
COLORTERM: "truecolor",
},
algorithms: {
kex: [
@@ -688,6 +707,15 @@ wss.on("connection", async (ws: WebSocket, req) => {
"aes256-cbc",
"3des-cbc",
],
serverHostKey: [
"ssh-rsa",
"rsa-sha2-256",
"rsa-sha2-512",
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp521",
"ssh-ed25519",
],
hmac: [
"hmac-sha2-256-etm@openssh.com",
"hmac-sha2-512-etm@openssh.com",
@@ -726,13 +754,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
if (resolvedCredentials.keyPassword) {
connectConfig.passphrase = resolvedCredentials.keyPassword;
}
if (
resolvedCredentials.keyType &&
resolvedCredentials.keyType !== "auto"
) {
connectConfig.privateKeyType = resolvedCredentials.keyType;
}
} catch (keyError) {
sshLogger.error("SSH key format error: " + keyError.message);
ws.send(
@@ -766,7 +787,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
sshConn.connect(connectConfig);
}
function handleResize(data: { cols: number; rows: number }) {
function handleResize(data: ResizeData) {
if (sshStream && sshStream.setWindow) {
sshStream.setWindow(data.rows, data.cols, data.rows, data.cols);
ws.send(
@@ -788,8 +809,11 @@ wss.on("connection", async (ws: WebSocket, req) => {
if (sshStream) {
try {
sshStream.end();
} catch (e: any) {
sshLogger.error("Error closing stream: " + e.message);
} catch (e: unknown) {
sshLogger.error(
"Error closing stream: " +
(e instanceof Error ? e.message : "Unknown error"),
);
}
sshStream = null;
}
@@ -797,8 +821,11 @@ wss.on("connection", async (ws: WebSocket, req) => {
if (sshConn) {
try {
sshConn.end();
} catch (e: any) {
sshLogger.error("Error closing connection: " + e.message);
} catch (e: unknown) {
sshLogger.error(
"Error closing connection: " +
(e instanceof Error ? e.message : "Unknown error"),
);
}
sshConn = null;
}
@@ -809,8 +836,11 @@ wss.on("connection", async (ws: WebSocket, req) => {
if (sshConn && sshStream) {
try {
sshStream.write("\x00");
} catch (e: any) {
sshLogger.error("SSH keepalive failed: " + e.message);
} catch (e: unknown) {
sshLogger.error(
"SSH keepalive failed: " +
(e instanceof Error ? e.message : "Unknown error"),
);
cleanupSSH();
}
}