diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index eda828b7..4d7c3ed0 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -702,24 +702,22 @@ router.get("/oidc/callback", async (req, res) => { const getNestedValue = ( obj: Record, path: string, - ): any => { + ): unknown => { if (!path || !obj) return null; return path.split(".").reduce((current, key) => current?.[key], obj); }; - const identifier = - getNestedValue(userInfo, config.identifier_path) || + const identifier = (getNestedValue(userInfo, config.identifier_path) || userInfo[config.identifier_path] || userInfo.sub || userInfo.email || - userInfo.preferred_username; + userInfo.preferred_username) as string; - const name = - getNestedValue(userInfo, config.name_path) || + const name = (getNestedValue(userInfo, config.name_path) || userInfo[config.name_path] || userInfo.name || userInfo.given_name || - identifier; + identifier) as string; if (!identifier) { authLogger.error( @@ -753,14 +751,14 @@ router.get("/oidc/callback", async (req, res) => { is_admin: isFirstUser, is_oidc: true, oidc_identifier: identifier, - client_id: config.client_id, - client_secret: config.client_secret, - issuer_url: config.issuer_url, - authorization_url: config.authorization_url, - token_url: config.token_url, - identifier_path: config.identifier_path, - name_path: config.name_path, - scopes: config.scopes, + client_id: String(config.client_id), + client_secret: String(config.client_secret), + issuer_url: String(config.issuer_url), + authorization_url: String(config.authorization_url), + token_url: String(config.token_url), + identifier_path: String(config.identifier_path), + name_path: String(config.name_path), + scopes: String(config.scopes), }); try { diff --git a/src/backend/ssh/file-manager.ts b/src/backend/ssh/file-manager.ts index 4cc4642d..a00204bb 100644 --- a/src/backend/ssh/file-manager.ts +++ b/src/backend/ssh/file-manager.ts @@ -8,6 +8,7 @@ import { eq, and } from "drizzle-orm"; import { fileLogger } from "../utils/logger.js"; import { SimpleDBOps } from "../utils/simple-db-ops.js"; import { AuthManager } from "../utils/auth-manager.js"; +import type { AuthenticatedRequest } from "../../types/index.js"; function isExecutableFile(permissions: string, fileName: string): boolean { const hasExecutePermission = @@ -166,7 +167,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { credentialId, } = req.body; - const userId = (req as any).userId; + const userId = (req as AuthenticatedRequest).userId; if (!userId) { fileLogger.error("SSH connection rejected: no authenticated user", { @@ -246,7 +247,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { ); } - const config: any = { + const config: Record = { host: ip, port: port || 22, username, @@ -417,7 +418,9 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { }); } else { if (resolvedCredentials.password) { - const responses = prompts.map(() => resolvedCredentials.password || ""); + const responses = prompts.map( + () => resolvedCredentials.password || "", + ); finish(responses); } else { finish(prompts.map(() => "")); @@ -432,7 +435,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { app.post("/ssh/file_manager/ssh/connect-totp", async (req, res) => { const { sessionId, totpCode } = req.body; - const userId = (req as any).userId; + const userId = (req as AuthenticatedRequest).userId; if (!userId) { fileLogger.error("TOTP verification rejected: no authenticated user", { @@ -454,7 +457,9 @@ app.post("/ssh/file_manager/ssh/connect-totp", async (req, res) => { sessionId, userId, }); - return res.status(404).json({ error: "TOTP session expired. Please reconnect." }); + return res + .status(404) + .json({ error: "TOTP session expired. Please reconnect." }); } delete pendingTOTPSessions[sessionId]; @@ -462,8 +467,12 @@ app.post("/ssh/file_manager/ssh/connect-totp", async (req, res) => { if (Date.now() - session.createdAt > 120000) { try { session.client.end(); - } catch {} - return res.status(408).json({ error: "TOTP session timeout. Please reconnect." }); + } catch { + // Ignore errors when closing timed out session + } + return res + .status(408) + .json({ error: "TOTP session timeout. Please reconnect." }); } session.finish([totpCode]); @@ -487,7 +496,10 @@ app.post("/ssh/file_manager/ssh/connect-totp", async (req, res) => { userId, }); - res.json({ status: "success", message: "TOTP verified, SSH connection established" }); + res.json({ + status: "success", + message: "TOTP verified, SSH connection established", + }); }); session.client.on("error", (err) => { diff --git a/src/backend/ssh/server-stats.ts b/src/backend/ssh/server-stats.ts index e640feef..38ed66c9 100644 --- a/src/backend/ssh/server-stats.ts +++ b/src/backend/ssh/server-stats.ts @@ -194,7 +194,7 @@ class SSHConnectionPool { } class RequestQueue { - private queues = new Map Promise>>(); + private queues = new Map Promise>>(); private processing = new Set(); async queueRequest(hostId: number, request: () => Promise): Promise { diff --git a/src/backend/ssh/tunnel.ts b/src/backend/ssh/tunnel.ts index 335f84f3..afb6f095 100644 --- a/src/backend/ssh/tunnel.ts +++ b/src/backend/ssh/tunnel.ts @@ -903,7 +903,7 @@ async function connectSSHTunnel( }); }); - const connOptions: any = { + const connOptions: Record = { host: tunnelConfig.sourceIP, port: tunnelConfig.sourceSSHPort, username: tunnelConfig.sourceUsername, @@ -1065,7 +1065,7 @@ async function killRemoteTunnelByMarker( } const conn = new Client(); - const connOptions: any = { + const connOptions: Record = { host: tunnelConfig.sourceIP, port: tunnelConfig.sourceSSHPort, username: tunnelConfig.sourceUsername, @@ -1461,10 +1461,10 @@ async function initializeAutoStartTunnels(): Promise { }); }, 1000); } - } catch (error: any) { + } catch (error) { tunnelLogger.error( "Failed to initialize auto-start tunnels:", - error.message, + error instanceof Error ? error.message : "Unknown error", ); } } diff --git a/src/backend/utils/database-file-encryption.ts b/src/backend/utils/database-file-encryption.ts index 523280ce..002464b0 100644 --- a/src/backend/utils/database-file-encryption.ts +++ b/src/backend/utils/database-file-encryption.ts @@ -30,7 +30,11 @@ class DatabaseFileEncryption { const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(this.ALGORITHM, key, iv) as any; + const cipher = crypto.createCipheriv( + this.ALGORITHM, + key, + iv, + ) as crypto.CipherGCM; const encrypted = Buffer.concat([cipher.update(buffer), cipher.final()]); const tag = cipher.getAuthTag(); @@ -78,7 +82,11 @@ class DatabaseFileEncryption { const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(this.ALGORITHM, key, iv) as any; + const cipher = crypto.createCipheriv( + this.ALGORITHM, + key, + iv, + ) as crypto.CipherGCM; const encrypted = Buffer.concat([ cipher.update(sourceData), cipher.final(), @@ -163,7 +171,7 @@ class DatabaseFileEncryption { metadata.algorithm, key, Buffer.from(metadata.iv, "hex"), - ) as any; + ) as crypto.DecipherGCM; decipher.setAuthTag(Buffer.from(metadata.tag, "hex")); const decryptedBuffer = Buffer.concat([ @@ -233,7 +241,7 @@ class DatabaseFileEncryption { metadata.algorithm, key, Buffer.from(metadata.iv, "hex"), - ) as any; + ) as crypto.DecipherGCM; decipher.setAuthTag(Buffer.from(metadata.tag, "hex")); const decrypted = Buffer.concat([ diff --git a/src/backend/utils/database-migration.ts b/src/backend/utils/database-migration.ts index 0fba053e..153ef18d 100644 --- a/src/backend/utils/database-migration.ts +++ b/src/backend/utils/database-migration.ts @@ -234,7 +234,9 @@ export class DatabaseMigration { memoryDb.exec("PRAGMA foreign_keys = OFF"); for (const table of tables) { - const rows = originalDb.prepare(`SELECT * FROM ${table.name}`).all(); + const rows = originalDb + .prepare(`SELECT * FROM ${table.name}`) + .all() as Record[]; if (rows.length > 0) { const columns = Object.keys(rows[0]); @@ -244,7 +246,7 @@ export class DatabaseMigration { ); const insertTransaction = memoryDb.transaction( - (dataRows: any[]) => { + (dataRows: Record[]) => { for (const row of dataRows) { const values = columns.map((col) => row[col]); insertStmt.run(values); diff --git a/src/backend/utils/lazy-field-encryption.ts b/src/backend/utils/lazy-field-encryption.ts index 98f7aa44..e64db2a3 100644 --- a/src/backend/utils/lazy-field-encryption.ts +++ b/src/backend/utils/lazy-field-encryption.ts @@ -1,6 +1,14 @@ import { FieldCrypto } from "./field-crypto.js"; import { databaseLogger } from "./logger.js"; +interface DatabaseInstance { + prepare: (sql: string) => { + all: (param?: unknown) => unknown[]; + get: (param?: unknown) => unknown; + run: (...params: unknown[]) => unknown; + }; +} + export class LazyFieldEncryption { private static readonly LEGACY_FIELD_NAME_MAP: Record = { key_password: "keyPassword", @@ -182,12 +190,12 @@ export class LazyFieldEncryption { } static migrateRecordSensitiveFields( - record: any, + record: Record, sensitiveFields: string[], userKEK: Buffer, recordId: string, ): { - updatedRecord: any; + updatedRecord: Record; migratedFields: string[]; needsUpdate: boolean; } { @@ -202,7 +210,7 @@ export class LazyFieldEncryption { try { const { encrypted, wasPlaintext, wasLegacyEncryption } = this.migrateFieldToEncrypted( - fieldValue, + fieldValue as string, userKEK, recordId, fieldName, @@ -279,7 +287,7 @@ export class LazyFieldEncryption { static async checkUserNeedsMigration( userId: string, userKEK: Buffer, - db: any, + db: DatabaseInstance, ): Promise<{ needsMigration: boolean; plaintextFields: Array<{ @@ -298,7 +306,9 @@ export class LazyFieldEncryption { try { const sshHosts = db .prepare("SELECT * FROM ssh_data WHERE user_id = ?") - .all(userId); + .all(userId) as Array< + Record & { id: string | number } + >; for (const host of sshHosts) { const sensitiveFields = this.getSensitiveFieldsForTable("ssh_data"); const hostPlaintextFields: string[] = []; @@ -307,7 +317,7 @@ export class LazyFieldEncryption { if ( host[field] && this.fieldNeedsMigration( - host[field], + host[field] as string, userKEK, host.id.toString(), field, @@ -329,7 +339,9 @@ export class LazyFieldEncryption { const sshCredentials = db .prepare("SELECT * FROM ssh_credentials WHERE user_id = ?") - .all(userId); + .all(userId) as Array< + Record & { id: string | number } + >; for (const credential of sshCredentials) { const sensitiveFields = this.getSensitiveFieldsForTable("ssh_credentials"); @@ -339,7 +351,7 @@ export class LazyFieldEncryption { if ( credential[field] && this.fieldNeedsMigration( - credential[field], + credential[field] as string, userKEK, credential.id.toString(), field, diff --git a/src/backend/utils/logger.ts b/src/backend/utils/logger.ts index f020047a..1d8b340e 100644 --- a/src/backend/utils/logger.ts +++ b/src/backend/utils/logger.ts @@ -11,7 +11,7 @@ export interface LogContext { sessionId?: string; requestId?: string; duration?: number; - [key: string]: any; + [key: string]: unknown; } const SENSITIVE_FIELDS = [ diff --git a/src/backend/utils/user-data-import.ts b/src/backend/utils/user-data-import.ts index 60f41dac..da776893 100644 --- a/src/backend/utils/user-data-import.ts +++ b/src/backend/utils/user-data-import.ts @@ -89,7 +89,7 @@ class UserDataImport { ) { const importStats = await this.importSshHosts( targetUserId, - exportData.userData.sshHosts, + exportData.userData.sshHosts as Record[], { replaceExisting, dryRun, userDataKey }, ); result.summary.sshHostsImported = importStats.imported; @@ -104,7 +104,7 @@ class UserDataImport { ) { const importStats = await this.importSshCredentials( targetUserId, - exportData.userData.sshCredentials, + exportData.userData.sshCredentials as Record[], { replaceExisting, dryRun, userDataKey }, ); result.summary.sshCredentialsImported = importStats.imported; @@ -129,7 +129,7 @@ class UserDataImport { ) { const importStats = await this.importDismissedAlerts( targetUserId, - exportData.userData.dismissedAlerts, + exportData.userData.dismissedAlerts as Record[], { replaceExisting, dryRun }, ); result.summary.dismissedAlertsImported = importStats.imported; @@ -159,7 +159,7 @@ class UserDataImport { private static async importSshHosts( targetUserId: string, - sshHosts: any[], + sshHosts: Record[], options: { replaceExisting: boolean; dryRun: boolean; @@ -198,7 +198,9 @@ class UserDataImport { delete processedHostData.id; - await getDb().insert(sshData).values(processedHostData); + await getDb() + .insert(sshData) + .values(processedHostData as unknown as typeof sshData.$inferInsert); imported++; } catch (error) { errors.push( @@ -213,7 +215,7 @@ class UserDataImport { private static async importSshCredentials( targetUserId: string, - credentials: any[], + credentials: Record[], options: { replaceExisting: boolean; dryRun: boolean; @@ -254,7 +256,11 @@ class UserDataImport { delete processedCredentialData.id; - await getDb().insert(sshCredentials).values(processedCredentialData); + await getDb() + .insert(sshCredentials) + .values( + processedCredentialData as unknown as typeof sshCredentials.$inferInsert, + ); imported++; } catch (error) { errors.push( @@ -269,7 +275,7 @@ class UserDataImport { private static async importFileManagerData( targetUserId: string, - fileManagerData: any, + fileManagerData: Record, options: { replaceExisting: boolean; dryRun: boolean }, ) { let imported = 0; @@ -356,7 +362,7 @@ class UserDataImport { private static async importDismissedAlerts( targetUserId: string, - alerts: any[], + alerts: Record[], options: { replaceExisting: boolean; dryRun: boolean }, ) { let imported = 0; @@ -376,7 +382,7 @@ class UserDataImport { .where( and( eq(dismissedAlerts.userId, targetUserId), - eq(dismissedAlerts.alertId, alert.alertId), + eq(dismissedAlerts.alertId, alert.alertId as string), ), ); @@ -395,10 +401,12 @@ class UserDataImport { if (existing.length > 0 && options.replaceExisting) { await getDb() .update(dismissedAlerts) - .set(newAlert) + .set(newAlert as typeof dismissedAlerts.$inferInsert) .where(eq(dismissedAlerts.id, existing[0].id)); } else { - await getDb().insert(dismissedAlerts).values(newAlert); + await getDb() + .insert(dismissedAlerts) + .values(newAlert as typeof dismissedAlerts.$inferInsert); } imported++; diff --git a/src/components/ui/password-input.tsx b/src/components/ui/password-input.tsx index f1cd0066..5eac52b5 100644 --- a/src/components/ui/password-input.tsx +++ b/src/components/ui/password-input.tsx @@ -5,8 +5,7 @@ import { Eye, EyeOff } from "lucide-react"; import { Input } from "@/components/ui/input"; import { cn } from "@/lib/utils"; -interface PasswordInputProps - extends React.InputHTMLAttributes {} +type PasswordInputProps = React.InputHTMLAttributes; export const PasswordInput = React.forwardRef< HTMLInputElement, diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index 557077ac..04e0013c 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -8,7 +8,10 @@ const Toaster = ({ ...props }: ToasterProps) => { const originalToast = toast; - const rateLimitedToast = (message: string, options?: any) => { + const rateLimitedToast = ( + message: string, + options?: Record, + ) => { const now = Date.now(); const lastToast = lastToastRef.current; @@ -25,13 +28,13 @@ const Toaster = ({ ...props }: ToasterProps) => { }; Object.assign(toast, { - success: (message: string, options?: any) => + success: (message: string, options?: Record) => rateLimitedToast(message, { ...options, type: "success" }), - error: (message: string, options?: any) => + error: (message: string, options?: Record) => rateLimitedToast(message, { ...options, type: "error" }), - warning: (message: string, options?: any) => + warning: (message: string, options?: Record) => rateLimitedToast(message, { ...options, type: "warning" }), - info: (message: string, options?: any) => + info: (message: string, options?: Record) => rateLimitedToast(message, { ...options, type: "info" }), message: rateLimitedToast, }); diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx index e306ca0a..6c816b16 100644 --- a/src/components/ui/textarea.tsx +++ b/src/components/ui/textarea.tsx @@ -2,8 +2,7 @@ import * as React from "react"; import { cn } from "../../lib/utils"; -export interface TextareaProps - extends React.TextareaHTMLAttributes {} +export type TextareaProps = React.TextareaHTMLAttributes; const Textarea = React.forwardRef( ({ className, ...props }, ref) => { diff --git a/src/components/ui/version-check-modal.tsx b/src/components/ui/version-check-modal.tsx index 3762c9ce..52b10ddd 100644 --- a/src/components/ui/version-check-modal.tsx +++ b/src/components/ui/version-check-modal.tsx @@ -14,7 +14,10 @@ export function VersionCheckModal({ isAuthenticated = false, }: VersionCheckModalProps) { const { t } = useTranslation(); - const [versionInfo, setVersionInfo] = useState(null); + const [versionInfo, setVersionInfo] = useState | null>(null); const [versionChecking, setVersionChecking] = useState(false); const [versionDismissed] = useState(false); diff --git a/src/lib/frontend-logger.ts b/src/lib/frontend-logger.ts index 2b25b2b3..0eec0783 100644 --- a/src/lib/frontend-logger.ts +++ b/src/lib/frontend-logger.ts @@ -17,7 +17,7 @@ export interface LogContext { errorCode?: string; errorMessage?: string; - [key: string]: any; + [key: string]: unknown; } class FrontendLogger { diff --git a/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx b/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx index 27bd7035..d4e0d7d3 100644 --- a/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx +++ b/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx @@ -155,7 +155,9 @@ export function CredentialEditor({ type FormData = z.infer; const form = useForm({ - resolver: zodResolver(formSchema) as any, + resolver: zodResolver(formSchema) as unknown as Parameters< + typeof useForm + >[0]["resolver"], defaultValues: { name: "", description: "", @@ -198,7 +200,7 @@ export function CredentialEditor({ formData.publicKey = fullCredentialDetails.publicKey || ""; formData.keyPassword = fullCredentialDetails.keyPassword || ""; formData.keyType = - (fullCredentialDetails.keyType as any) || ("auto" as const); + (fullCredentialDetails.keyType as string) || ("auto" as const); } form.reset(formData); diff --git a/src/ui/Desktop/Apps/Credentials/CredentialsManager.tsx b/src/ui/Desktop/Apps/Credentials/CredentialsManager.tsx index b0df5255..aedae3d5 100644 --- a/src/ui/Desktop/Apps/Credentials/CredentialsManager.tsx +++ b/src/ui/Desktop/Apps/Credentials/CredentialsManager.tsx @@ -71,7 +71,15 @@ export function CredentialsManager({ const [showDeployDialog, setShowDeployDialog] = useState(false); const [deployingCredential, setDeployingCredential] = useState(null); - const [availableHosts, setAvailableHosts] = useState([]); + const [availableHosts, setAvailableHosts] = useState< + Array<{ + id: number; + name: string; + ip: string; + port: number; + username: string; + }> + >([]); const [selectedHostId, setSelectedHostId] = useState(""); const [deployLoading, setDeployLoading] = useState(false); const [hostSearchQuery, setHostSearchQuery] = useState(""); @@ -207,10 +215,13 @@ export function CredentialsManager({ ); await fetchCredentials(); window.dispatchEvent(new CustomEvent("credentials:changed")); - } catch (err: any) { - if (err.response?.data?.details) { + } catch (err: unknown) { + const error = err as { + response?: { data?: { error?: string; details?: string } }; + }; + if (error.response?.data?.details) { toast.error( - `${err.response.data.error}\n${err.response.data.details}`, + `${error.response.data.error}\n${error.response.data.details}`, ); } else { toast.error(t("credentials.failedToDeleteCredential")); diff --git a/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx b/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx index 56887ae5..70cf8d3a 100644 --- a/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx @@ -96,15 +96,19 @@ export function DiffViewer({ setContent1(response1.content || ""); setContent2(response2.content || ""); - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to load files for diff:", error); - const errorData = error?.response?.data; + const err = error as { + message?: string; + response?: { data?: { tooLarge?: boolean; error?: string } }; + }; + const errorData = err?.response?.data; if (errorData?.tooLarge) { setError(t("fileManager.fileTooLarge", { error: errorData.error })); } else if ( - error.message?.includes("connection") || - error.message?.includes("established") + err.message?.includes("connection") || + err.message?.includes("established") ) { setError( t("fileManager.sshConnectionFailed", { @@ -117,9 +121,7 @@ export function DiffViewer({ setError( t("fileManager.loadFileFailed", { error: - error.message || - errorData?.error || - t("fileManager.unknownError"), + err.message || errorData?.error || t("fileManager.unknownError"), }), ); } @@ -157,12 +159,13 @@ export function DiffViewer({ t("fileManager.downloadFileSuccess", { name: file.name }), ); } - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to download file:", error); + const err = error as { message?: string }; toast.error( t("fileManager.downloadFileFailed") + ": " + - (error.message || t("fileManager.unknownError")), + (err.message || t("fileManager.unknownError")), ); } }; diff --git a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx index ad28c650..914ad304 100644 --- a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx @@ -289,7 +289,7 @@ function getLanguageExtension(filename: string) { return language ? loadLanguage(language) : null; } -function formatFileSize(bytes?: number, t?: any): string { +function formatFileSize(bytes?: number, t?: (key: string) => string): string { if (!bytes) return t ? t("fileManager.unknownSize") : "Unknown size"; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(1024)); @@ -323,7 +323,9 @@ export function FileViewer({ const [pdfScale, setPdfScale] = useState(1.2); const [pdfError, setPdfError] = useState(false); const [markdownEditMode, setMarkdownEditMode] = useState(false); - const editorRef = useRef(null); + const editorRef = useRef<{ + view?: { dispatch: (transaction: unknown) => void }; + } | null>(null); const fileTypeInfo = getFileType(file.name); diff --git a/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx b/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx index 3dac7a53..1075775d 100644 --- a/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx @@ -157,28 +157,40 @@ export function FileWindow({ const extension = file.name.split(".").pop()?.toLowerCase(); setIsEditable(!mediaExtensions.includes(extension || "")); - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to load file:", error); - const errorData = error?.response?.data; + const err = error as { + message?: string; + isFileNotFound?: boolean; + response?: { + status?: number; + data?: { + tooLarge?: boolean; + error?: string; + fileNotFound?: boolean; + }; + }; + }; + const errorData = err?.response?.data; if (errorData?.tooLarge) { toast.error(`File too large: ${errorData.error}`, { duration: 10000, }); } else if ( - error.message?.includes("connection") || - error.message?.includes("established") + err.message?.includes("connection") || + err.message?.includes("established") ) { toast.error( `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`, ); } else { const errorMessage = - errorData?.error || error.message || "Unknown error"; + errorData?.error || err.message || "Unknown error"; const isFileNotFound = - (error as any).isFileNotFound || + err.isFileNotFound || errorData?.fileNotFound || - error.response?.status === 404 || + err.response?.status === 404 || errorMessage.includes("File not found") || errorMessage.includes("No such file or directory") || errorMessage.includes("cannot access") || @@ -229,10 +241,11 @@ export function FileWindow({ const contentSize = new Blob([fileContent]).size; file.size = contentSize; } - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to load file content:", error); + const err = error as { message?: string }; toast.error( - `${t("fileManager.failedToLoadFile")}: ${error.message || t("fileManager.unknownError")}`, + `${t("fileManager.failedToLoadFile")}: ${err.message || t("fileManager.unknownError")}`, ); } finally { setIsLoading(false); @@ -258,19 +271,20 @@ export function FileWindow({ } toast.success(t("fileManager.fileSavedSuccessfully")); - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to save file:", error); + const err = error as { message?: string }; if ( - error.message?.includes("connection") || - error.message?.includes("established") + err.message?.includes("connection") || + err.message?.includes("established") ) { toast.error( `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`, ); } else { toast.error( - `${t("fileManager.failedToSaveFile")}: ${error.message || t("fileManager.unknownError")}`, + `${t("fileManager.failedToSaveFile")}: ${err.message || t("fileManager.unknownError")}`, ); } } finally { @@ -335,19 +349,20 @@ export function FileWindow({ toast.success(t("fileManager.fileDownloadedSuccessfully")); } - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to download file:", error); + const err = error as { message?: string }; if ( - error.message?.includes("connection") || - error.message?.includes("established") + err.message?.includes("connection") || + err.message?.includes("established") ) { toast.error( `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`, ); } else { toast.error( - `Failed to download file: ${error.message || "Unknown error"}`, + `Failed to download file: ${err.message || "Unknown error"}`, ); } } diff --git a/src/ui/Desktop/Apps/File Manager/components/TerminalWindow.tsx b/src/ui/Desktop/Apps/File Manager/components/TerminalWindow.tsx index c8971107..7f32de01 100644 --- a/src/ui/Desktop/Apps/File Manager/components/TerminalWindow.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/TerminalWindow.tsx @@ -38,9 +38,17 @@ export function TerminalWindow({ const { t } = useTranslation(); const { closeWindow, maximizeWindow, focusWindow, windows } = useWindowManager(); - const terminalRef = React.useRef(null); + const terminalRef = React.useRef<{ fit?: () => void } | null>(null); const resizeTimeoutRef = React.useRef(null); + React.useEffect(() => { + return () => { + if (resizeTimeoutRef.current) { + clearTimeout(resizeTimeoutRef.current); + } + }; + }, []); + const currentWindow = windows.find((w) => w.id === windowId); if (!currentWindow) { return null; @@ -70,14 +78,6 @@ export function TerminalWindow({ }, 100); }; - React.useEffect(() => { - return () => { - if (resizeTimeoutRef.current) { - clearTimeout(resizeTimeoutRef.current); - } - }; - }, []); - const terminalTitle = executeCommand ? t("terminal.runTitle", { host: hostConfig.name, command: executeCommand }) : initialPath diff --git a/src/ui/Desktop/Apps/Host Manager/HostManager.tsx b/src/ui/Desktop/Apps/Host Manager/HostManager.tsx index e6b07a59..6749f2a7 100644 --- a/src/ui/Desktop/Apps/Host Manager/HostManager.tsx +++ b/src/ui/Desktop/Apps/Host Manager/HostManager.tsx @@ -21,7 +21,11 @@ export function HostManager({ const [activeTab, setActiveTab] = useState("host_viewer"); const [editingHost, setEditingHost] = useState(null); - const [editingCredential, setEditingCredential] = useState(null); + const [editingCredential, setEditingCredential] = useState<{ + id: number; + name?: string; + username: string; + } | null>(null); const { state: sidebarState } = useSidebar(); const handleEditHost = (host: SSHHost) => { @@ -34,7 +38,11 @@ export function HostManager({ setActiveTab("host_viewer"); }; - const handleEditCredential = (credential: any) => { + const handleEditCredential = (credential: { + id: number; + name?: string; + username: string; + }) => { setEditingCredential(credential); setActiveTab("add_credential"); }; diff --git a/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx b/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx index 5f4c0224..df38275f 100644 --- a/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx +++ b/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx @@ -60,7 +60,14 @@ interface SSHHost { enableTunnel: boolean; enableFileManager: boolean; defaultPath: string; - tunnelConnections: any[]; + tunnelConnections: Array<{ + sourcePort: number; + endpointPort: number; + endpointHost: string; + maxRetries: number; + retryInterval: number; + autoStart: boolean; + }>; statsConfig?: StatsConfig; createdAt: string; updatedAt: string; @@ -79,7 +86,9 @@ export function HostManagerEditor({ const { t } = useTranslation(); const [folders, setFolders] = useState([]); const [sshConfigurations, setSshConfigurations] = useState([]); - const [credentials, setCredentials] = useState([]); + const [credentials, setCredentials] = useState< + Array<{ id: number; username: string; authType: string }> + >([]); const [authTab, setAuthTab] = useState<"password" | "key" | "credential">( "password", @@ -292,7 +301,7 @@ export function HostManagerEditor({ type FormData = z.infer; const form = useForm({ - resolver: zodResolver(formSchema) as any, + resolver: zodResolver(formSchema), defaultValues: { name: "", ip: "", @@ -377,7 +386,17 @@ export function HostManagerEditor({ } else if (defaultAuthType === "key") { formData.key = editingHost.id ? "existing_key" : editingHost.key; formData.keyPassword = cleanedHost.keyPassword || ""; - formData.keyType = (cleanedHost.keyType as any) || "auto"; + formData.keyType = + (cleanedHost.keyType as + | "auto" + | "ssh-rsa" + | "ssh-ed25519" + | "ecdsa-sha2-nistp256" + | "ecdsa-sha2-nistp384" + | "ecdsa-sha2-nistp521" + | "ssh-dss" + | "ssh-rsa-sha2-256" + | "ssh-rsa-sha2-512") || "auto"; } else if (defaultAuthType === "credential") { formData.credentialId = cleanedHost.credentialId || "existing_credential"; @@ -430,7 +449,7 @@ export function HostManagerEditor({ data.name = `${data.username}@${data.ip}`; } - const submitData: any = { + const submitData: Record = { name: data.name, ip: data.ip, port: data.port, diff --git a/src/ui/Desktop/Apps/Server/widgets/NetworkWidget.tsx b/src/ui/Desktop/Apps/Server/widgets/NetworkWidget.tsx index 0093d92e..4a3e7379 100644 --- a/src/ui/Desktop/Apps/Server/widgets/NetworkWidget.tsx +++ b/src/ui/Desktop/Apps/Server/widgets/NetworkWidget.tsx @@ -11,7 +11,16 @@ interface NetworkWidgetProps { export function NetworkWidget({ metrics }: NetworkWidgetProps) { const { t } = useTranslation(); - const network = (metrics as any)?.network; + const metricsWithNetwork = metrics as ServerMetrics & { + network?: { + interfaces?: Array<{ + name: string; + state: string; + ip: string; + }>; + }; + }; + const network = metricsWithNetwork?.network; const interfaces = network?.interfaces || []; return ( @@ -30,7 +39,7 @@ export function NetworkWidget({ metrics }: NetworkWidgetProps) {

{t("serverStats.noInterfacesFound")}

) : ( - interfaces.map((iface: any, index: number) => ( + interfaces.map((iface, index: number) => (
; + }; + }; + const processes = metricsWithProcesses?.processes; const topProcesses = processes?.top || []; return ( @@ -46,7 +59,7 @@ export function ProcessesWidget({ metrics }: ProcessesWidgetProps) {
) : (
- {topProcesses.map((proc: any, index: number) => ( + {topProcesses.map((proc, index: number) => (
diff --git a/src/ui/Desktop/Apps/Server/widgets/UptimeWidget.tsx b/src/ui/Desktop/Apps/Server/widgets/UptimeWidget.tsx index 9f47382f..c8f02db7 100644 --- a/src/ui/Desktop/Apps/Server/widgets/UptimeWidget.tsx +++ b/src/ui/Desktop/Apps/Server/widgets/UptimeWidget.tsx @@ -11,7 +11,13 @@ interface UptimeWidgetProps { export function UptimeWidget({ metrics }: UptimeWidgetProps) { const { t } = useTranslation(); - const uptime = (metrics as any)?.uptime; + const metricsWithUptime = metrics as ServerMetrics & { + uptime?: { + formatted?: string; + seconds?: number; + }; + }; + const uptime = metricsWithUptime?.uptime; return (
diff --git a/src/ui/Desktop/Apps/Tunnel/TunnelViewer.tsx b/src/ui/Desktop/Apps/Tunnel/TunnelViewer.tsx index 4327244c..10df33f7 100644 --- a/src/ui/Desktop/Apps/Tunnel/TunnelViewer.tsx +++ b/src/ui/Desktop/Apps/Tunnel/TunnelViewer.tsx @@ -11,7 +11,7 @@ interface SSHTunnelViewerProps { action: "connect" | "disconnect" | "cancel", host: SSHHost, tunnelIndex: number, - ) => Promise; + ) => Promise; } export function TunnelViewer({ diff --git a/src/ui/Desktop/Homepage/HomepageAuth.tsx b/src/ui/Desktop/Homepage/HomepageAuth.tsx index ab13afd2..43d4c9b9 100644 --- a/src/ui/Desktop/Homepage/HomepageAuth.tsx +++ b/src/ui/Desktop/Homepage/HomepageAuth.tsx @@ -164,7 +164,7 @@ export function HomepageAuth({ } try { - let res, meRes; + let res; if (tab === "login") { res = await loginUser(localUsername, password); } else { @@ -194,7 +194,7 @@ export function HomepageAuth({ throw new Error(t("errors.loginFailed")); } - [meRes] = await Promise.all([getUserInfo()]); + const [meRes] = await Promise.all([getUserInfo()]); setInternalLoggedIn(true); setLoggedIn(true); @@ -217,16 +217,22 @@ export function HomepageAuth({ setTotpRequired(false); setTotpCode(""); setTotpTempToken(""); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; const errorMessage = - err?.response?.data?.error || err?.message || t("errors.unknownError"); + error?.response?.data?.error || + error?.message || + t("errors.unknownError"); toast.error(errorMessage); setInternalLoggedIn(false); setLoggedIn(false); setIsAdmin(false); setUsername(null); setUserId(null); - if (err?.response?.data?.error?.includes("Database")) { + if (error?.response?.data?.error?.includes("Database")) { setDbConnectionFailed(true); } else { setDbError(null); @@ -242,10 +248,14 @@ export function HomepageAuth({ await initiatePasswordReset(localUsername); setResetStep("verify"); toast.success(t("messages.resetCodeSent")); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; toast.error( - err?.response?.data?.error || - err?.message || + error?.response?.data?.error || + error?.message || t("errors.failedPasswordReset"), ); } finally { @@ -260,8 +270,9 @@ export function HomepageAuth({ setTempToken(response.tempToken); setResetStep("newPassword"); toast.success(t("messages.codeVerified")); - } catch (err: any) { - toast.error(err?.response?.data?.error || t("errors.failedVerifyCode")); + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; + toast.error(error?.response?.data?.error || t("errors.failedVerifyCode")); } finally { setResetLoading(false); } @@ -296,9 +307,10 @@ export function HomepageAuth({ setTab("login"); resetPasswordState(); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; toast.error( - err?.response?.data?.error || t("errors.failedCompleteReset"), + error?.response?.data?.error || t("errors.failedCompleteReset"), ); } finally { setResetLoading(false); @@ -359,11 +371,15 @@ export function HomepageAuth({ setTotpCode(""); setTotpTempToken(""); toast.success(t("messages.loginSuccess")); - } catch (err: any) { - const errorCode = err?.response?.data?.code; + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { code?: string; error?: string } }; + }; + const errorCode = error?.response?.data?.code; const errorMessage = - err?.response?.data?.error || - err?.message || + error?.response?.data?.error || + error?.message || t("errors.invalidTotpCode"); if (errorCode === "SESSION_EXPIRED") { @@ -391,10 +407,14 @@ export function HomepageAuth({ } window.location.replace(authUrl); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; const errorMessage = - err?.response?.data?.error || - err?.message || + error?.response?.data?.error || + error?.message || t("errors.failedOidcLogin"); toast.error(errorMessage); setOidcLoading(false); diff --git a/src/ui/Desktop/Navigation/Hosts/FolderCard.tsx b/src/ui/Desktop/Navigation/Hosts/FolderCard.tsx index 72c12084..aabe27e0 100644 --- a/src/ui/Desktop/Navigation/Hosts/FolderCard.tsx +++ b/src/ui/Desktop/Navigation/Hosts/FolderCard.tsx @@ -23,7 +23,14 @@ interface SSHHost { enableTunnel: boolean; enableFileManager: boolean; defaultPath: string; - tunnelConnections: any[]; + tunnelConnections: Array<{ + sourcePort: number; + endpointPort: number; + endpointHost: string; + maxRetries: number; + retryInterval: number; + autoStart: boolean; + }>; createdAt: string; updatedAt: string; } diff --git a/src/ui/Desktop/Navigation/Hosts/Host.tsx b/src/ui/Desktop/Navigation/Hosts/Host.tsx index 50688a36..de08682d 100644 --- a/src/ui/Desktop/Navigation/Hosts/Host.tsx +++ b/src/ui/Desktop/Navigation/Hosts/Host.tsx @@ -20,7 +20,6 @@ export function Host({ host }: HostProps): React.ReactElement { : `${host.username}@${host.ip}:${host.port}`; useEffect(() => { - let intervalId: number | undefined; let cancelled = false; const fetchStatus = async () => { @@ -29,13 +28,14 @@ export function Host({ host }: HostProps): React.ReactElement { if (!cancelled) { setServerStatus(res?.status === "online" ? "online" : "offline"); } - } catch (error: any) { + } catch (error: unknown) { if (!cancelled) { - if (error?.response?.status === 503) { + const err = error as { response?: { status?: number } }; + if (err?.response?.status === 503) { setServerStatus("offline"); - } else if (error?.response?.status === 504) { + } else if (err?.response?.status === 504) { setServerStatus("degraded"); - } else if (error?.response?.status === 404) { + } else if (err?.response?.status === 404) { setServerStatus("offline"); } else { setServerStatus("offline"); @@ -46,7 +46,7 @@ export function Host({ host }: HostProps): React.ReactElement { fetchStatus(); - intervalId = window.setInterval(fetchStatus, 30000); + const intervalId = window.setInterval(fetchStatus, 30000); return () => { cancelled = true; diff --git a/src/ui/Desktop/Navigation/Tabs/TabContext.tsx b/src/ui/Desktop/Navigation/Tabs/TabContext.tsx index 0f5d6d32..0ce51380 100644 --- a/src/ui/Desktop/Navigation/Tabs/TabContext.tsx +++ b/src/ui/Desktop/Navigation/Tabs/TabContext.tsx @@ -19,7 +19,16 @@ interface TabContextType { setCurrentTab: (tabId: number) => void; setSplitScreenTab: (tabId: number) => void; getTab: (tabId: number) => Tab | undefined; - updateHostConfig: (hostId: number, newHostConfig: any) => void; + updateHostConfig: ( + hostId: number, + newHostConfig: { + id: number; + name?: string; + username: string; + ip: string; + port: number; + }, + ) => void; } const TabContext = createContext(undefined); @@ -98,7 +107,9 @@ export function TabProvider({ children }: TabProviderProps) { id, title: effectiveTitle, terminalRef: - tabData.type === "terminal" ? React.createRef() : undefined, + tabData.type === "terminal" + ? React.createRef<{ disconnect?: () => void }>() + : undefined, }; setTabs((prev) => [...prev, newTab]); setCurrentTab(id); @@ -140,7 +151,16 @@ export function TabProvider({ children }: TabProviderProps) { return tabs.find((tab) => tab.id === tabId); }; - const updateHostConfig = (hostId: number, newHostConfig: any) => { + const updateHostConfig = ( + hostId: number, + newHostConfig: { + id: number; + name?: string; + username: string; + ip: string; + port: number; + }, + ) => { setTabs((prev) => prev.map((tab) => { if (tab.hostConfig && tab.hostConfig.id === hostId) { diff --git a/src/ui/Desktop/User/PasswordReset.tsx b/src/ui/Desktop/User/PasswordReset.tsx index fb6094c9..bd116683 100644 --- a/src/ui/Desktop/User/PasswordReset.tsx +++ b/src/ui/Desktop/User/PasswordReset.tsx @@ -49,10 +49,14 @@ export function PasswordReset({ userInfo }: PasswordResetProps) { await initiatePasswordReset(userInfo.username); setResetStep("verify"); setError(null); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; setError( - err?.response?.data?.error || - err?.message || + error?.response?.data?.error || + error?.message || t("common.failedToInitiatePasswordReset"), ); } finally { @@ -80,9 +84,10 @@ export function PasswordReset({ userInfo }: PasswordResetProps) { setTempToken(response.tempToken); setResetStep("newPassword"); setError(null); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; setError( - err?.response?.data?.error || t("common.failedToVerifyResetCode"), + error?.response?.data?.error || t("common.failedToVerifyResetCode"), ); } finally { setResetLoading(false); @@ -110,9 +115,11 @@ export function PasswordReset({ userInfo }: PasswordResetProps) { toast.success(t("common.passwordResetSuccess")); resetPasswordState(); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; setError( - err?.response?.data?.error || t("common.failedToCompletePasswordReset"), + error?.response?.data?.error || + t("common.failedToCompletePasswordReset"), ); } finally { setResetLoading(false); diff --git a/src/ui/Desktop/User/TOTPSetup.tsx b/src/ui/Desktop/User/TOTPSetup.tsx index 8545781f..3a65efe2 100644 --- a/src/ui/Desktop/User/TOTPSetup.tsx +++ b/src/ui/Desktop/User/TOTPSetup.tsx @@ -66,8 +66,9 @@ export function TOTPSetup({ setSecret(response.secret); setSetupStep("qr"); setIsSettingUp(true); - } catch (err: any) { - setError(err?.response?.data?.error || "Failed to start TOTP setup"); + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; + setError(error?.response?.data?.error || "Failed to start TOTP setup"); } finally { setLoading(false); } @@ -86,8 +87,9 @@ export function TOTPSetup({ setBackupCodes(response.backup_codes); setSetupStep("backup"); toast.success(t("auth.twoFactorEnabledSuccess")); - } catch (err: any) { - setError(err?.response?.data?.error || "Invalid verification code"); + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; + setError(error?.response?.data?.error || "Invalid verification code"); } finally { setLoading(false); } @@ -105,8 +107,9 @@ export function TOTPSetup({ setDisableCode(""); onStatusChange?.(false); toast.success(t("auth.twoFactorDisabled")); - } catch (err: any) { - setError(err?.response?.data?.error || "Failed to disable TOTP"); + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; + setError(error?.response?.data?.error || "Failed to disable TOTP"); } finally { setLoading(false); } @@ -122,8 +125,11 @@ export function TOTPSetup({ ); setBackupCodes(response.backup_codes); toast.success(t("auth.newBackupCodesGenerated")); - } catch (err: any) { - setError(err?.response?.data?.error || "Failed to generate backup codes"); + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; + setError( + error?.response?.data?.error || "Failed to generate backup codes", + ); } finally { setLoading(false); } diff --git a/src/ui/Desktop/User/UserProfile.tsx b/src/ui/Desktop/User/UserProfile.tsx index 45d3693e..5004ba34 100644 --- a/src/ui/Desktop/User/UserProfile.tsx +++ b/src/ui/Desktop/User/UserProfile.tsx @@ -62,8 +62,9 @@ export function UserProfile({ isTopbarOpen = true }: UserProfileProps) { is_oidc: info.is_oidc, totp_enabled: info.totp_enabled || false, }); - } catch (err: any) { - setError(err?.response?.data?.error || t("errors.loadFailed")); + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; + setError(error?.response?.data?.error || t("errors.loadFailed")); } finally { setLoading(false); } diff --git a/src/ui/Mobile/Apps/Navigation/Hosts/FolderCard.tsx b/src/ui/Mobile/Apps/Navigation/Hosts/FolderCard.tsx index ffbbfab5..02e02338 100644 --- a/src/ui/Mobile/Apps/Navigation/Hosts/FolderCard.tsx +++ b/src/ui/Mobile/Apps/Navigation/Hosts/FolderCard.tsx @@ -23,7 +23,14 @@ interface SSHHost { enableTunnel: boolean; enableFileManager: boolean; defaultPath: string; - tunnelConnections: any[]; + tunnelConnections: Array<{ + sourcePort: number; + endpointPort: number; + endpointHost: string; + maxRetries: number; + retryInterval: number; + autoStart: boolean; + }>; createdAt: string; updatedAt: string; } diff --git a/src/ui/Mobile/Apps/Navigation/Hosts/Host.tsx b/src/ui/Mobile/Apps/Navigation/Hosts/Host.tsx index 74c730de..2110c6e2 100644 --- a/src/ui/Mobile/Apps/Navigation/Hosts/Host.tsx +++ b/src/ui/Mobile/Apps/Navigation/Hosts/Host.tsx @@ -20,7 +20,6 @@ export function Host({ host, onHostConnect }: HostProps): React.ReactElement { : `${host.username}@${host.ip}:${host.port}`; useEffect(() => { - let intervalId: number | undefined; let cancelled = false; const fetchStatus = async () => { @@ -29,13 +28,14 @@ export function Host({ host, onHostConnect }: HostProps): React.ReactElement { if (!cancelled) { setServerStatus(res?.status === "online" ? "online" : "offline"); } - } catch (error: any) { + } catch (error: unknown) { if (!cancelled) { - if (error?.response?.status === 503) { + const err = error as { response?: { status?: number } }; + if (err?.response?.status === 503) { setServerStatus("offline"); - } else if (error?.response?.status === 504) { + } else if (err?.response?.status === 504) { setServerStatus("degraded"); - } else if (error?.response?.status === 404) { + } else if (err?.response?.status === 404) { setServerStatus("offline"); } else { setServerStatus("offline"); @@ -46,7 +46,7 @@ export function Host({ host, onHostConnect }: HostProps): React.ReactElement { fetchStatus(); - intervalId = window.setInterval(fetchStatus, 30000); + const intervalId = window.setInterval(fetchStatus, 30000); return () => { cancelled = true; diff --git a/src/ui/Mobile/Apps/Navigation/LeftSidebar.tsx b/src/ui/Mobile/Apps/Navigation/LeftSidebar.tsx index 7c5b0c59..5745d89b 100644 --- a/src/ui/Mobile/Apps/Navigation/LeftSidebar.tsx +++ b/src/ui/Mobile/Apps/Navigation/LeftSidebar.tsx @@ -42,7 +42,14 @@ interface SSHHost { enableTunnel: boolean; enableFileManager: boolean; defaultPath: string; - tunnelConnections: any[]; + tunnelConnections: Array<{ + sourcePort: number; + endpointPort: number; + endpointHost: string; + maxRetries: number; + retryInterval: number; + autoStart: boolean; + }>; createdAt: string; updatedAt: string; } diff --git a/src/ui/Mobile/Apps/Navigation/Tabs/TabContext.tsx b/src/ui/Mobile/Apps/Navigation/Tabs/TabContext.tsx index 8f117f93..45186e86 100644 --- a/src/ui/Mobile/Apps/Navigation/Tabs/TabContext.tsx +++ b/src/ui/Mobile/Apps/Navigation/Tabs/TabContext.tsx @@ -56,7 +56,7 @@ export function TabProvider({ children }: TabProviderProps) { ...tabData, id, title: computeUniqueTitle(tabData.title), - terminalRef: React.createRef(), + terminalRef: React.createRef<{ disconnect?: () => void }>(), }; setTabs((prev) => [...prev, newTab]); setCurrentTab(id); diff --git a/src/ui/Mobile/Homepage/HomepageAuth.tsx b/src/ui/Mobile/Homepage/HomepageAuth.tsx index 81ad51e2..9cd6c5b0 100644 --- a/src/ui/Mobile/Homepage/HomepageAuth.tsx +++ b/src/ui/Mobile/Homepage/HomepageAuth.tsx @@ -162,7 +162,7 @@ export function HomepageAuth({ } try { - let res, meRes; + let res; if (tab === "login") { res = await loginUser(localUsername, password); } else { @@ -192,7 +192,7 @@ export function HomepageAuth({ throw new Error(t("errors.loginFailed")); } - [meRes] = await Promise.all([getUserInfo()]); + const [meRes] = await Promise.all([getUserInfo()]); setInternalLoggedIn(true); setLoggedIn(true); @@ -215,16 +215,22 @@ export function HomepageAuth({ setTotpRequired(false); setTotpCode(""); setTotpTempToken(""); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; const errorMessage = - err?.response?.data?.error || err?.message || t("errors.unknownError"); + error?.response?.data?.error || + error?.message || + t("errors.unknownError"); toast.error(errorMessage); setInternalLoggedIn(false); setLoggedIn(false); setIsAdmin(false); setUsername(null); setUserId(null); - if (err?.response?.data?.error?.includes("Database")) { + if (error?.response?.data?.error?.includes("Database")) { setDbError(t("errors.databaseConnection")); } else { setDbError(null); @@ -241,10 +247,14 @@ export function HomepageAuth({ await initiatePasswordReset(localUsername); setResetStep("verify"); toast.success(t("messages.resetCodeSent")); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; toast.error( - err?.response?.data?.error || - err?.message || + error?.response?.data?.error || + error?.message || t("errors.failedPasswordReset"), ); } finally { @@ -260,8 +270,9 @@ export function HomepageAuth({ setTempToken(response.tempToken); setResetStep("newPassword"); toast.success(t("messages.codeVerified")); - } catch (err: any) { - toast.error(err?.response?.data?.error || t("errors.failedVerifyCode")); + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; + toast.error(error?.response?.data?.error || t("errors.failedVerifyCode")); } finally { setResetLoading(false); } @@ -298,9 +309,10 @@ export function HomepageAuth({ setTab("login"); resetPasswordState(); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { response?: { data?: { error?: string } } }; toast.error( - err?.response?.data?.error || t("errors.failedCompleteReset"), + error?.response?.data?.error || t("errors.failedCompleteReset"), ); } finally { setResetLoading(false); @@ -364,11 +376,15 @@ export function HomepageAuth({ setTotpCode(""); setTotpTempToken(""); toast.success(t("messages.loginSuccess")); - } catch (err: any) { - const errorCode = err?.response?.data?.code; + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { code?: string; error?: string } }; + }; + const errorCode = error?.response?.data?.code; const errorMessage = - err?.response?.data?.error || - err?.message || + error?.response?.data?.error || + error?.message || t("errors.invalidTotpCode"); if (errorCode === "SESSION_EXPIRED") { @@ -397,10 +413,14 @@ export function HomepageAuth({ } window.location.replace(authUrl); - } catch (err: any) { + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; const errorMessage = - err?.response?.data?.error || - err?.message || + error?.response?.data?.error || + error?.message || t("errors.failedOidcLogin"); toast.error(errorMessage); setOidcLoading(false); diff --git a/src/ui/Mobile/Navigation/Hosts/FolderCard.tsx b/src/ui/Mobile/Navigation/Hosts/FolderCard.tsx index 0862f240..d2cdcb97 100644 --- a/src/ui/Mobile/Navigation/Hosts/FolderCard.tsx +++ b/src/ui/Mobile/Navigation/Hosts/FolderCard.tsx @@ -23,7 +23,14 @@ interface SSHHost { enableTunnel: boolean; enableFileManager: boolean; defaultPath: string; - tunnelConnections: any[]; + tunnelConnections: Array<{ + sourcePort: number; + endpointPort: number; + endpointHost: string; + maxRetries: number; + retryInterval: number; + autoStart: boolean; + }>; createdAt: string; updatedAt: string; } diff --git a/src/ui/Mobile/Navigation/Hosts/Host.tsx b/src/ui/Mobile/Navigation/Hosts/Host.tsx index 82c853de..96ed970b 100644 --- a/src/ui/Mobile/Navigation/Hosts/Host.tsx +++ b/src/ui/Mobile/Navigation/Hosts/Host.tsx @@ -20,7 +20,6 @@ export function Host({ host, onHostConnect }: HostProps): React.ReactElement { : `${host.username}@${host.ip}:${host.port}`; useEffect(() => { - let intervalId: number | undefined; let cancelled = false; const fetchStatus = async () => { @@ -29,13 +28,14 @@ export function Host({ host, onHostConnect }: HostProps): React.ReactElement { if (!cancelled) { setServerStatus(res?.status === "online" ? "online" : "offline"); } - } catch (error: any) { + } catch (error: unknown) { if (!cancelled) { - if (error?.response?.status === 503) { + const err = error as { response?: { status?: number } }; + if (err?.response?.status === 503) { setServerStatus("offline"); - } else if (error?.response?.status === 504) { + } else if (err?.response?.status === 504) { setServerStatus("degraded"); - } else if (error?.response?.status === 404) { + } else if (err?.response?.status === 404) { setServerStatus("offline"); } else { setServerStatus("offline"); @@ -46,7 +46,7 @@ export function Host({ host, onHostConnect }: HostProps): React.ReactElement { fetchStatus(); - intervalId = window.setInterval(fetchStatus, 30000); + const intervalId = window.setInterval(fetchStatus, 30000); return () => { cancelled = true; diff --git a/src/ui/Mobile/Navigation/LeftSidebar.tsx b/src/ui/Mobile/Navigation/LeftSidebar.tsx index c21d6659..1c90d5eb 100644 --- a/src/ui/Mobile/Navigation/LeftSidebar.tsx +++ b/src/ui/Mobile/Navigation/LeftSidebar.tsx @@ -43,7 +43,14 @@ interface SSHHost { enableTunnel: boolean; enableFileManager: boolean; defaultPath: string; - tunnelConnections: any[]; + tunnelConnections: Array<{ + sourcePort: number; + endpointPort: number; + endpointHost: string; + maxRetries: number; + retryInterval: number; + autoStart: boolean; + }>; createdAt: string; updatedAt: string; } diff --git a/src/ui/Mobile/Navigation/Tabs/TabContext.tsx b/src/ui/Mobile/Navigation/Tabs/TabContext.tsx index c12a2bf9..d471c476 100644 --- a/src/ui/Mobile/Navigation/Tabs/TabContext.tsx +++ b/src/ui/Mobile/Navigation/Tabs/TabContext.tsx @@ -56,7 +56,7 @@ export function TabProvider({ children }: TabProviderProps) { ...tabData, id, title: computeUniqueTitle(tabData.title), - terminalRef: React.createRef(), + terminalRef: React.createRef<{ disconnect?: () => void }>(), }; setTabs((prev) => [...prev, newTab]); setCurrentTab(id); diff --git a/src/ui/hooks/useDragToDesktop.ts b/src/ui/hooks/useDragToDesktop.ts index f275291a..932ffe7b 100644 --- a/src/ui/hooks/useDragToDesktop.ts +++ b/src/ui/hooks/useDragToDesktop.ts @@ -114,9 +114,10 @@ export function useDragToDesktop({ sshSessionId }: UseDragToDesktopProps) { }, 10000); return true; - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to drag to desktop:", error); - const errorMessage = error.message || "Drag failed"; + const err = error as { message?: string }; + const errorMessage = err.message || "Drag failed"; setState((prev) => ({ ...prev, @@ -223,9 +224,10 @@ export function useDragToDesktop({ sshSessionId }: UseDragToDesktopProps) { })); }, 15000); return true; - } catch (error: any) { + } catch (error: unknown) { console.error("Failed to batch drag to desktop:", error); - const errorMessage = error.message || "Batch drag failed"; + const err = error as { message?: string }; + const errorMessage = err.message || "Batch drag failed"; setState((prev) => ({ ...prev, diff --git a/src/ui/hooks/useDragToSystemDesktop.ts b/src/ui/hooks/useDragToSystemDesktop.ts index 577d7149..d8e53f67 100644 --- a/src/ui/hooks/useDragToSystemDesktop.ts +++ b/src/ui/hooks/useDragToSystemDesktop.ts @@ -34,7 +34,9 @@ export function useDragToSystemDesktop({ sshSessionId }: UseDragToSystemProps) { options: DragToSystemOptions; } | null>(null); - const saveLastDirectory = async (fileHandle: any) => { + const saveLastDirectory = async (fileHandle: { + getParent?: () => Promise; + }) => { try { if ("indexedDB" in window && fileHandle.getParent) { const dirHandle = await fileHandle.getParent(); @@ -133,10 +135,33 @@ export function useDragToSystemDesktop({ sshSessionId }: UseDragToSystemProps) { const fileName = fileList.length === 1 ? fileList[0].name : `files_${Date.now()}.zip`; - let fileHandle: any = null; + let fileHandle: { + createWritable?: () => Promise<{ + write: (data: Blob) => Promise; + close: () => Promise; + }>; + getParent?: () => Promise; + } | null = null; if (isFileSystemAPISupported()) { try { - fileHandle = await (window as any).showSaveFilePicker({ + fileHandle = await ( + window as Window & { + showSaveFilePicker?: (options: { + suggestedName: string; + startIn: string; + types: Array<{ + description: string; + accept: Record; + }>; + }) => Promise<{ + createWritable?: () => Promise<{ + write: (data: Blob) => Promise; + close: () => Promise; + }>; + getParent?: () => Promise; + }>; + } + ).showSaveFilePicker!({ suggestedName: fileName, startIn: "desktop", types: [ @@ -156,8 +181,9 @@ export function useDragToSystemDesktop({ sshSessionId }: UseDragToSystemProps) { }, ], }); - } catch (error: any) { - if (error.name === "AbortError") { + } catch (error: unknown) { + const err = error as { name?: string }; + if (err.name === "AbortError") { setState((prev) => ({ ...prev, isDownloading: false, @@ -211,8 +237,9 @@ export function useDragToSystemDesktop({ sshSessionId }: UseDragToSystemProps) { }, 1000); return true; - } catch (error: any) { - const errorMessage = error.message || "Save failed"; + } catch (error: unknown) { + const err = error as { message?: string }; + const errorMessage = err.message || "Save failed"; setState((prev) => ({ ...prev,