feat: enhance server stats widgets and fix TypeScript/ESLint errors #394
5
.github/workflows/pr-check.yml
vendored
5
.github/workflows/pr-check.yml
vendored
@@ -16,10 +16,11 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: |
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
|
||||
- name: Run ESLint
|
||||
run: npx eslint .
|
||||
|
||||
561
lint-output-current.txt
Normal file
561
lint-output-current.txt
Normal file
@@ -0,0 +1,561 @@
|
||||
|
||||
> termix@1.7.2 lint
|
||||
> eslint .
|
||||
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\database\routes\users.ts
|
||||
705:8 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\file-manager.ts
|
||||
169:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
435:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
465:13 error Empty block statement no-empty
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\server-stats.ts
|
||||
64:34 error '_reject' is defined but never used @typescript-eslint/no-unused-vars
|
||||
197:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1130:9 error 'now' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\terminal.ts
|
||||
157:5 error 'userPayload' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
350:13 error 'cols' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
350:19 error 'rows' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\tunnel.ts
|
||||
840:34 error 'data' is defined but never used @typescript-eslint/no-unused-vars
|
||||
906:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1068:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1442:21 error 'hasSourcePassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1443:21 error 'hasSourceKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1444:21 error 'hasEndpointPassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1447:21 error 'hasEndpointKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1472:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\starter.ts
|
||||
78:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
131:47 error 'promise' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\auto-ssl-setup.ts
|
||||
104:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\data-crypto.ts
|
||||
100:31 error 'plaintextFields' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
455:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-file-encryption.ts
|
||||
33:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
81:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
166:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
236:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
304:13 error 'currentFingerprint' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-migration.ts
|
||||
247:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\lazy-field-encryption.ts
|
||||
185:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
282:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\logger.ts
|
||||
14:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\simple-db-ops.ts
|
||||
134:5 error '_userId' is defined but never used @typescript-eslint/no-unused-vars
|
||||
136:5 warning Unused eslint-disable directive (no problems were reported from '@typescript-eslint/no-explicit-any')
|
||||
139:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
157:5 error '_tableName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\ssh-key-utils.ts
|
||||
52:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
243:20 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
321:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-crypto.ts
|
||||
198:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
278:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
301:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
420:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
460:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-data-import.ts
|
||||
162:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
216:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
272:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
359:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\theme-provider.tsx
|
||||
66:14 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\badge.tsx
|
||||
46:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\button.tsx
|
||||
62:18 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\form.tsx
|
||||
158:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\password-input.tsx
|
||||
8:11 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\shadcn-io\status\index.tsx
|
||||
21:3 error 'className' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sidebar.tsx
|
||||
12:3 error 'Sheet' is defined but never used @typescript-eslint/no-unused-vars
|
||||
13:3 error 'SheetContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
14:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
29:7 error 'SIDEBAR_WIDTH_MOBILE' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:11 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:28 error 'openMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:40 error 'setOpenMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
724:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sonner.tsx
|
||||
11:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
28:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
32:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
34:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\textarea.tsx
|
||||
5:18 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\version-check-modal.tsx
|
||||
4:10 error 'RefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
4:21 error 'X' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'onDismiss' is defined but never used @typescript-eslint/no-unused-vars
|
||||
20:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:6 warning React Hook useEffect has missing dependencies: 'checkForUpdates' and 'onContinue'. Either include them or remove the dependency array. If 'onContinue' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
50:9 error 'handleVersionDismiss' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\lib\frontend-logger.ts
|
||||
20:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
221:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
247:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
268:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
282:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
301:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\main.tsx
|
||||
12:10 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:10 error Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Admin\AdminSettings.tsx
|
||||
29:3 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
49:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:10 error 'securityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
99:31 error 'setSecurityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
127:6 warning React Hook React.useEffect has missing dependencies: 'fetchUsers' and 't'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
149:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
171:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
281:16 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
295:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
366:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
458:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialEditor.tsx
|
||||
45:10 error 'credentials' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
47:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
98:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
158:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
201:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
224:6 warning React Hook useEffect has a missing dependency: 'editingCredential'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialSelector.tsx
|
||||
38:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
48:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialViewer.tsx
|
||||
67:6 warning React Hook useEffect has missing dependencies: 'fetchCredentialDetails' and 'fetchHostsUsing'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
73:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
82:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
100:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialsManager.tsx
|
||||
15:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetFooter' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
21:3 error 'Select' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:3 error 'SelectContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'SelectItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
24:3 error 'SelectTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:3 error 'SelectValue' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:3 error 'Pin' is defined but never used @typescript-eslint/no-unused-vars
|
||||
78:29 error 'setViewingCredential' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
91:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
102:6 warning React Hook useEffect has a missing dependency: 'fetchCredentials'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
156:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
227:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
288:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
362:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManager.tsx
|
||||
26:3 error 'Eye' is defined but never used @typescript-eslint/no-unused-vars
|
||||
27:3 error 'Settings' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:23 error 'setCurrentHost' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:26 error 'selectFile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:38 error 'selectAll' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
151:11 error 'isDragging' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
208:6 warning React Hook useEffect has a missing dependency: 'initializeSSHConnection'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
386:5 warning React Hook useCallback has a missing dependency: 'handleCloseWithError'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
455:6 warning React Hook useEffect has a missing dependency: 'handleOpenTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
787:49 error 'editMode' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
837:12 error 'handleFileEdit' is defined but never used @typescript-eslint/no-unused-vars
|
||||
841:12 error 'handleFileView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1356:23 error 'index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1665:6 warning React Hook useEffect has a missing dependency: 'loadPinnedFiles'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerContextMenu.tsx
|
||||
16:3 error 'Share' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'ExternalLink' is defined but never used @typescript-eslint/no-unused-vars
|
||||
193:9 error 'hasDirectories' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerGrid.tsx
|
||||
169:3 error 'onFileSelect' is defined but never used @typescript-eslint/no-unused-vars
|
||||
191:3 error 'onSystemDragStart' is defined but never used @typescript-eslint/no-unused-vars
|
||||
371:6 warning React Hook useEffect has missing dependencies: 'historyIndex' and 'navigationHistory'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
431:9 error 'handlePathInputKeyDown' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
483:5 warning React Hook useCallback has an unnecessary dependency: 'dragState.counter'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
620:34 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
674:5 warning React Hook useCallback has an unnecessary dependency: 'onDownload'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
810:6 warning React Hook useEffect has missing dependencies: 'hasClipboard' and 'onStartEdit'. Either include them or remove the dependency array. If 'onStartEdit' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerSidebar.tsx
|
||||
79:3 error 'onLoadDirectory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
107:6 warning React Hook useEffect has a missing dependency: 'loadQuickAccessData'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
113:6 warning React Hook useEffect has a missing dependency: 'loadDirectoryTree'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DiffViewer.tsx
|
||||
64:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
160:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
206:6 warning React Hook useEffect has a missing dependency: 'loadFileContents'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DraggableWindow.tsx
|
||||
3:17 error 'Square' is defined but never used @typescript-eslint/no-unused-vars
|
||||
208:5 warning React Hook useCallback has an unnecessary dependency: 'position'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileViewer.tsx
|
||||
63:8 error 'ReactPlayer' is defined but never used @typescript-eslint/no-unused-vars
|
||||
293:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
314:10 error 'originalContent' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
329:28 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
381:9 warning The 'handleSave' function makes the dependencies of useEffect Hook (at line 411) change on every render. To fix this, wrap the definition of 'handleSave' in its own useCallback() Hook react-hooks/exhaustive-deps
|
||||
979:29 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1100:32 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1384:33 error 'audio' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileWindow.tsx
|
||||
59:53 error 'updateWindow' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
179:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
212:6 warning React Hook useEffect has missing dependencies: 'closeWindow', 'ensureSSHConnection', 'onFileNotFound', 't', and 'windowId'. Either include them or remove the dependency array. If 'onFileNotFound' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
232:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
261:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
338:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\TerminalWindow.tsx
|
||||
41:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
53:9 error 'handleMinimize' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
77:3 error React Hook "React.useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\WindowManager.tsx
|
||||
132:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManager.tsx
|
||||
18:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:62 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:29 error 'updatedHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
38:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerEditor.tsx
|
||||
41:28 error 'WidgetType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
63:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
80:10 error 'hosts' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
83:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
84:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
305:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
390:52 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
423:6 warning React Hook useEffect has missing dependencies: 'editingHost' and 'form'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
443:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
536:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerViewer.tsx
|
||||
83:6 warning React Hook useEffect has a missing dependency: 'fetchHosts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
109:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
125:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
163:47 error 'actualAuthType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
169:13 error '_' is defined but never used @typescript-eslint/no-unused-vars
|
||||
188:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
225:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
254:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
294:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
442:6 warning React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\Server.tsx
|
||||
151:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
168:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
177:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
260:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\DiskWidget.tsx
|
||||
15:39 error 'metricsHistory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\NetworkWidget.tsx
|
||||
14:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\ProcessesWidget.tsx
|
||||
14:33 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:38 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\SystemWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\UptimeWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\SnippetsSidebar.tsx
|
||||
55:6 warning React Hook useEffect has a missing dependency: 'fetchSnippets'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
63:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
97:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
128:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\Terminal.tsx
|
||||
83:12 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
84:12 error 'isAuthenticated' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
227:7 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
230:14 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
493:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
529:37 error 'event' is defined but never used @typescript-eslint/no-unused-vars
|
||||
713:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
753:8 warning React Hook useEffect has missing dependencies: 'connectToHost' and 'hardRefresh'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
772:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
784:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Tunnel\TunnelViewer.tsx
|
||||
6:3 error 'TunnelConnection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
66:40 error '_host' is defined but never used @typescript-eslint/no-unused-vars
|
||||
66:47 error '_index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\DesktopApp.tsx
|
||||
15:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:10 error 'view' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
19:10 error 'mountedViews' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Electron Only\ServerConfig.tsx
|
||||
77:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
120:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\Homepage.tsx
|
||||
7:10 error 'useTranslation' is defined but never used @typescript-eslint/no-unused-vars
|
||||
28:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
29:10 error 'username' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAlertManager.tsx
|
||||
20:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
27:6 warning React Hook useEffect has a missing dependency: 'fetchUserAlerts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
55:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
80:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
54:3 error 'dbError' is defined but never used @typescript-eslint/no-unused-vars
|
||||
68:10 error 'visibility' is assigned a value but only used as a type @typescript-eslint/no-unused-vars
|
||||
74:9 error 'toggleVisibility' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
78:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
163:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
213:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
236:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
262:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
281:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
319:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
385:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
418:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
431:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
465:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
481:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
518:9 error 'retryDatabaseConnection' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
531:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
545:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HompageUpdateLog.tsx
|
||||
69:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
74:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\AppView.tsx
|
||||
14:3 error 'LucideRefreshCcw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'LucideRefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'RefreshCcwDot' is defined but never used @typescript-eslint/no-unused-vars
|
||||
117:6 warning React Hook useEffect has a missing dependency: 'hideThenFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
117:40 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps
|
||||
121:6 warning React Hook useEffect has a missing dependency: 'scheduleMeasureAndFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
133:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
142:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\Host.tsx
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\LeftSidebar.tsx
|
||||
6:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
91:3 error 'getView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
152:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
215:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
218:6 warning React Hook React.useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
290:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
300:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
323:11 error 'jwt' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Tabs\TabContext.tsx
|
||||
22:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
27:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
101:55 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
143:60 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\TopNavbar.tsx
|
||||
8:3 error 'Accordion' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'AccordionContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'AccordionItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
11:3 error 'AccordionTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\PasswordReset.tsx
|
||||
49:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
52:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
83:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
113:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\TOTPSetup.tsx
|
||||
69:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
89:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
108:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
125:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\UserProfile.tsx
|
||||
11:24 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
42:6 warning React Hook useEffect has missing dependencies: 'fetchUserInfo' and 'fetchVersion'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
48:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
65:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\LeftSidebar.tsx
|
||||
45:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Terminal\Terminal.tsx
|
||||
15:10 error 'toast' is defined but never used @typescript-eslint/no-unused-vars
|
||||
55:12 error 'isConnected' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:12 error 'isConnecting' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
57:12 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:7 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
163:14 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
394:8 warning React Hook useEffect has missing dependencies: 'hardRefresh', 'isAuthenticated', and 'setupWebSocketListeners'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
404:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
413:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
67:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
153:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
197:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
220:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
243:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
246:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
265:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
303:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
369:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
402:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
415:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
449:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
466:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\MobileApp.tsx
|
||||
10:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:37 error 'removeTab' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
23:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
69:6 warning React Hook useEffect has a missing dependency: 'fitCurrentTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
98:6 warning React Hook React.useEffect has missing dependencies: 'fitCurrentTerminal' and 'tabs.length'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
131:23 error 'id' is defined but never used @typescript-eslint/no-unused-vars
|
||||
135:24 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\LeftSidebar.tsx
|
||||
46:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToDesktop.ts
|
||||
120:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
140:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
229:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
254:35 error 'onSuccess' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
277:5 warning React Hook useCallback has unnecessary dependencies: 'sshHost' and 'sshSessionId'. Either exclude them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToSystemDesktop.ts
|
||||
26:3 error 'sshHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:9 error 'getLastSaveDirectory' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
68:48 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
167:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
170:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
245:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
263:5 warning React Hook useCallback has missing dependencies: 'createFileBlob' and 'createZipBlob'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\main-axios.ts
|
||||
7:3 error 'Credential' is defined but never used @typescript-eslint/no-unused-vars
|
||||
8:3 error 'CredentialData' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'HostInfo' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'ApiResponse' is defined but never used @typescript-eslint/no-unused-vars
|
||||
346:3 error 'apiPort' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
994:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1031:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1068:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
✖ 385 problems (326 errors, 59 warnings)
|
||||
0 errors and 1 warning potentially fixable with the `--fix` option.
|
||||
|
||||
561
lint-output-final.txt
Normal file
561
lint-output-final.txt
Normal file
@@ -0,0 +1,561 @@
|
||||
|
||||
> termix@1.7.2 lint
|
||||
> eslint .
|
||||
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\database\routes\users.ts
|
||||
705:8 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\file-manager.ts
|
||||
169:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
435:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
465:13 error Empty block statement no-empty
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\server-stats.ts
|
||||
64:34 error '_reject' is defined but never used @typescript-eslint/no-unused-vars
|
||||
197:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1130:9 error 'now' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\terminal.ts
|
||||
157:5 error 'userPayload' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
350:13 error 'cols' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
350:19 error 'rows' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\tunnel.ts
|
||||
840:34 error 'data' is defined but never used @typescript-eslint/no-unused-vars
|
||||
906:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1068:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1442:21 error 'hasSourcePassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1443:21 error 'hasSourceKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1444:21 error 'hasEndpointPassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1447:21 error 'hasEndpointKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1472:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\starter.ts
|
||||
78:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
131:47 error 'promise' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\auto-ssl-setup.ts
|
||||
104:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\data-crypto.ts
|
||||
100:31 error 'plaintextFields' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
455:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-file-encryption.ts
|
||||
33:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
81:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
166:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
236:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
304:13 error 'currentFingerprint' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-migration.ts
|
||||
247:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\lazy-field-encryption.ts
|
||||
185:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
282:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\logger.ts
|
||||
14:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\simple-db-ops.ts
|
||||
134:5 error '_userId' is defined but never used @typescript-eslint/no-unused-vars
|
||||
136:5 warning Unused eslint-disable directive (no problems were reported from '@typescript-eslint/no-explicit-any')
|
||||
139:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
157:5 error '_tableName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\ssh-key-utils.ts
|
||||
52:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
243:20 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
321:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-crypto.ts
|
||||
198:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
278:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
301:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
420:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
460:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-data-import.ts
|
||||
162:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
216:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
272:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
359:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\theme-provider.tsx
|
||||
66:14 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\badge.tsx
|
||||
46:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\button.tsx
|
||||
62:18 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\form.tsx
|
||||
158:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\password-input.tsx
|
||||
8:11 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\shadcn-io\status\index.tsx
|
||||
21:3 error 'className' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sidebar.tsx
|
||||
12:3 error 'Sheet' is defined but never used @typescript-eslint/no-unused-vars
|
||||
13:3 error 'SheetContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
14:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
29:7 error 'SIDEBAR_WIDTH_MOBILE' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:11 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:28 error 'openMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:40 error 'setOpenMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
724:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sonner.tsx
|
||||
11:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
28:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
32:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
34:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\textarea.tsx
|
||||
5:18 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\version-check-modal.tsx
|
||||
4:10 error 'RefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
4:21 error 'X' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'onDismiss' is defined but never used @typescript-eslint/no-unused-vars
|
||||
20:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:6 warning React Hook useEffect has missing dependencies: 'checkForUpdates' and 'onContinue'. Either include them or remove the dependency array. If 'onContinue' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
50:9 error 'handleVersionDismiss' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\lib\frontend-logger.ts
|
||||
20:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
221:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
247:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
268:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
282:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
301:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\main.tsx
|
||||
12:10 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:10 error Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Admin\AdminSettings.tsx
|
||||
29:3 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
49:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:10 error 'securityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
99:31 error 'setSecurityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
127:6 warning React Hook React.useEffect has missing dependencies: 'fetchUsers' and 't'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
149:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
171:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
281:16 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
295:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
366:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
458:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialEditor.tsx
|
||||
45:10 error 'credentials' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
47:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
98:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
158:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
201:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
224:6 warning React Hook useEffect has a missing dependency: 'editingCredential'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialSelector.tsx
|
||||
38:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
48:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialViewer.tsx
|
||||
67:6 warning React Hook useEffect has missing dependencies: 'fetchCredentialDetails' and 'fetchHostsUsing'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
73:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
82:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
100:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialsManager.tsx
|
||||
15:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetFooter' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
21:3 error 'Select' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:3 error 'SelectContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'SelectItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
24:3 error 'SelectTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:3 error 'SelectValue' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:3 error 'Pin' is defined but never used @typescript-eslint/no-unused-vars
|
||||
78:29 error 'setViewingCredential' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
91:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
102:6 warning React Hook useEffect has a missing dependency: 'fetchCredentials'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
156:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
227:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
288:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
362:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManager.tsx
|
||||
26:3 error 'Eye' is defined but never used @typescript-eslint/no-unused-vars
|
||||
27:3 error 'Settings' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:23 error 'setCurrentHost' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:26 error 'selectFile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:38 error 'selectAll' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
151:11 error 'isDragging' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
208:6 warning React Hook useEffect has a missing dependency: 'initializeSSHConnection'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
386:5 warning React Hook useCallback has a missing dependency: 'handleCloseWithError'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
455:6 warning React Hook useEffect has a missing dependency: 'handleOpenTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
787:49 error 'editMode' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
837:12 error 'handleFileEdit' is defined but never used @typescript-eslint/no-unused-vars
|
||||
841:12 error 'handleFileView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1356:23 error 'index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1665:6 warning React Hook useEffect has a missing dependency: 'loadPinnedFiles'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerContextMenu.tsx
|
||||
16:3 error 'Share' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'ExternalLink' is defined but never used @typescript-eslint/no-unused-vars
|
||||
193:9 error 'hasDirectories' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerGrid.tsx
|
||||
169:3 error 'onFileSelect' is defined but never used @typescript-eslint/no-unused-vars
|
||||
191:3 error 'onSystemDragStart' is defined but never used @typescript-eslint/no-unused-vars
|
||||
371:6 warning React Hook useEffect has missing dependencies: 'historyIndex' and 'navigationHistory'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
431:9 error 'handlePathInputKeyDown' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
483:5 warning React Hook useCallback has an unnecessary dependency: 'dragState.counter'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
620:34 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
674:5 warning React Hook useCallback has an unnecessary dependency: 'onDownload'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
810:6 warning React Hook useEffect has missing dependencies: 'hasClipboard' and 'onStartEdit'. Either include them or remove the dependency array. If 'onStartEdit' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerSidebar.tsx
|
||||
79:3 error 'onLoadDirectory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
107:6 warning React Hook useEffect has a missing dependency: 'loadQuickAccessData'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
113:6 warning React Hook useEffect has a missing dependency: 'loadDirectoryTree'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DiffViewer.tsx
|
||||
64:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
160:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
206:6 warning React Hook useEffect has a missing dependency: 'loadFileContents'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DraggableWindow.tsx
|
||||
3:17 error 'Square' is defined but never used @typescript-eslint/no-unused-vars
|
||||
208:5 warning React Hook useCallback has an unnecessary dependency: 'position'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileViewer.tsx
|
||||
63:8 error 'ReactPlayer' is defined but never used @typescript-eslint/no-unused-vars
|
||||
293:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
314:10 error 'originalContent' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
329:28 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
381:9 warning The 'handleSave' function makes the dependencies of useEffect Hook (at line 411) change on every render. To fix this, wrap the definition of 'handleSave' in its own useCallback() Hook react-hooks/exhaustive-deps
|
||||
979:29 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1100:32 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1384:33 error 'audio' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileWindow.tsx
|
||||
59:53 error 'updateWindow' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
179:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
212:6 warning React Hook useEffect has missing dependencies: 'closeWindow', 'ensureSSHConnection', 'onFileNotFound', 't', and 'windowId'. Either include them or remove the dependency array. If 'onFileNotFound' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
232:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
261:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
338:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\TerminalWindow.tsx
|
||||
41:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
53:9 error 'handleMinimize' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
77:3 error React Hook "React.useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\WindowManager.tsx
|
||||
132:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManager.tsx
|
||||
18:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:62 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:29 error 'updatedHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
38:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerEditor.tsx
|
||||
41:28 error 'WidgetType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
63:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
80:10 error 'hosts' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
83:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
84:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
305:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
390:52 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
423:6 warning React Hook useEffect has missing dependencies: 'editingHost' and 'form'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
443:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
536:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerViewer.tsx
|
||||
83:6 warning React Hook useEffect has a missing dependency: 'fetchHosts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
109:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
125:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
163:47 error 'actualAuthType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
169:13 error '_' is defined but never used @typescript-eslint/no-unused-vars
|
||||
188:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
225:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
254:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
294:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
442:6 warning React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\Server.tsx
|
||||
151:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
168:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
177:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
260:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\DiskWidget.tsx
|
||||
15:39 error 'metricsHistory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\NetworkWidget.tsx
|
||||
14:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\ProcessesWidget.tsx
|
||||
14:33 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:38 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\SystemWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\UptimeWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\SnippetsSidebar.tsx
|
||||
55:6 warning React Hook useEffect has a missing dependency: 'fetchSnippets'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
63:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
97:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
128:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\Terminal.tsx
|
||||
83:12 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
84:12 error 'isAuthenticated' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
227:7 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
230:14 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
493:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
529:37 error 'event' is defined but never used @typescript-eslint/no-unused-vars
|
||||
713:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
753:8 warning React Hook useEffect has missing dependencies: 'connectToHost' and 'hardRefresh'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
772:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
784:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Tunnel\TunnelViewer.tsx
|
||||
6:3 error 'TunnelConnection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
66:40 error '_host' is defined but never used @typescript-eslint/no-unused-vars
|
||||
66:47 error '_index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\DesktopApp.tsx
|
||||
15:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:10 error 'view' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
19:10 error 'mountedViews' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Electron Only\ServerConfig.tsx
|
||||
77:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
120:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\Homepage.tsx
|
||||
7:10 error 'useTranslation' is defined but never used @typescript-eslint/no-unused-vars
|
||||
28:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
29:10 error 'username' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAlertManager.tsx
|
||||
20:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
27:6 warning React Hook useEffect has a missing dependency: 'fetchUserAlerts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
55:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
80:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
54:3 error 'dbError' is defined but never used @typescript-eslint/no-unused-vars
|
||||
68:10 error 'visibility' is assigned a value but only used as a type @typescript-eslint/no-unused-vars
|
||||
74:9 error 'toggleVisibility' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
78:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
163:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
213:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
236:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
262:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
281:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
319:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
385:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
418:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
431:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
465:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
481:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
518:9 error 'retryDatabaseConnection' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
531:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
545:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HompageUpdateLog.tsx
|
||||
69:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
74:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\AppView.tsx
|
||||
14:3 error 'LucideRefreshCcw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'LucideRefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'RefreshCcwDot' is defined but never used @typescript-eslint/no-unused-vars
|
||||
117:6 warning React Hook useEffect has a missing dependency: 'hideThenFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
117:40 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps
|
||||
121:6 warning React Hook useEffect has a missing dependency: 'scheduleMeasureAndFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
133:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
142:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\Host.tsx
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\LeftSidebar.tsx
|
||||
6:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
91:3 error 'getView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
152:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
215:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
218:6 warning React Hook React.useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
290:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
300:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
323:11 error 'jwt' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Tabs\TabContext.tsx
|
||||
22:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
27:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
101:55 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
143:60 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\TopNavbar.tsx
|
||||
8:3 error 'Accordion' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'AccordionContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'AccordionItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
11:3 error 'AccordionTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\PasswordReset.tsx
|
||||
49:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
52:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
83:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
113:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\TOTPSetup.tsx
|
||||
69:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
89:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
108:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
125:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\UserProfile.tsx
|
||||
11:24 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
42:6 warning React Hook useEffect has missing dependencies: 'fetchUserInfo' and 'fetchVersion'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
48:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
65:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\LeftSidebar.tsx
|
||||
45:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Terminal\Terminal.tsx
|
||||
15:10 error 'toast' is defined but never used @typescript-eslint/no-unused-vars
|
||||
55:12 error 'isConnected' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:12 error 'isConnecting' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
57:12 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:7 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
163:14 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
394:8 warning React Hook useEffect has missing dependencies: 'hardRefresh', 'isAuthenticated', and 'setupWebSocketListeners'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
404:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
413:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
67:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
153:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
197:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
220:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
243:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
246:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
265:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
303:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
369:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
402:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
415:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
449:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
466:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\MobileApp.tsx
|
||||
10:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:37 error 'removeTab' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
23:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
69:6 warning React Hook useEffect has a missing dependency: 'fitCurrentTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
98:6 warning React Hook React.useEffect has missing dependencies: 'fitCurrentTerminal' and 'tabs.length'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
131:23 error 'id' is defined but never used @typescript-eslint/no-unused-vars
|
||||
135:24 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\LeftSidebar.tsx
|
||||
46:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToDesktop.ts
|
||||
120:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
140:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
229:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
254:35 error 'onSuccess' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
277:5 warning React Hook useCallback has unnecessary dependencies: 'sshHost' and 'sshSessionId'. Either exclude them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToSystemDesktop.ts
|
||||
26:3 error 'sshHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:9 error 'getLastSaveDirectory' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
68:48 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
167:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
170:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
245:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
263:5 warning React Hook useCallback has missing dependencies: 'createFileBlob' and 'createZipBlob'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\main-axios.ts
|
||||
7:3 error 'Credential' is defined but never used @typescript-eslint/no-unused-vars
|
||||
8:3 error 'CredentialData' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'HostInfo' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'ApiResponse' is defined but never used @typescript-eslint/no-unused-vars
|
||||
346:3 error 'apiPort' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
994:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1031:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1068:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
✖ 385 problems (326 errors, 59 warnings)
|
||||
0 errors and 1 warning potentially fixable with the `--fix` option.
|
||||
|
||||
593
lint-output-new.txt
Normal file
593
lint-output-new.txt
Normal file
@@ -0,0 +1,593 @@
|
||||
|
||||
> termix@1.7.2 lint
|
||||
> eslint .
|
||||
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\database\routes\users.ts
|
||||
688:74 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\file-manager.ts
|
||||
169:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
435:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
465:13 error Empty block statement no-empty
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\server-stats.ts
|
||||
64:34 error '_reject' is defined but never used @typescript-eslint/no-unused-vars
|
||||
197:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1122:9 error 'now' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\terminal.ts
|
||||
82:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
116:5 error 'userPayload' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
190:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
316:13 error 'cols' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
316:19 error 'rows' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
633:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
779:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
788:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
800:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\tunnel.ts
|
||||
830:34 error 'data' is defined but never used @typescript-eslint/no-unused-vars
|
||||
896:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1053:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1427:21 error 'hasSourcePassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1428:21 error 'hasSourceKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1429:21 error 'hasEndpointPassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1432:21 error 'hasEndpointKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1457:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\starter.ts
|
||||
78:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
131:47 error 'promise' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\auth-manager.ts
|
||||
166:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
197:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
198:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
205:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
218:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
260:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\auto-ssl-setup.ts
|
||||
104:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\data-crypto.ts
|
||||
18:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
41:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
76:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
87:31 error 'plaintextFields' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
223:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
391:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
400:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
438:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-file-encryption.ts
|
||||
33:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
81:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
166:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
236:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
304:13 error 'currentFingerprint' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-migration.ts
|
||||
247:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\lazy-field-encryption.ts
|
||||
185:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
282:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\logger.ts
|
||||
14:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\simple-db-ops.ts
|
||||
134:5 error '_userId' is defined but never used @typescript-eslint/no-unused-vars
|
||||
154:5 error '_tableName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\ssh-key-utils.ts
|
||||
52:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
243:20 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
321:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-crypto.ts
|
||||
198:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
278:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
301:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
420:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
460:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-data-import.ts
|
||||
162:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
216:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
272:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
359:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\theme-provider.tsx
|
||||
66:14 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\badge.tsx
|
||||
46:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\button.tsx
|
||||
62:18 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\form.tsx
|
||||
158:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\password-input.tsx
|
||||
8:11 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\shadcn-io\status\index.tsx
|
||||
21:3 error 'className' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sidebar.tsx
|
||||
12:3 error 'Sheet' is defined but never used @typescript-eslint/no-unused-vars
|
||||
13:3 error 'SheetContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
14:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
29:7 error 'SIDEBAR_WIDTH_MOBILE' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:11 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:28 error 'openMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:40 error 'setOpenMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
724:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sonner.tsx
|
||||
11:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
28:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
32:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
34:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\textarea.tsx
|
||||
5:18 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\version-check-modal.tsx
|
||||
4:10 error 'RefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
4:21 error 'X' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'onDismiss' is defined but never used @typescript-eslint/no-unused-vars
|
||||
20:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:6 warning React Hook useEffect has missing dependencies: 'checkForUpdates' and 'onContinue'. Either include them or remove the dependency array. If 'onContinue' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
50:9 error 'handleVersionDismiss' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\lib\frontend-logger.ts
|
||||
20:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
221:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
247:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
268:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
282:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
301:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\main.tsx
|
||||
12:10 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:10 error Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Admin\AdminSettings.tsx
|
||||
29:3 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
49:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:10 error 'securityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
99:31 error 'setSecurityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
126:6 warning React Hook React.useEffect has missing dependencies: 'fetchUsers' and 't'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
147:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
168:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
275:16 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
289:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
360:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
452:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialEditor.tsx
|
||||
45:10 error 'credentials' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
47:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
98:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
158:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
201:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
224:6 warning React Hook useEffect has a missing dependency: 'editingCredential'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialSelector.tsx
|
||||
38:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
48:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialViewer.tsx
|
||||
67:6 warning React Hook useEffect has missing dependencies: 'fetchCredentialDetails' and 'fetchHostsUsing'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
73:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
82:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
100:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialsManager.tsx
|
||||
15:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetFooter' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
21:3 error 'Select' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:3 error 'SelectContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'SelectItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
24:3 error 'SelectTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:3 error 'SelectValue' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:3 error 'Pin' is defined but never used @typescript-eslint/no-unused-vars
|
||||
78:29 error 'setViewingCredential' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
91:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
102:6 warning React Hook useEffect has a missing dependency: 'fetchCredentials'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
156:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
227:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
288:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
362:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManager.tsx
|
||||
26:3 error 'Eye' is defined but never used @typescript-eslint/no-unused-vars
|
||||
27:3 error 'Settings' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:23 error 'setCurrentHost' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:26 error 'selectFile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:38 error 'selectAll' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
151:11 error 'isDragging' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
208:6 warning React Hook useEffect has a missing dependency: 'initializeSSHConnection'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
386:5 warning React Hook useCallback has a missing dependency: 'handleCloseWithError'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
455:6 warning React Hook useEffect has a missing dependency: 'handleOpenTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
787:49 error 'editMode' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
837:12 error 'handleFileEdit' is defined but never used @typescript-eslint/no-unused-vars
|
||||
841:12 error 'handleFileView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1356:23 error 'index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1663:6 warning React Hook useEffect has a missing dependency: 'loadPinnedFiles'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerContextMenu.tsx
|
||||
16:3 error 'Share' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'ExternalLink' is defined but never used @typescript-eslint/no-unused-vars
|
||||
193:9 error 'hasDirectories' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerGrid.tsx
|
||||
169:3 error 'onFileSelect' is defined but never used @typescript-eslint/no-unused-vars
|
||||
191:3 error 'onSystemDragStart' is defined but never used @typescript-eslint/no-unused-vars
|
||||
371:6 warning React Hook useEffect has missing dependencies: 'historyIndex' and 'navigationHistory'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
431:9 error 'handlePathInputKeyDown' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
483:5 warning React Hook useCallback has an unnecessary dependency: 'dragState.counter'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
620:34 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
674:5 warning React Hook useCallback has an unnecessary dependency: 'onDownload'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
810:6 warning React Hook useEffect has missing dependencies: 'hasClipboard' and 'onStartEdit'. Either include them or remove the dependency array. If 'onStartEdit' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerSidebar.tsx
|
||||
50:3 error 'onLoadDirectory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
78:6 warning React Hook useEffect has a missing dependency: 'loadQuickAccessData'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
84:6 warning React Hook useEffect has a missing dependency: 'loadDirectoryTree'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
91:61 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
101:49 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
110:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
235:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
238:54 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
303:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
306:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DiffViewer.tsx
|
||||
64:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
160:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
206:6 warning React Hook useEffect has a missing dependency: 'loadFileContents'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DraggableWindow.tsx
|
||||
3:17 error 'Square' is defined but never used @typescript-eslint/no-unused-vars
|
||||
208:5 warning React Hook useCallback has an unnecessary dependency: 'position'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileViewer.tsx
|
||||
63:8 error 'ReactPlayer' is defined but never used @typescript-eslint/no-unused-vars
|
||||
293:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
314:10 error 'originalContent' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
329:28 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
381:9 warning The 'handleSave' function makes the dependencies of useEffect Hook (at line 411) change on every render. To fix this, wrap the definition of 'handleSave' in its own useCallback() Hook react-hooks/exhaustive-deps
|
||||
979:29 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1100:32 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1384:33 error 'audio' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileWindow.tsx
|
||||
59:53 error 'updateWindow' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
179:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
212:6 warning React Hook useEffect has missing dependencies: 'closeWindow', 'ensureSSHConnection', 'onFileNotFound', 't', and 'windowId'. Either include them or remove the dependency array. If 'onFileNotFound' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
232:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
261:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
338:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\TerminalWindow.tsx
|
||||
41:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
53:9 error 'handleMinimize' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
77:3 error React Hook "React.useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\WindowManager.tsx
|
||||
132:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManager.tsx
|
||||
18:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:62 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:29 error 'updatedHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
38:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerEditor.tsx
|
||||
41:28 error 'WidgetType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
63:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
80:10 error 'hosts' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
83:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
84:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
305:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
390:52 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
423:6 warning React Hook useEffect has missing dependencies: 'editingHost' and 'form'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
443:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
536:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerViewer.tsx
|
||||
83:6 warning React Hook useEffect has a missing dependency: 'fetchHosts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
109:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
125:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
163:47 error 'actualAuthType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
169:13 error '_' is defined but never used @typescript-eslint/no-unused-vars
|
||||
188:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
225:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
254:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
294:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
442:6 warning React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\Server.tsx
|
||||
17:8 error 'StatsConfig' is defined but never used @typescript-eslint/no-unused-vars
|
||||
31:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
47:41 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
128:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
145:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
154:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
166:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
196:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
230:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
239:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
294:35 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\DiskWidget.tsx
|
||||
15:39 error 'metricsHistory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\NetworkWidget.tsx
|
||||
14:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\ProcessesWidget.tsx
|
||||
14:33 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:38 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\SystemWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\UptimeWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\SnippetsSidebar.tsx
|
||||
55:6 warning React Hook useEffect has a missing dependency: 'fetchSnippets'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
63:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
97:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
128:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\Terminal.tsx
|
||||
79:10 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
80:10 error 'isAuthenticated' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
216:5 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
219:12 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
477:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
513:35 error 'event' is defined but never used @typescript-eslint/no-unused-vars
|
||||
697:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
736:6 warning React Hook useEffect has missing dependencies: 'connectToHost' and 'hardRefresh'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
755:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
767:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Tunnel\TunnelViewer.tsx
|
||||
6:3 error 'TunnelConnection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
66:40 error '_host' is defined but never used @typescript-eslint/no-unused-vars
|
||||
66:47 error '_index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\DesktopApp.tsx
|
||||
15:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:10 error 'view' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
19:10 error 'mountedViews' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Electron Only\ServerConfig.tsx
|
||||
77:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
120:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\Homepage.tsx
|
||||
7:10 error 'useTranslation' is defined but never used @typescript-eslint/no-unused-vars
|
||||
28:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
29:10 error 'username' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAlertManager.tsx
|
||||
20:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
27:6 warning React Hook useEffect has a missing dependency: 'fetchUserAlerts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
55:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
80:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
54:3 error 'dbError' is defined but never used @typescript-eslint/no-unused-vars
|
||||
68:10 error 'visibility' is assigned a value but only used as a type @typescript-eslint/no-unused-vars
|
||||
74:9 error 'toggleVisibility' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
78:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
163:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
213:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
236:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
262:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
281:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
319:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
385:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
418:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
431:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
465:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
481:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
518:9 error 'retryDatabaseConnection' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
531:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
545:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HompageUpdateLog.tsx
|
||||
69:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
74:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\AppView.tsx
|
||||
14:3 error 'LucideRefreshCcw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'LucideRefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'RefreshCcwDot' is defined but never used @typescript-eslint/no-unused-vars
|
||||
117:6 warning React Hook useEffect has a missing dependency: 'hideThenFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
117:40 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps
|
||||
121:6 warning React Hook useEffect has a missing dependency: 'scheduleMeasureAndFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
133:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
142:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\Host.tsx
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\LeftSidebar.tsx
|
||||
6:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
91:3 error 'getView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
152:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
215:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
218:6 warning React Hook React.useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
290:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
300:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
323:11 error 'jwt' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Tabs\TabContext.tsx
|
||||
22:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
27:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
101:55 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
143:60 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\TopNavbar.tsx
|
||||
8:3 error 'Accordion' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'AccordionContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'AccordionItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
11:3 error 'AccordionTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\PasswordReset.tsx
|
||||
49:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
52:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
83:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
113:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\TOTPSetup.tsx
|
||||
69:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
89:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
108:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
125:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\UserProfile.tsx
|
||||
11:24 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
42:6 warning React Hook useEffect has missing dependencies: 'fetchUserInfo' and 'fetchVersion'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
48:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
65:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\LeftSidebar.tsx
|
||||
45:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Terminal\Terminal.tsx
|
||||
15:10 error 'toast' is defined but never used @typescript-eslint/no-unused-vars
|
||||
57:10 error 'isConnected' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
58:10 error 'isConnecting' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
59:10 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
155:5 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
158:12 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
384:6 warning React Hook useEffect has missing dependencies: 'hardRefresh', 'isAuthenticated', and 'setupWebSocketListeners'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
394:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
403:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
67:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
153:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
197:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
220:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
243:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
246:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
265:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
303:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
369:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
402:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
415:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
449:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
466:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\MobileApp.tsx
|
||||
10:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:37 error 'removeTab' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
23:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
69:6 warning React Hook useEffect has a missing dependency: 'fitCurrentTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
98:6 warning React Hook React.useEffect has missing dependencies: 'fitCurrentTerminal' and 'tabs.length'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
131:23 error 'id' is defined but never used @typescript-eslint/no-unused-vars
|
||||
135:24 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\LeftSidebar.tsx
|
||||
46:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToDesktop.ts
|
||||
120:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
140:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
229:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
254:35 error 'onSuccess' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
277:5 warning React Hook useCallback has unnecessary dependencies: 'sshHost' and 'sshSessionId'. Either exclude them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToSystemDesktop.ts
|
||||
26:3 error 'sshHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:9 error 'getLastSaveDirectory' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
68:48 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
167:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
170:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
245:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
263:5 warning React Hook useCallback has missing dependencies: 'createFileBlob' and 'createZipBlob'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\main-axios.ts
|
||||
7:3 error 'Credential' is defined but never used @typescript-eslint/no-unused-vars
|
||||
8:3 error 'CredentialData' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'HostInfo' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'ApiResponse' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:3 error 'apiPort' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
925:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
962:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
999:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
✖ 416 problems (358 errors, 58 warnings)
|
||||
|
||||
631
lint-output.txt
Normal file
631
lint-output.txt
Normal file
@@ -0,0 +1,631 @@
|
||||
|
||||
> termix@1.7.2 lint
|
||||
> eslint .
|
||||
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\database\routes\users.ts
|
||||
688:74 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\file-manager.ts
|
||||
169:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
435:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
465:13 error Empty block statement no-empty
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\server-stats.ts
|
||||
64:34 error '_reject' is defined but never used @typescript-eslint/no-unused-vars
|
||||
197:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1122:9 error 'now' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\terminal.ts
|
||||
82:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
116:5 error 'userPayload' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
190:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
316:13 error 'cols' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
316:19 error 'rows' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
633:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
779:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
788:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
800:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\tunnel.ts
|
||||
830:34 error 'data' is defined but never used @typescript-eslint/no-unused-vars
|
||||
896:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1053:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1427:21 error 'hasSourcePassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1428:21 error 'hasSourceKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1429:21 error 'hasEndpointPassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1432:21 error 'hasEndpointKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1457:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\starter.ts
|
||||
78:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
131:47 error 'promise' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\auth-manager.ts
|
||||
166:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
197:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
198:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
205:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
218:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
260:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\auto-ssl-setup.ts
|
||||
104:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\data-crypto.ts
|
||||
18:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
41:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
76:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
87:31 error 'plaintextFields' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
223:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
391:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
400:6 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
438:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-file-encryption.ts
|
||||
33:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
81:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
166:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
236:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
304:13 error 'currentFingerprint' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-migration.ts
|
||||
247:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\lazy-field-encryption.ts
|
||||
185:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
282:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\logger.ts
|
||||
14:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\simple-db-ops.ts
|
||||
134:5 error '_userId' is defined but never used @typescript-eslint/no-unused-vars
|
||||
154:5 error '_tableName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\ssh-key-utils.ts
|
||||
52:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
243:20 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
321:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-crypto.ts
|
||||
198:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
278:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
301:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
420:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
460:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-data-export.ts
|
||||
21:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
22:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
24:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
25:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
26:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
28:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
86:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
188:35 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-data-import.ts
|
||||
162:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
216:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
272:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
359:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\theme-provider.tsx
|
||||
66:14 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\badge.tsx
|
||||
46:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\button.tsx
|
||||
62:18 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\form.tsx
|
||||
158:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\password-input.tsx
|
||||
8:11 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\shadcn-io\status\index.tsx
|
||||
21:3 error 'className' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sidebar.tsx
|
||||
12:3 error 'Sheet' is defined but never used @typescript-eslint/no-unused-vars
|
||||
13:3 error 'SheetContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
14:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
29:7 error 'SIDEBAR_WIDTH_MOBILE' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:11 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:28 error 'openMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:40 error 'setOpenMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
724:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sonner.tsx
|
||||
11:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
28:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
32:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
34:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\textarea.tsx
|
||||
5:18 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\version-check-modal.tsx
|
||||
4:10 error 'RefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
4:21 error 'X' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'onDismiss' is defined but never used @typescript-eslint/no-unused-vars
|
||||
20:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:6 warning React Hook useEffect has missing dependencies: 'checkForUpdates' and 'onContinue'. Either include them or remove the dependency array. If 'onContinue' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
50:9 error 'handleVersionDismiss' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\lib\frontend-logger.ts
|
||||
20:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
221:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
247:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
268:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
282:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
301:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\main.tsx
|
||||
12:10 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:10 error Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Admin\AdminSettings.tsx
|
||||
29:3 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
49:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:10 error 'securityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
99:31 error 'setSecurityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
126:6 warning React Hook React.useEffect has missing dependencies: 'fetchUsers' and 't'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
147:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
168:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
275:16 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
289:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
360:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
452:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialEditor.tsx
|
||||
45:10 error 'credentials' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
47:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
98:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
158:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
201:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
224:6 warning React Hook useEffect has a missing dependency: 'editingCredential'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialSelector.tsx
|
||||
38:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
48:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialViewer.tsx
|
||||
67:6 warning React Hook useEffect has missing dependencies: 'fetchCredentialDetails' and 'fetchHostsUsing'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
73:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
82:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
100:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialsManager.tsx
|
||||
15:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetFooter' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
21:3 error 'Select' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:3 error 'SelectContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'SelectItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
24:3 error 'SelectTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:3 error 'SelectValue' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:3 error 'Pin' is defined but never used @typescript-eslint/no-unused-vars
|
||||
78:29 error 'setViewingCredential' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
91:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
102:6 warning React Hook useEffect has a missing dependency: 'fetchCredentials'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
156:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
227:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
288:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
362:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManager.tsx
|
||||
26:3 error 'Eye' is defined but never used @typescript-eslint/no-unused-vars
|
||||
27:3 error 'Settings' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:23 error 'setCurrentHost' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:26 error 'selectFile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:38 error 'selectAll' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
151:11 error 'isDragging' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
208:6 warning React Hook useEffect has a missing dependency: 'initializeSSHConnection'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
386:5 warning React Hook useCallback has a missing dependency: 'handleCloseWithError'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
455:6 warning React Hook useEffect has a missing dependency: 'handleOpenTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
787:49 error 'editMode' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
837:12 error 'handleFileEdit' is defined but never used @typescript-eslint/no-unused-vars
|
||||
841:12 error 'handleFileView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1356:23 error 'index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1663:6 warning React Hook useEffect has a missing dependency: 'loadPinnedFiles'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerContextMenu.tsx
|
||||
16:3 error 'Share' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'ExternalLink' is defined but never used @typescript-eslint/no-unused-vars
|
||||
193:9 error 'hasDirectories' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerGrid.tsx
|
||||
169:3 error 'onFileSelect' is defined but never used @typescript-eslint/no-unused-vars
|
||||
191:3 error 'onSystemDragStart' is defined but never used @typescript-eslint/no-unused-vars
|
||||
371:6 warning React Hook useEffect has missing dependencies: 'historyIndex' and 'navigationHistory'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
431:9 error 'handlePathInputKeyDown' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
483:5 warning React Hook useCallback has an unnecessary dependency: 'dragState.counter'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
620:34 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
674:5 warning React Hook useCallback has an unnecessary dependency: 'onDownload'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
810:6 warning React Hook useEffect has missing dependencies: 'hasClipboard' and 'onStartEdit'. Either include them or remove the dependency array. If 'onStartEdit' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerSidebar.tsx
|
||||
50:3 error 'onLoadDirectory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
78:6 warning React Hook useEffect has a missing dependency: 'loadQuickAccessData'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
84:6 warning React Hook useEffect has a missing dependency: 'loadDirectoryTree'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
91:61 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
101:49 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
110:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
235:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
238:54 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
303:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
306:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DiffViewer.tsx
|
||||
64:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
160:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
206:6 warning React Hook useEffect has a missing dependency: 'loadFileContents'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DraggableWindow.tsx
|
||||
3:17 error 'Square' is defined but never used @typescript-eslint/no-unused-vars
|
||||
208:5 warning React Hook useCallback has an unnecessary dependency: 'position'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileViewer.tsx
|
||||
63:8 error 'ReactPlayer' is defined but never used @typescript-eslint/no-unused-vars
|
||||
293:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
314:10 error 'originalContent' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
329:28 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
381:9 warning The 'handleSave' function makes the dependencies of useEffect Hook (at line 411) change on every render. To fix this, wrap the definition of 'handleSave' in its own useCallback() Hook react-hooks/exhaustive-deps
|
||||
979:29 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1100:32 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1384:33 error 'audio' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileWindow.tsx
|
||||
59:53 error 'updateWindow' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
179:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
212:6 warning React Hook useEffect has missing dependencies: 'closeWindow', 'ensureSSHConnection', 'onFileNotFound', 't', and 'windowId'. Either include them or remove the dependency array. If 'onFileNotFound' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
232:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
261:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
338:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\TerminalWindow.tsx
|
||||
41:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
53:9 error 'handleMinimize' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
77:3 error React Hook "React.useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\WindowManager.tsx
|
||||
132:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManager.tsx
|
||||
18:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:62 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:29 error 'updatedHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
38:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerEditor.tsx
|
||||
41:28 error 'WidgetType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
63:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
80:10 error 'hosts' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
83:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
84:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
305:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
390:52 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
423:6 warning React Hook useEffect has missing dependencies: 'editingHost' and 'form'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
443:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
536:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerViewer.tsx
|
||||
83:6 warning React Hook useEffect has a missing dependency: 'fetchHosts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
109:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
125:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
163:47 error 'actualAuthType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
169:13 error '_' is defined but never used @typescript-eslint/no-unused-vars
|
||||
188:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
225:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
254:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
294:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
442:6 warning React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\Server.tsx
|
||||
17:8 error 'StatsConfig' is defined but never used @typescript-eslint/no-unused-vars
|
||||
31:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
47:41 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
128:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
145:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
154:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
166:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
196:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
230:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
239:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
294:35 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\DiskWidget.tsx
|
||||
15:39 error 'metricsHistory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\NetworkWidget.tsx
|
||||
14:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\ProcessesWidget.tsx
|
||||
14:33 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:38 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\SystemWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\UptimeWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\SnippetsSidebar.tsx
|
||||
55:6 warning React Hook useEffect has a missing dependency: 'fetchSnippets'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
63:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
97:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
128:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\Terminal.tsx
|
||||
79:10 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
80:10 error 'isAuthenticated' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
216:5 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
219:12 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
477:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
513:35 error 'event' is defined but never used @typescript-eslint/no-unused-vars
|
||||
697:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
736:6 warning React Hook useEffect has missing dependencies: 'connectToHost' and 'hardRefresh'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
755:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
767:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Tunnel\TunnelViewer.tsx
|
||||
6:3 error 'TunnelConnection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
66:40 error '_host' is defined but never used @typescript-eslint/no-unused-vars
|
||||
66:47 error '_index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\DesktopApp.tsx
|
||||
15:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:10 error 'view' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
19:10 error 'mountedViews' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Electron Only\ServerConfig.tsx
|
||||
77:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
120:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\Homepage.tsx
|
||||
7:10 error 'useTranslation' is defined but never used @typescript-eslint/no-unused-vars
|
||||
28:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
29:10 error 'username' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAlertManager.tsx
|
||||
20:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
27:6 warning React Hook useEffect has a missing dependency: 'fetchUserAlerts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
55:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
80:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
54:3 error 'dbError' is defined but never used @typescript-eslint/no-unused-vars
|
||||
68:10 error 'visibility' is assigned a value but only used as a type @typescript-eslint/no-unused-vars
|
||||
74:9 error 'toggleVisibility' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
78:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
163:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
213:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
236:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
262:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
281:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
319:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
385:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
418:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
431:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
465:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
481:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
518:9 error 'retryDatabaseConnection' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
531:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
545:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HompageUpdateLog.tsx
|
||||
69:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
74:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\AppView.tsx
|
||||
14:3 error 'LucideRefreshCcw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'LucideRefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'RefreshCcwDot' is defined but never used @typescript-eslint/no-unused-vars
|
||||
28:75 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
97:6 warning React Hook useEffect has a missing dependency: 'hideThenFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
97:40 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps
|
||||
101:6 warning React Hook useEffect has a missing dependency: 'scheduleMeasureAndFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
113:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
122:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
137:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
253:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
264:10 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
267:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
319:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
407:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\Host.tsx
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\LeftSidebar.tsx
|
||||
6:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
60:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
90:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
91:3 error 'getView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
115:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
121:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
131:44 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
141:51 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
146:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
209:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
209:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
212:6 warning React Hook React.useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
284:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
294:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
317:11 error 'jwt' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
322:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Tabs\TabContext.tsx
|
||||
22:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
27:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
101:55 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
143:60 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\TopNavbar.tsx
|
||||
8:3 error 'Accordion' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'AccordionContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'AccordionItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
11:3 error 'AccordionTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
38:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
195:35 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
209:35 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
218:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
226:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
232:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\PasswordReset.tsx
|
||||
49:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
52:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
83:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
113:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\TOTPSetup.tsx
|
||||
69:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
89:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
108:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
125:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\UserProfile.tsx
|
||||
11:24 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
42:6 warning React Hook useEffect has missing dependencies: 'fetchUserInfo' and 'fetchVersion'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
48:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
65:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\LeftSidebar.tsx
|
||||
45:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Terminal\Terminal.tsx
|
||||
15:10 error 'toast' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
23:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
35:10 error 'isConnected' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
36:10 error 'isConnecting' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
37:10 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
73:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
74:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
133:5 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
136:12 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
278:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
279:24 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
311:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
362:6 warning React Hook useEffect has missing dependencies: 'hardRefresh', 'isAuthenticated', and 'setupWebSocketListeners'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
372:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
381:6 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
67:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
153:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
197:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
220:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
243:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
246:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
265:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
303:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
369:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
402:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
415:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
449:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
466:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\MobileApp.tsx
|
||||
10:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:37 error 'removeTab' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
23:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
69:6 warning React Hook useEffect has a missing dependency: 'fitCurrentTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
98:6 warning React Hook React.useEffect has missing dependencies: 'fitCurrentTerminal' and 'tabs.length'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
131:23 error 'id' is defined but never used @typescript-eslint/no-unused-vars
|
||||
135:24 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\LeftSidebar.tsx
|
||||
46:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToDesktop.ts
|
||||
120:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
140:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
229:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
254:35 error 'onSuccess' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
277:5 warning React Hook useCallback has unnecessary dependencies: 'sshHost' and 'sshSessionId'. Either exclude them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToSystemDesktop.ts
|
||||
26:3 error 'sshHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:9 error 'getLastSaveDirectory' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
68:48 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
167:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
170:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
245:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
263:5 warning React Hook useCallback has missing dependencies: 'createFileBlob' and 'createZipBlob'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\main-axios.ts
|
||||
7:3 error 'Credential' is defined but never used @typescript-eslint/no-unused-vars
|
||||
8:3 error 'CredentialData' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'HostInfo' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'ApiResponse' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:3 error 'apiPort' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
925:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
962:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
999:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
✖ 452 problems (394 errors, 58 warnings)
|
||||
|
||||
534
lint-progress.txt
Normal file
534
lint-progress.txt
Normal file
@@ -0,0 +1,534 @@
|
||||
|
||||
> termix@1.7.2 lint
|
||||
> eslint .
|
||||
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\database\routes\users.ts
|
||||
705:8 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\file-manager.ts
|
||||
169:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
435:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
465:13 error Empty block statement no-empty
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\server-stats.ts
|
||||
64:34 error '_reject' is defined but never used @typescript-eslint/no-unused-vars
|
||||
197:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1130:9 error 'now' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\terminal.ts
|
||||
157:5 error 'userPayload' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
350:13 error 'cols' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
350:19 error 'rows' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\ssh\tunnel.ts
|
||||
840:34 error 'data' is defined but never used @typescript-eslint/no-unused-vars
|
||||
906:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1068:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1442:21 error 'hasSourcePassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1443:21 error 'hasSourceKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1444:21 error 'hasEndpointPassword' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1447:21 error 'hasEndpointKey' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1472:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\starter.ts
|
||||
131:47 error '_promise' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-file-encryption.ts
|
||||
33:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
81:72 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
166:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
236:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
304:13 error 'currentFingerprint' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\database-migration.ts
|
||||
247:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\lazy-field-encryption.ts
|
||||
185:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
282:9 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\logger.ts
|
||||
14:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\backend\utils\user-data-import.ts
|
||||
162:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
216:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
272:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
359:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\theme-provider.tsx
|
||||
66:14 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\badge.tsx
|
||||
46:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\button.tsx
|
||||
62:18 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\form.tsx
|
||||
158:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\password-input.tsx
|
||||
8:11 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\shadcn-io\status\index.tsx
|
||||
21:3 error 'className' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sidebar.tsx
|
||||
12:3 error 'Sheet' is defined but never used @typescript-eslint/no-unused-vars
|
||||
13:3 error 'SheetContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
14:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
29:7 error 'SIDEBAR_WIDTH_MOBILE' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:11 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:28 error 'openMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
164:40 error 'setOpenMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
724:3 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\sonner.tsx
|
||||
11:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
28:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
32:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
34:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\textarea.tsx
|
||||
5:18 error An interface declaring no members is equivalent to its supertype @typescript-eslint/no-empty-object-type
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\components\ui\version-check-modal.tsx
|
||||
4:10 error 'RefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
4:21 error 'X' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'onDismiss' is defined but never used @typescript-eslint/no-unused-vars
|
||||
20:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
30:6 warning React Hook useEffect has missing dependencies: 'checkForUpdates' and 'onContinue'. Either include them or remove the dependency array. If 'onContinue' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
50:9 error 'handleVersionDismiss' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\lib\frontend-logger.ts
|
||||
20:18 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
221:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
247:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
268:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
282:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
301:11 error 'shortUrl' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\main.tsx
|
||||
12:10 error 'isMobile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:10 error Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Admin\AdminSettings.tsx
|
||||
29:3 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
49:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:10 error 'securityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
99:31 error 'setSecurityInitialized' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
127:6 warning React Hook React.useEffect has missing dependencies: 'fetchUsers' and 't'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
149:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
171:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
281:16 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
295:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
366:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
458:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialEditor.tsx
|
||||
45:10 error 'credentials' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
47:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
98:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
158:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
201:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
224:6 warning React Hook useEffect has a missing dependency: 'editingCredential'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialSelector.tsx
|
||||
38:16 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
48:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialViewer.tsx
|
||||
67:6 warning React Hook useEffect has missing dependencies: 'fetchCredentialDetails' and 'fetchHostsUsing'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
73:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
82:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
100:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Credentials\CredentialsManager.tsx
|
||||
15:3 error 'SheetDescription' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:3 error 'SheetFooter' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'SheetHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:3 error 'SheetTitle' is defined but never used @typescript-eslint/no-unused-vars
|
||||
21:3 error 'Select' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:3 error 'SelectContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'SelectItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
24:3 error 'SelectTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:3 error 'SelectValue' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:3 error 'Pin' is defined but never used @typescript-eslint/no-unused-vars
|
||||
78:29 error 'setViewingCredential' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
91:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
102:6 warning React Hook useEffect has a missing dependency: 'fetchCredentials'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
156:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
227:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
288:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
362:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManager.tsx
|
||||
26:3 error 'Eye' is defined but never used @typescript-eslint/no-unused-vars
|
||||
27:3 error 'Settings' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:23 error 'setCurrentHost' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:26 error 'selectFile' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
148:38 error 'selectAll' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
151:11 error 'isDragging' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
208:6 warning React Hook useEffect has a missing dependency: 'initializeSSHConnection'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
386:5 warning React Hook useCallback has a missing dependency: 'handleCloseWithError'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
455:6 warning React Hook useEffect has a missing dependency: 'handleOpenTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
787:49 error 'editMode' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
837:12 error 'handleFileEdit' is defined but never used @typescript-eslint/no-unused-vars
|
||||
841:12 error 'handleFileView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1356:23 error 'index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1665:6 warning React Hook useEffect has a missing dependency: 'loadPinnedFiles'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerContextMenu.tsx
|
||||
16:3 error 'Share' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'ExternalLink' is defined but never used @typescript-eslint/no-unused-vars
|
||||
193:9 error 'hasDirectories' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerGrid.tsx
|
||||
169:3 error 'onFileSelect' is defined but never used @typescript-eslint/no-unused-vars
|
||||
191:3 error 'onSystemDragStart' is defined but never used @typescript-eslint/no-unused-vars
|
||||
371:6 warning React Hook useEffect has missing dependencies: 'historyIndex' and 'navigationHistory'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
431:9 error 'handlePathInputKeyDown' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
483:5 warning React Hook useCallback has an unnecessary dependency: 'dragState.counter'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
620:34 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
674:5 warning React Hook useCallback has an unnecessary dependency: 'onDownload'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
810:6 warning React Hook useEffect has missing dependencies: 'hasClipboard' and 'onStartEdit'. Either include them or remove the dependency array. If 'onStartEdit' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\FileManagerSidebar.tsx
|
||||
79:3 error 'onLoadDirectory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
107:6 warning React Hook useEffect has a missing dependency: 'loadQuickAccessData'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
113:6 warning React Hook useEffect has a missing dependency: 'loadDirectoryTree'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DiffViewer.tsx
|
||||
64:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
99:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
160:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
206:6 warning React Hook useEffect has a missing dependency: 'loadFileContents'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\DraggableWindow.tsx
|
||||
3:17 error 'Square' is defined but never used @typescript-eslint/no-unused-vars
|
||||
208:5 warning React Hook useCallback has an unnecessary dependency: 'position'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileViewer.tsx
|
||||
63:8 error 'ReactPlayer' is defined but never used @typescript-eslint/no-unused-vars
|
||||
293:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
314:10 error 'originalContent' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
329:28 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
381:9 warning The 'handleSave' function makes the dependencies of useEffect Hook (at line 411) change on every render. To fix this, wrap the definition of 'handleSave' in its own useCallback() Hook react-hooks/exhaustive-deps
|
||||
979:29 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1100:32 error 'node' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1384:33 error 'audio' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\FileWindow.tsx
|
||||
59:53 error 'updateWindow' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
179:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
212:6 warning React Hook useEffect has missing dependencies: 'closeWindow', 'ensureSSHConnection', 'onFileNotFound', 't', and 'windowId'. Either include them or remove the dependency array. If 'onFileNotFound' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
232:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
261:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
338:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\TerminalWindow.tsx
|
||||
41:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
53:9 error 'handleMinimize' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
77:3 error React Hook "React.useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\File Manager\components\WindowManager.tsx
|
||||
132:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManager.tsx
|
||||
18:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
25:62 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:29 error 'updatedHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
38:45 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerEditor.tsx
|
||||
41:28 error 'WidgetType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
63:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
80:10 error 'hosts' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
83:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
84:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
305:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
390:52 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
423:6 warning React Hook useEffect has missing dependencies: 'editingHost' and 'form'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
443:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
536:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Host Manager\HostManagerViewer.tsx
|
||||
83:6 warning React Hook useEffect has a missing dependency: 'fetchHosts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
109:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
125:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
163:47 error 'actualAuthType' is defined but never used @typescript-eslint/no-unused-vars
|
||||
169:13 error '_' is defined but never used @typescript-eslint/no-unused-vars
|
||||
188:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
225:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
254:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
294:28 error 'e' is defined but never used @typescript-eslint/no-unused-vars
|
||||
328:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
442:6 warning React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\Server.tsx
|
||||
151:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
168:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
177:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
260:6 warning React Hook React.useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\DiskWidget.tsx
|
||||
15:39 error 'metricsHistory' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\NetworkWidget.tsx
|
||||
14:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
33:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\ProcessesWidget.tsx
|
||||
14:33 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:38 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\SystemWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Server\widgets\UptimeWidget.tsx
|
||||
14:30 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\SnippetsSidebar.tsx
|
||||
55:6 warning React Hook useEffect has a missing dependency: 'fetchSnippets'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
63:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
97:18 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
128:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Terminal\Terminal.tsx
|
||||
83:12 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
84:12 error 'isAuthenticated' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
227:7 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
230:14 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
493:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
529:37 error 'event' is defined but never used @typescript-eslint/no-unused-vars
|
||||
713:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
753:8 warning React Hook useEffect has missing dependencies: 'connectToHost' and 'hardRefresh'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
772:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
784:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Apps\Tunnel\TunnelViewer.tsx
|
||||
6:3 error 'TunnelConnection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
66:40 error '_host' is defined but never used @typescript-eslint/no-unused-vars
|
||||
66:47 error '_index' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\DesktopApp.tsx
|
||||
15:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
18:10 error 'view' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
19:10 error 'mountedViews' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Electron Only\ServerConfig.tsx
|
||||
77:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
120:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\Homepage.tsx
|
||||
7:10 error 'useTranslation' is defined but never used @typescript-eslint/no-unused-vars
|
||||
28:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
29:10 error 'username' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAlertManager.tsx
|
||||
20:10 error 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
27:6 warning React Hook useEffect has a missing dependency: 'fetchUserAlerts'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
55:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
80:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
54:3 error 'dbError' is defined but never used @typescript-eslint/no-unused-vars
|
||||
68:10 error 'visibility' is assigned a value but only used as a type @typescript-eslint/no-unused-vars
|
||||
74:9 error 'toggleVisibility' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
78:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
163:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
213:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
236:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
259:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
262:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
281:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
319:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
385:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
418:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
431:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
465:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
481:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
518:9 error 'retryDatabaseConnection' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
531:14 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
545:18 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Homepage\HompageUpdateLog.tsx
|
||||
69:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
74:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\AppView.tsx
|
||||
14:3 error 'LucideRefreshCcw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
15:3 error 'LucideRefreshCw' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:3 error 'RefreshCcwDot' is defined but never used @typescript-eslint/no-unused-vars
|
||||
117:6 warning React Hook useEffect has a missing dependency: 'hideThenFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
117:40 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps
|
||||
121:6 warning React Hook useEffect has a missing dependency: 'scheduleMeasureAndFit'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
133:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
142:6 warning React Hook useEffect has a missing dependency: 'fitActiveAndNotify'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Hosts\Host.tsx
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\LeftSidebar.tsx
|
||||
6:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
90:3 error 'onSelectView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
91:3 error 'getView' is defined but never used @typescript-eslint/no-unused-vars
|
||||
152:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
215:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
218:6 warning React Hook React.useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
290:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
300:6 warning React Hook React.useMemo has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
323:11 error 'jwt' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\Tabs\TabContext.tsx
|
||||
22:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
27:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
101:55 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
143:60 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\Navigation\TopNavbar.tsx
|
||||
8:3 error 'Accordion' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'AccordionContent' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'AccordionItem' is defined but never used @typescript-eslint/no-unused-vars
|
||||
11:3 error 'AccordionTrigger' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\PasswordReset.tsx
|
||||
49:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
52:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
83:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
113:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\TOTPSetup.tsx
|
||||
69:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
89:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
108:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
125:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Desktop\User\UserProfile.tsx
|
||||
11:24 error 'Key' is defined but never used @typescript-eslint/no-unused-vars
|
||||
42:6 warning React Hook useEffect has missing dependencies: 'fetchUserInfo' and 'fetchVersion'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
48:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
65:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\LeftSidebar.tsx
|
||||
45:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Apps\Terminal\Terminal.tsx
|
||||
15:10 error 'toast' is defined but never used @typescript-eslint/no-unused-vars
|
||||
55:12 error 'isConnected' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
56:12 error 'isConnecting' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
57:12 error 'connectionError' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
160:7 warning React Hook useImperativeHandle has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
163:14 error 'handleWindowResize' is defined but never used @typescript-eslint/no-unused-vars
|
||||
394:8 warning React Hook useEffect has missing dependencies: 'hardRefresh', 'isAuthenticated', and 'setupWebSocketListeners'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
404:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
413:8 warning React Hook useEffect has a missing dependency: 'hardRefresh'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Homepage\HomepageAuth.tsx
|
||||
22:3 error 'setCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
23:3 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
67:10 error 'error' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
153:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
197:8 error 'meRes' is never reassigned. Use 'const' instead prefer-const
|
||||
220:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
243:13 error 'result' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
246:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
265:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
303:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
369:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
402:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
415:11 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
449:17 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
466:6 warning React Hook useEffect has missing dependencies: 'onAuthSuccess', 'setDbError', 'setIsAdmin', 'setLoggedIn', 'setUserId', 'setUsername', and 't'. Either include them or remove the dependency array. If 'setLoggedIn' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\MobileApp.tsx
|
||||
10:23 error 'getCookie' is defined but never used @typescript-eslint/no-unused-vars
|
||||
17:37 error 'removeTab' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
23:10 error 'isAdmin' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
69:6 warning React Hook useEffect has a missing dependency: 'fitCurrentTerminal'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
98:6 warning React Hook React.useEffect has missing dependencies: 'fitCurrentTerminal' and 'tabs.length'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
131:23 error 'id' is defined but never used @typescript-eslint/no-unused-vars
|
||||
135:24 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\FolderCard.tsx
|
||||
26:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Hosts\Host.tsx
|
||||
5:10 error 'Server' is defined but never used @typescript-eslint/no-unused-vars
|
||||
32:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
49:5 error 'intervalId' is never reassigned. Use 'const' instead prefer-const
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\LeftSidebar.tsx
|
||||
46:22 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
78:24 error 'setHostsLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:14 error 'err' is defined but never used @typescript-eslint/no-unused-vars
|
||||
93:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\Mobile\Navigation\Tabs\TabContext.tsx
|
||||
24:17 error Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components
|
||||
37:11 error 't' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
61:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToDesktop.ts
|
||||
120:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
140:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
229:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
249:5 warning React Hook useCallback has an unnecessary dependency: 'sshHost'. Either exclude it or remove the dependency array react-hooks/exhaustive-deps
|
||||
254:35 error 'onSuccess' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
277:5 warning React Hook useCallback has unnecessary dependencies: 'sshHost' and 'sshSessionId'. Either exclude them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\hooks\useDragToSystemDesktop.ts
|
||||
26:3 error 'sshHost' is defined but never used @typescript-eslint/no-unused-vars
|
||||
40:9 error 'getLastSaveDirectory' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
68:48 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
167:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
170:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
190:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
245:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
263:5 warning React Hook useCallback has missing dependencies: 'createFileBlob' and 'createZipBlob'. Either include them or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
C:\Users\29037\WebstormProjects\Termix\src\ui\main-axios.ts
|
||||
7:3 error 'Credential' is defined but never used @typescript-eslint/no-unused-vars
|
||||
8:3 error 'CredentialData' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:3 error 'HostInfo' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:3 error 'ApiResponse' is defined but never used @typescript-eslint/no-unused-vars
|
||||
346:3 error 'apiPort' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
994:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1031:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1068:12 error 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
✖ 369 problems (311 errors, 58 warnings)
|
||||
|
||||
4361
package-lock.json
generated
4361
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -101,6 +101,7 @@
|
||||
"react-simple-keyboard": "^3.8.120",
|
||||
"react-syntax-highlighter": "^15.6.6",
|
||||
"react-xtermjs": "^1.0.10",
|
||||
"recharts": "^3.2.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sonner": "^2.0.7",
|
||||
"speakeasy": "^2.0.0",
|
||||
|
||||
@@ -31,8 +31,13 @@ import {
|
||||
dismissedAlerts,
|
||||
sshCredentialUsage,
|
||||
settings,
|
||||
snippets,
|
||||
} from "./db/schema.js";
|
||||
import type {
|
||||
CacheEntry,
|
||||
GitHubRelease,
|
||||
GitHubAPIResponse,
|
||||
AuthenticatedRequest,
|
||||
} from "../../types/index.js";
|
||||
import { getDb } from "./db/index.js";
|
||||
import Database from "better-sqlite3";
|
||||
|
||||
@@ -107,17 +112,11 @@ const upload = multer({
|
||||
},
|
||||
});
|
||||
|
||||
interface CacheEntry {
|
||||
data: any;
|
||||
timestamp: number;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
class GitHubCache {
|
||||
private cache: Map<string, CacheEntry> = new Map();
|
||||
private readonly CACHE_DURATION = 30 * 60 * 1000;
|
||||
|
||||
set(key: string, data: any): void {
|
||||
set<T>(key: string, data: T): void {
|
||||
const now = Date.now();
|
||||
this.cache.set(key, {
|
||||
data,
|
||||
@@ -126,7 +125,7 @@ class GitHubCache {
|
||||
});
|
||||
}
|
||||
|
||||
get(key: string): any | null {
|
||||
get<T>(key: string): T | null {
|
||||
const entry = this.cache.get(key);
|
||||
if (!entry) {
|
||||
return null;
|
||||
@@ -137,7 +136,7 @@ class GitHubCache {
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry.data;
|
||||
return entry.data as T;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,34 +146,16 @@ const GITHUB_API_BASE = "https://api.github.com";
|
||||
const REPO_OWNER = "LukeGus";
|
||||
const REPO_NAME = "Termix";
|
||||
|
||||
interface GitHubRelease {
|
||||
id: number;
|
||||
tag_name: string;
|
||||
name: string;
|
||||
body: string;
|
||||
published_at: string;
|
||||
html_url: string;
|
||||
assets: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
size: number;
|
||||
download_count: number;
|
||||
browser_download_url: string;
|
||||
}>;
|
||||
prerelease: boolean;
|
||||
draft: boolean;
|
||||
}
|
||||
|
||||
async function fetchGitHubAPI(
|
||||
async function fetchGitHubAPI<T>(
|
||||
endpoint: string,
|
||||
cacheKey: string,
|
||||
): Promise<any> {
|
||||
const cachedData = githubCache.get(cacheKey);
|
||||
if (cachedData) {
|
||||
): Promise<GitHubAPIResponse<T>> {
|
||||
const cachedEntry = githubCache.get<CacheEntry<T>>(cacheKey);
|
||||
if (cachedEntry) {
|
||||
return {
|
||||
data: cachedData,
|
||||
data: cachedEntry.data,
|
||||
cached: true,
|
||||
cache_age: Date.now() - cachedData.timestamp,
|
||||
cache_age: Date.now() - cachedEntry.timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -193,8 +174,13 @@ async function fetchGitHubAPI(
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
githubCache.set(cacheKey, data);
|
||||
const data = (await response.json()) as T;
|
||||
const cacheData: CacheEntry<T> = {
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
expiresAt: Date.now() + 30 * 60 * 1000,
|
||||
};
|
||||
githubCache.set(cacheKey, cacheData);
|
||||
|
||||
return {
|
||||
data: data,
|
||||
@@ -274,7 +260,7 @@ app.get("/version", authenticateJWT, async (req, res) => {
|
||||
|
||||
try {
|
||||
const cacheKey = "latest_release";
|
||||
const releaseData = await fetchGitHubAPI(
|
||||
const releaseData = await fetchGitHubAPI<GitHubRelease>(
|
||||
`/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`,
|
||||
cacheKey,
|
||||
);
|
||||
@@ -325,12 +311,12 @@ app.get("/releases/rss", authenticateJWT, async (req, res) => {
|
||||
);
|
||||
const cacheKey = `releases_rss_${page}_${per_page}`;
|
||||
|
||||
const releasesData = await fetchGitHubAPI(
|
||||
const releasesData = await fetchGitHubAPI<GitHubRelease[]>(
|
||||
`/repos/${REPO_OWNER}/${REPO_NAME}/releases?page=${page}&per_page=${per_page}`,
|
||||
cacheKey,
|
||||
);
|
||||
|
||||
const rssItems = releasesData.data.map((release: GitHubRelease) => ({
|
||||
const rssItems = releasesData.data.map((release) => ({
|
||||
id: release.id,
|
||||
title: release.name || release.tag_name,
|
||||
description: release.body,
|
||||
@@ -459,7 +445,7 @@ app.post("/encryption/regenerate-jwt", requireAdmin, async (req, res) => {
|
||||
|
||||
app.post("/database/export", authenticateJWT, async (req, res) => {
|
||||
try {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { password } = req.body;
|
||||
|
||||
if (!password) {
|
||||
@@ -692,7 +678,7 @@ app.post("/database/export", authenticateJWT, async (req, res) => {
|
||||
decrypted.authType,
|
||||
decrypted.password || null,
|
||||
decrypted.key || null,
|
||||
decrypted.keyPassword || null,
|
||||
decrypted.key_password || null,
|
||||
decrypted.keyType || null,
|
||||
decrypted.autostartPassword || null,
|
||||
decrypted.autostartKey || null,
|
||||
@@ -735,9 +721,9 @@ app.post("/database/export", authenticateJWT, async (req, res) => {
|
||||
decrypted.username,
|
||||
decrypted.password || null,
|
||||
decrypted.key || null,
|
||||
decrypted.privateKey || null,
|
||||
decrypted.publicKey || null,
|
||||
decrypted.keyPassword || null,
|
||||
decrypted.private_key || null,
|
||||
decrypted.public_key || null,
|
||||
decrypted.key_password || null,
|
||||
decrypted.keyType || null,
|
||||
decrypted.detectedKeyType || null,
|
||||
decrypted.usageCount || 0,
|
||||
@@ -913,7 +899,7 @@ app.post(
|
||||
return res.status(400).json({ error: "No file uploaded" });
|
||||
}
|
||||
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { password } = req.body;
|
||||
|
||||
if (!password) {
|
||||
@@ -1321,7 +1307,7 @@ app.post(
|
||||
|
||||
apiLogger.error("SQLite import failed", error, {
|
||||
operation: "sqlite_import_api_failed",
|
||||
userId: (req as any).userId,
|
||||
userId: (req as AuthenticatedRequest).userId,
|
||||
});
|
||||
res.status(500).json({
|
||||
error: "Failed to import SQLite data",
|
||||
@@ -1333,7 +1319,7 @@ app.post(
|
||||
|
||||
app.post("/database/export/preview", authenticateJWT, async (req, res) => {
|
||||
try {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { scope = "user_data", includeCredentials = true } = req.body;
|
||||
|
||||
const exportData = await UserDataExport.exportUserData(userId, {
|
||||
|
||||
@@ -166,6 +166,7 @@ async function initializeCompleteDatabase(): Promise<void> {
|
||||
tunnel_connections TEXT,
|
||||
enable_file_manager INTEGER NOT NULL DEFAULT 1,
|
||||
default_path TEXT,
|
||||
stats_config TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||
@@ -373,6 +374,7 @@ const migrateSchema = () => {
|
||||
addColumnIfNotExists("ssh_data", "autostart_password", "TEXT");
|
||||
addColumnIfNotExists("ssh_data", "autostart_key", "TEXT");
|
||||
addColumnIfNotExists("ssh_data", "autostart_key_password", "TEXT");
|
||||
addColumnIfNotExists("ssh_data", "stats_config", "TEXT");
|
||||
|
||||
addColumnIfNotExists("ssh_credentials", "private_key", "TEXT");
|
||||
addColumnIfNotExists("ssh_credentials", "public_key", "TEXT");
|
||||
|
||||
@@ -65,6 +65,7 @@ export const sshData = sqliteTable("ssh_data", {
|
||||
.notNull()
|
||||
.default(true),
|
||||
defaultPath: text("default_path"),
|
||||
statsConfig: text("stats_config"),
|
||||
createdAt: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import type {
|
||||
AuthenticatedRequest,
|
||||
CacheEntry,
|
||||
TermixAlert,
|
||||
} from "../../../types/index.js";
|
||||
import express from "express";
|
||||
import { db } from "../db/index.js";
|
||||
import { dismissedAlerts } from "../db/schema.js";
|
||||
@@ -6,17 +11,11 @@ import fetch from "node-fetch";
|
||||
import { authLogger } from "../../utils/logger.js";
|
||||
import { AuthManager } from "../../utils/auth-manager.js";
|
||||
|
||||
interface CacheEntry {
|
||||
data: any;
|
||||
timestamp: number;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
class AlertCache {
|
||||
private cache: Map<string, CacheEntry> = new Map();
|
||||
private readonly CACHE_DURATION = 5 * 60 * 1000;
|
||||
|
||||
set(key: string, data: any): void {
|
||||
set<T>(key: string, data: T): void {
|
||||
const now = Date.now();
|
||||
this.cache.set(key, {
|
||||
data,
|
||||
@@ -25,7 +24,7 @@ class AlertCache {
|
||||
});
|
||||
}
|
||||
|
||||
get(key: string): any | null {
|
||||
get<T>(key: string): T | null {
|
||||
const entry = this.cache.get(key);
|
||||
if (!entry) {
|
||||
return null;
|
||||
@@ -36,7 +35,7 @@ class AlertCache {
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry.data;
|
||||
return entry.data as T;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,20 +46,9 @@ const REPO_OWNER = "LukeGus";
|
||||
const REPO_NAME = "Termix-Docs";
|
||||
const ALERTS_FILE = "main/termix-alerts.json";
|
||||
|
||||
interface TermixAlert {
|
||||
id: string;
|
||||
title: string;
|
||||
message: string;
|
||||
expiresAt: string;
|
||||
priority?: "low" | "medium" | "high" | "critical";
|
||||
type?: "info" | "warning" | "error" | "success";
|
||||
actionUrl?: string;
|
||||
actionText?: string;
|
||||
}
|
||||
|
||||
async function fetchAlertsFromGitHub(): Promise<TermixAlert[]> {
|
||||
const cacheKey = "termix_alerts";
|
||||
const cachedData = alertCache.get(cacheKey);
|
||||
const cachedData = alertCache.get<TermixAlert[]>(cacheKey);
|
||||
if (cachedData) {
|
||||
return cachedData;
|
||||
}
|
||||
@@ -115,7 +103,7 @@ const authenticateJWT = authManager.createAuthMiddleware();
|
||||
// GET /alerts
|
||||
router.get("/", authenticateJWT, async (req, res) => {
|
||||
try {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
const allAlerts = await fetchAlertsFromGitHub();
|
||||
|
||||
@@ -148,7 +136,7 @@ router.get("/", authenticateJWT, async (req, res) => {
|
||||
router.post("/dismiss", authenticateJWT, async (req, res) => {
|
||||
try {
|
||||
const { alertId } = req.body;
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!alertId) {
|
||||
authLogger.warn("Missing alertId in dismiss request", { userId });
|
||||
@@ -186,7 +174,7 @@ router.post("/dismiss", authenticateJWT, async (req, res) => {
|
||||
// GET /alerts/dismissed/:userId
|
||||
router.get("/dismissed", authenticateJWT, async (req, res) => {
|
||||
try {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
const dismissedAlertRecords = await db
|
||||
.select({
|
||||
@@ -211,7 +199,7 @@ router.get("/dismissed", authenticateJWT, async (req, res) => {
|
||||
router.delete("/dismiss", authenticateJWT, async (req, res) => {
|
||||
try {
|
||||
const { alertId } = req.body;
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!alertId) {
|
||||
return res.status(400).json({ error: "Alert ID is required" });
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { AuthenticatedRequest } from "../../../types/index.js";
|
||||
import express from "express";
|
||||
import { db } from "../db/index.js";
|
||||
import { sshCredentials, sshCredentialUsage, sshData } from "../db/schema.js";
|
||||
@@ -27,7 +28,11 @@ function generateSSHKeyPair(
|
||||
} {
|
||||
try {
|
||||
let ssh2Type = keyType;
|
||||
const options: any = {};
|
||||
const options: {
|
||||
bits?: number;
|
||||
passphrase?: string;
|
||||
cipher?: string;
|
||||
} = {};
|
||||
|
||||
if (keyType === "ssh-rsa") {
|
||||
ssh2Type = "rsa";
|
||||
@@ -44,6 +49,7 @@ function generateSSHKeyPair(
|
||||
options.cipher = "aes128-cbc";
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const keyPair = ssh2Utils.generateKeyPairSync(ssh2Type as any, options);
|
||||
|
||||
return {
|
||||
@@ -62,7 +68,7 @@ function generateSSHKeyPair(
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function isNonEmptyString(val: any): val is string {
|
||||
function isNonEmptyString(val: unknown): val is string {
|
||||
return typeof val === "string" && val.trim().length > 0;
|
||||
}
|
||||
|
||||
@@ -77,7 +83,7 @@ router.post(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
@@ -224,7 +230,7 @@ router.get(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
authLogger.warn("Invalid userId for credential fetch");
|
||||
@@ -257,7 +263,7 @@ router.get(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
authLogger.warn("Invalid userId for credential folder fetch");
|
||||
@@ -295,7 +301,7 @@ router.get(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id } = req.params;
|
||||
|
||||
if (!isNonEmptyString(userId) || !id) {
|
||||
@@ -326,19 +332,19 @@ router.get(
|
||||
const output = formatCredentialOutput(credential);
|
||||
|
||||
if (credential.password) {
|
||||
(output as any).password = credential.password;
|
||||
output.password = credential.password;
|
||||
}
|
||||
if (credential.key) {
|
||||
(output as any).key = credential.key;
|
||||
output.key = credential.key;
|
||||
}
|
||||
if (credential.private_key) {
|
||||
(output as any).privateKey = credential.private_key;
|
||||
output.privateKey = credential.private_key;
|
||||
}
|
||||
if (credential.public_key) {
|
||||
(output as any).publicKey = credential.public_key;
|
||||
output.publicKey = credential.public_key;
|
||||
}
|
||||
if (credential.key_password) {
|
||||
(output as any).keyPassword = credential.key_password;
|
||||
output.keyPassword = credential.key_password;
|
||||
}
|
||||
|
||||
res.json(output);
|
||||
@@ -359,7 +365,7 @@ router.put(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
@@ -383,7 +389,7 @@ router.put(
|
||||
return res.status(404).json({ error: "Credential not found" });
|
||||
}
|
||||
|
||||
const updateFields: any = {};
|
||||
const updateFields: Record<string, string | null | undefined> = {};
|
||||
|
||||
if (updateData.name !== undefined)
|
||||
updateFields.name = updateData.name.trim();
|
||||
@@ -495,7 +501,7 @@ router.delete(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id } = req.params;
|
||||
|
||||
if (!isNonEmptyString(userId) || !id) {
|
||||
@@ -594,7 +600,7 @@ router.post(
|
||||
"/:id/apply-to-host/:hostId",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id: credentialId, hostId } = req.params;
|
||||
|
||||
if (!isNonEmptyString(userId) || !credentialId || !hostId) {
|
||||
@@ -627,8 +633,8 @@ router.post(
|
||||
.update(sshData)
|
||||
.set({
|
||||
credentialId: parseInt(credentialId),
|
||||
username: credential.username,
|
||||
authType: credential.auth_type || credential.authType,
|
||||
username: credential.username as string,
|
||||
authType: (credential.auth_type || credential.authType) as string,
|
||||
password: null,
|
||||
key: null,
|
||||
key_password: null,
|
||||
@@ -673,7 +679,7 @@ router.get(
|
||||
"/:id/hosts",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id: credentialId } = req.params;
|
||||
|
||||
if (!isNonEmptyString(userId) || !credentialId) {
|
||||
@@ -705,7 +711,9 @@ router.get(
|
||||
},
|
||||
);
|
||||
|
||||
function formatCredentialOutput(credential: any): any {
|
||||
function formatCredentialOutput(
|
||||
credential: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
return {
|
||||
id: credential.id,
|
||||
name: credential.name,
|
||||
@@ -729,7 +737,9 @@ function formatCredentialOutput(credential: any): any {
|
||||
};
|
||||
}
|
||||
|
||||
function formatSSHHostOutput(host: any): any {
|
||||
function formatSSHHostOutput(
|
||||
host: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
return {
|
||||
id: host.id,
|
||||
userId: host.userId,
|
||||
@@ -749,7 +759,7 @@ function formatSSHHostOutput(host: any): any {
|
||||
enableTerminal: !!host.enableTerminal,
|
||||
enableTunnel: !!host.enableTunnel,
|
||||
tunnelConnections: host.tunnelConnections
|
||||
? JSON.parse(host.tunnelConnections)
|
||||
? JSON.parse(host.tunnelConnections as string)
|
||||
: [],
|
||||
enableFileManager: !!host.enableFileManager,
|
||||
defaultPath: host.defaultPath,
|
||||
@@ -764,7 +774,7 @@ router.put(
|
||||
"/folders/rename",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { oldName, newName } = req.body;
|
||||
|
||||
if (!isNonEmptyString(oldName) || !isNonEmptyString(newName)) {
|
||||
@@ -1117,10 +1127,10 @@ router.post(
|
||||
);
|
||||
|
||||
async function deploySSHKeyToHost(
|
||||
hostConfig: any,
|
||||
hostConfig: Record<string, unknown>,
|
||||
publicKey: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_credentialData: any,
|
||||
_credentialData: Record<string, unknown>,
|
||||
): Promise<{ success: boolean; message?: string; error?: string }> {
|
||||
return new Promise((resolve) => {
|
||||
const conn = new Client();
|
||||
@@ -1364,7 +1374,7 @@ async function deploySSHKeyToHost(
|
||||
});
|
||||
|
||||
try {
|
||||
const connectionConfig: any = {
|
||||
const connectionConfig: Record<string, unknown> = {
|
||||
host: hostConfig.ip,
|
||||
port: hostConfig.port || 22,
|
||||
username: hostConfig.username,
|
||||
@@ -1411,14 +1421,15 @@ async function deploySSHKeyToHost(
|
||||
connectionConfig.password = hostConfig.password;
|
||||
} else if (hostConfig.authType === "key" && hostConfig.privateKey) {
|
||||
try {
|
||||
const privateKey = hostConfig.privateKey as string;
|
||||
if (
|
||||
!hostConfig.privateKey.includes("-----BEGIN") ||
|
||||
!hostConfig.privateKey.includes("-----END")
|
||||
!privateKey.includes("-----BEGIN") ||
|
||||
!privateKey.includes("-----END")
|
||||
) {
|
||||
throw new Error("Invalid private key format");
|
||||
}
|
||||
|
||||
const cleanKey = hostConfig.privateKey
|
||||
const cleanKey = privateKey
|
||||
.trim()
|
||||
.replace(/\r\n/g, "\n")
|
||||
.replace(/\r/g, "\n");
|
||||
@@ -1473,7 +1484,7 @@ router.post(
|
||||
}
|
||||
|
||||
try {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
@@ -1540,7 +1551,7 @@ router.post(
|
||||
};
|
||||
|
||||
if (hostData.authType === "credential" && hostData.credentialId) {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
if (!userId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
@@ -1554,7 +1565,7 @@ router.post(
|
||||
db
|
||||
.select()
|
||||
.from(sshCredentials)
|
||||
.where(eq(sshCredentials.id, hostData.credentialId))
|
||||
.where(eq(sshCredentials.id, hostData.credentialId as number))
|
||||
.limit(1),
|
||||
"ssh_credentials",
|
||||
userId,
|
||||
@@ -1589,7 +1600,7 @@ router.post(
|
||||
|
||||
const deployResult = await deploySSHKeyToHost(
|
||||
hostConfig,
|
||||
credData.publicKey,
|
||||
credData.publicKey as string,
|
||||
credData,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { AuthenticatedRequest } from "../../../types/index.js";
|
||||
import express from "express";
|
||||
import { db } from "../db/index.js";
|
||||
import { snippets } from "../db/schema.js";
|
||||
@@ -8,7 +9,7 @@ import { AuthManager } from "../../utils/auth-manager.js";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function isNonEmptyString(val: any): val is string {
|
||||
function isNonEmptyString(val: unknown): val is string {
|
||||
return typeof val === "string" && val.trim().length > 0;
|
||||
}
|
||||
|
||||
@@ -23,7 +24,7 @@ router.get(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
authLogger.warn("Invalid userId for snippets fetch");
|
||||
@@ -52,12 +53,15 @@ router.get(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id } = req.params;
|
||||
const snippetId = parseInt(id, 10);
|
||||
|
||||
if (!isNonEmptyString(userId) || isNaN(snippetId)) {
|
||||
authLogger.warn("Invalid request for snippet fetch: invalid ID", { userId, id });
|
||||
authLogger.warn("Invalid request for snippet fetch: invalid ID", {
|
||||
userId,
|
||||
id,
|
||||
});
|
||||
return res.status(400).json({ error: "Invalid request parameters" });
|
||||
}
|
||||
|
||||
@@ -88,7 +92,7 @@ router.post(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { name, content, description } = req.body;
|
||||
|
||||
if (
|
||||
@@ -139,7 +143,7 @@ router.put(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
@@ -158,7 +162,12 @@ router.put(
|
||||
return res.status(404).json({ error: "Snippet not found" });
|
||||
}
|
||||
|
||||
const updateFields: any = {
|
||||
const updateFields: Partial<{
|
||||
updatedAt: ReturnType<typeof sql.raw>;
|
||||
name: string;
|
||||
content: string;
|
||||
description: string | null;
|
||||
}> = {
|
||||
updatedAt: sql`CURRENT_TIMESTAMP`,
|
||||
};
|
||||
|
||||
@@ -206,7 +215,7 @@ router.delete(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { id } = req.params;
|
||||
|
||||
if (!isNonEmptyString(userId) || !id) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { AuthenticatedRequest } from "../../../types/index.js";
|
||||
import express from "express";
|
||||
import { db } from "../db/index.js";
|
||||
import {
|
||||
@@ -22,11 +23,11 @@ const router = express.Router();
|
||||
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
|
||||
function isNonEmptyString(value: any): value is string {
|
||||
function isNonEmptyString(value: unknown): value is string {
|
||||
return typeof value === "string" && value.trim().length > 0;
|
||||
}
|
||||
|
||||
function isValidPort(port: any): port is number {
|
||||
function isValidPort(port: unknown): port is number {
|
||||
return typeof port === "number" && port > 0 && port <= 65535;
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ router.get("/db/host/internal", async (req: Request, res: Response) => {
|
||||
: [];
|
||||
|
||||
const hasAutoStartTunnels = tunnelConnections.some(
|
||||
(tunnel: any) => tunnel.autoStart,
|
||||
(tunnel: Record<string, unknown>) => tunnel.autoStart,
|
||||
);
|
||||
|
||||
if (!hasAutoStartTunnels) {
|
||||
@@ -99,7 +100,7 @@ router.get("/db/host/internal", async (req: Request, res: Response) => {
|
||||
credentialId: host.credentialId,
|
||||
enableTunnel: true,
|
||||
tunnelConnections: tunnelConnections.filter(
|
||||
(tunnel: any) => tunnel.autoStart,
|
||||
(tunnel: Record<string, unknown>) => tunnel.autoStart,
|
||||
),
|
||||
pin: !!host.pin,
|
||||
enableTerminal: !!host.enableTerminal,
|
||||
@@ -183,8 +184,8 @@ router.post(
|
||||
requireDataAccess,
|
||||
upload.single("key"),
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
let hostData: any;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
let hostData: Record<string, unknown>;
|
||||
|
||||
if (req.headers["content-type"]?.includes("multipart/form-data")) {
|
||||
if (req.body.data) {
|
||||
@@ -233,6 +234,7 @@ router.post(
|
||||
enableFileManager,
|
||||
defaultPath,
|
||||
tunnelConnections,
|
||||
statsConfig,
|
||||
} = hostData;
|
||||
if (
|
||||
!isNonEmptyString(userId) ||
|
||||
@@ -250,7 +252,7 @@ router.post(
|
||||
}
|
||||
|
||||
const effectiveAuthType = authType || authMethod;
|
||||
const sshDataObj: any = {
|
||||
const sshDataObj: Record<string, unknown> = {
|
||||
userId: userId,
|
||||
name,
|
||||
folder: folder || null,
|
||||
@@ -268,6 +270,7 @@ router.post(
|
||||
: null,
|
||||
enableFileManager: enableFileManager ? 1 : 0,
|
||||
defaultPath: defaultPath || null,
|
||||
statsConfig: statsConfig ? JSON.stringify(statsConfig) : null,
|
||||
};
|
||||
|
||||
if (effectiveAuthType === "password") {
|
||||
@@ -319,9 +322,12 @@ router.post(
|
||||
enableTerminal: !!createdHost.enableTerminal,
|
||||
enableTunnel: !!createdHost.enableTunnel,
|
||||
tunnelConnections: createdHost.tunnelConnections
|
||||
? JSON.parse(createdHost.tunnelConnections)
|
||||
? JSON.parse(createdHost.tunnelConnections as string)
|
||||
: [],
|
||||
enableFileManager: !!createdHost.enableFileManager,
|
||||
statsConfig: createdHost.statsConfig
|
||||
? JSON.parse(createdHost.statsConfig as string)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const resolvedHost = (await resolveHostCredentials(baseHost)) || baseHost;
|
||||
@@ -331,7 +337,7 @@ router.post(
|
||||
{
|
||||
operation: "host_create_success",
|
||||
userId,
|
||||
hostId: createdHost.id,
|
||||
hostId: createdHost.id as number,
|
||||
name,
|
||||
ip,
|
||||
port,
|
||||
@@ -362,8 +368,8 @@ router.put(
|
||||
upload.single("key"),
|
||||
async (req: Request, res: Response) => {
|
||||
const hostId = req.params.id;
|
||||
const userId = (req as any).userId;
|
||||
let hostData: any;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
let hostData: Record<string, unknown>;
|
||||
|
||||
if (req.headers["content-type"]?.includes("multipart/form-data")) {
|
||||
if (req.body.data) {
|
||||
@@ -414,6 +420,7 @@ router.put(
|
||||
enableFileManager,
|
||||
defaultPath,
|
||||
tunnelConnections,
|
||||
statsConfig,
|
||||
} = hostData;
|
||||
if (
|
||||
!isNonEmptyString(userId) ||
|
||||
@@ -433,7 +440,7 @@ router.put(
|
||||
}
|
||||
|
||||
const effectiveAuthType = authType || authMethod;
|
||||
const sshDataObj: any = {
|
||||
const sshDataObj: Record<string, unknown> = {
|
||||
name,
|
||||
folder,
|
||||
tags: Array.isArray(tags) ? tags.join(",") : tags || "",
|
||||
@@ -450,6 +457,7 @@ router.put(
|
||||
: null,
|
||||
enableFileManager: enableFileManager ? 1 : 0,
|
||||
defaultPath: defaultPath || null,
|
||||
statsConfig: statsConfig ? JSON.stringify(statsConfig) : null,
|
||||
};
|
||||
|
||||
if (effectiveAuthType === "password") {
|
||||
@@ -519,9 +527,12 @@ router.put(
|
||||
enableTerminal: !!updatedHost.enableTerminal,
|
||||
enableTunnel: !!updatedHost.enableTunnel,
|
||||
tunnelConnections: updatedHost.tunnelConnections
|
||||
? JSON.parse(updatedHost.tunnelConnections)
|
||||
? JSON.parse(updatedHost.tunnelConnections as string)
|
||||
: [],
|
||||
enableFileManager: !!updatedHost.enableFileManager,
|
||||
statsConfig: updatedHost.statsConfig
|
||||
? JSON.parse(updatedHost.statsConfig as string)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const resolvedHost = (await resolveHostCredentials(baseHost)) || baseHost;
|
||||
@@ -558,7 +569,7 @@ router.put(
|
||||
// Route: Get SSH data for the authenticated user (requires JWT)
|
||||
// GET /ssh/host
|
||||
router.get("/db/host", authenticateJWT, async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
if (!isNonEmptyString(userId)) {
|
||||
sshLogger.warn("Invalid userId for SSH data fetch", {
|
||||
operation: "host_fetch",
|
||||
@@ -574,7 +585,7 @@ router.get("/db/host", authenticateJWT, async (req: Request, res: Response) => {
|
||||
);
|
||||
|
||||
const result = await Promise.all(
|
||||
data.map(async (row: any) => {
|
||||
data.map(async (row: Record<string, unknown>) => {
|
||||
const baseHost = {
|
||||
...row,
|
||||
tags:
|
||||
@@ -587,9 +598,12 @@ router.get("/db/host", authenticateJWT, async (req: Request, res: Response) => {
|
||||
enableTerminal: !!row.enableTerminal,
|
||||
enableTunnel: !!row.enableTunnel,
|
||||
tunnelConnections: row.tunnelConnections
|
||||
? JSON.parse(row.tunnelConnections)
|
||||
? JSON.parse(row.tunnelConnections as string)
|
||||
: [],
|
||||
enableFileManager: !!row.enableFileManager,
|
||||
statsConfig: row.statsConfig
|
||||
? JSON.parse(row.statsConfig as string)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return (await resolveHostCredentials(baseHost)) || baseHost;
|
||||
@@ -613,7 +627,7 @@ router.get(
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const hostId = req.params.id;
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId) {
|
||||
sshLogger.warn("Invalid userId or hostId for SSH host fetch by ID", {
|
||||
@@ -654,6 +668,9 @@ router.get(
|
||||
? JSON.parse(host.tunnelConnections)
|
||||
: [],
|
||||
enableFileManager: !!host.enableFileManager,
|
||||
statsConfig: host.statsConfig
|
||||
? JSON.parse(host.statsConfig)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
res.json((await resolveHostCredentials(result)) || result);
|
||||
@@ -676,7 +693,7 @@ router.get(
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const hostId = req.params.id;
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId) {
|
||||
return res.status(400).json({ error: "Invalid userId or hostId" });
|
||||
@@ -723,7 +740,7 @@ router.get(
|
||||
enableFileManager: !!resolvedHost.enableFileManager,
|
||||
defaultPath: resolvedHost.defaultPath,
|
||||
tunnelConnections: resolvedHost.tunnelConnections
|
||||
? JSON.parse(resolvedHost.tunnelConnections)
|
||||
? JSON.parse(resolvedHost.tunnelConnections as string)
|
||||
: [],
|
||||
};
|
||||
|
||||
@@ -751,7 +768,7 @@ router.delete(
|
||||
"/db/host/:id",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const hostId = req.params.id;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId) {
|
||||
@@ -850,7 +867,7 @@ router.get(
|
||||
"/file_manager/recent",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const hostId = req.query.hostId
|
||||
? parseInt(req.query.hostId as string)
|
||||
: null;
|
||||
@@ -892,7 +909,7 @@ router.post(
|
||||
"/file_manager/recent",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path, name } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
@@ -941,7 +958,7 @@ router.delete(
|
||||
"/file_manager/recent",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
@@ -974,7 +991,7 @@ router.get(
|
||||
"/file_manager/pinned",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const hostId = req.query.hostId
|
||||
? parseInt(req.query.hostId as string)
|
||||
: null;
|
||||
@@ -1015,7 +1032,7 @@ router.post(
|
||||
"/file_manager/pinned",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path, name } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
@@ -1061,7 +1078,7 @@ router.delete(
|
||||
"/file_manager/pinned",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
@@ -1094,7 +1111,7 @@ router.get(
|
||||
"/file_manager/shortcuts",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const hostId = req.query.hostId
|
||||
? parseInt(req.query.hostId as string)
|
||||
: null;
|
||||
@@ -1135,7 +1152,7 @@ router.post(
|
||||
"/file_manager/shortcuts",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path, name } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
@@ -1181,7 +1198,7 @@ router.delete(
|
||||
"/file_manager/shortcuts",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
@@ -1208,21 +1225,26 @@ router.delete(
|
||||
},
|
||||
);
|
||||
|
||||
async function resolveHostCredentials(host: any): Promise<any> {
|
||||
async function resolveHostCredentials(
|
||||
host: Record<string, unknown>,
|
||||
): Promise<Record<string, unknown>> {
|
||||
try {
|
||||
if (host.credentialId && host.userId) {
|
||||
const credentialId = host.credentialId as number;
|
||||
const userId = host.userId as string;
|
||||
|
||||
const credentials = await SimpleDBOps.select(
|
||||
db
|
||||
.select()
|
||||
.from(sshCredentials)
|
||||
.where(
|
||||
and(
|
||||
eq(sshCredentials.id, host.credentialId),
|
||||
eq(sshCredentials.userId, host.userId),
|
||||
eq(sshCredentials.id, credentialId),
|
||||
eq(sshCredentials.userId, userId),
|
||||
),
|
||||
),
|
||||
"ssh_credentials",
|
||||
host.userId,
|
||||
userId,
|
||||
);
|
||||
|
||||
if (credentials.length > 0) {
|
||||
@@ -1239,13 +1261,6 @@ async function resolveHostCredentials(host: any): Promise<any> {
|
||||
}
|
||||
}
|
||||
|
||||
const result = { ...host };
|
||||
if (host.key_password !== undefined) {
|
||||
if (result.keyPassword === undefined) {
|
||||
result.keyPassword = host.key_password;
|
||||
}
|
||||
delete result.key_password;
|
||||
}
|
||||
const result = { ...host };
|
||||
if (host.key_password !== undefined) {
|
||||
if (result.keyPassword === undefined) {
|
||||
@@ -1268,7 +1283,7 @@ router.put(
|
||||
"/folders/rename",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { oldName, newName } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !oldName || !newName) {
|
||||
@@ -1333,7 +1348,7 @@ router.post(
|
||||
"/bulk-import",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hosts } = req.body;
|
||||
|
||||
if (!Array.isArray(hosts) || hosts.length === 0) {
|
||||
@@ -1405,7 +1420,7 @@ router.post(
|
||||
continue;
|
||||
}
|
||||
|
||||
const sshDataObj: any = {
|
||||
const sshDataObj: Record<string, unknown> = {
|
||||
userId: userId,
|
||||
name: hostData.name || `${hostData.username}@${hostData.ip}`,
|
||||
folder: hostData.folder || "Default",
|
||||
@@ -1430,6 +1445,9 @@ router.post(
|
||||
tunnelConnections: hostData.tunnelConnections
|
||||
? JSON.stringify(hostData.tunnelConnections)
|
||||
: "[]",
|
||||
statsConfig: hostData.statsConfig
|
||||
? JSON.stringify(hostData.statsConfig)
|
||||
: null,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
@@ -1460,7 +1478,7 @@ router.post(
|
||||
authenticateJWT,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { sshConfigId } = req.body;
|
||||
|
||||
if (!sshConfigId || typeof sshConfigId !== "number") {
|
||||
@@ -1524,7 +1542,7 @@ router.post(
|
||||
const tunnelConnections = JSON.parse(config.tunnelConnections);
|
||||
|
||||
const resolvedConnections = await Promise.all(
|
||||
tunnelConnections.map(async (tunnel: any) => {
|
||||
tunnelConnections.map(async (tunnel: Record<string, unknown>) => {
|
||||
if (
|
||||
tunnel.autoStart &&
|
||||
tunnel.endpointHost &&
|
||||
@@ -1613,7 +1631,7 @@ router.delete(
|
||||
"/autostart/disable",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { sshConfigId } = req.body;
|
||||
|
||||
if (!sshConfigId || typeof sshConfigId !== "number") {
|
||||
@@ -1659,7 +1677,7 @@ router.get(
|
||||
"/autostart/status",
|
||||
authenticateJWT,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
try {
|
||||
const autostartConfigs = await db
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { AuthenticatedRequest } from "../../../types/index.js";
|
||||
import express from "express";
|
||||
import crypto from "crypto";
|
||||
import { db } from "../db/index.js";
|
||||
@@ -27,7 +28,7 @@ async function verifyOIDCToken(
|
||||
idToken: string,
|
||||
issuerUrl: string,
|
||||
clientId: string,
|
||||
): Promise<any> {
|
||||
): Promise<Record<string, unknown>> {
|
||||
const normalizedIssuerUrl = issuerUrl.endsWith("/")
|
||||
? issuerUrl.slice(0, -1)
|
||||
: issuerUrl;
|
||||
@@ -48,22 +49,25 @@ async function verifyOIDCToken(
|
||||
const discoveryUrl = `${normalizedIssuerUrl}/.well-known/openid-configuration`;
|
||||
const discoveryResponse = await fetch(discoveryUrl);
|
||||
if (discoveryResponse.ok) {
|
||||
const discovery = (await discoveryResponse.json()) as any;
|
||||
const discovery = (await discoveryResponse.json()) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
if (discovery.jwks_uri) {
|
||||
jwksUrls.unshift(discovery.jwks_uri);
|
||||
jwksUrls.unshift(discovery.jwks_uri as string);
|
||||
}
|
||||
}
|
||||
} catch (discoveryError) {
|
||||
authLogger.error(`OIDC discovery failed: ${discoveryError}`);
|
||||
}
|
||||
|
||||
let jwks: any = null;
|
||||
let jwks: Record<string, unknown> | null = null;
|
||||
|
||||
for (const url of jwksUrls) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (response.ok) {
|
||||
const jwksData = (await response.json()) as any;
|
||||
const jwksData = (await response.json()) as Record<string, unknown>;
|
||||
if (jwksData && jwksData.keys && Array.isArray(jwksData.keys)) {
|
||||
jwks = jwksData;
|
||||
break;
|
||||
@@ -95,10 +99,12 @@ async function verifyOIDCToken(
|
||||
);
|
||||
const keyId = header.kid;
|
||||
|
||||
const publicKey = jwks.keys.find((key: any) => key.kid === keyId);
|
||||
const publicKey = jwks.keys.find(
|
||||
(key: Record<string, unknown>) => key.kid === keyId,
|
||||
);
|
||||
if (!publicKey) {
|
||||
throw new Error(
|
||||
`No matching public key found for key ID: ${keyId}. Available keys: ${jwks.keys.map((k: any) => k.kid).join(", ")}`,
|
||||
`No matching public key found for key ID: ${keyId}. Available keys: ${jwks.keys.map((k: Record<string, unknown>) => k.kid).join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,7 +121,7 @@ async function verifyOIDCToken(
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function isNonEmptyString(val: any): val is string {
|
||||
function isNonEmptyString(val: unknown): val is string {
|
||||
return typeof val === "string" && val.trim().length > 0;
|
||||
}
|
||||
|
||||
@@ -129,7 +135,7 @@ router.post("/create", async (req, res) => {
|
||||
const row = db.$client
|
||||
.prepare("SELECT value FROM settings WHERE key = 'allow_registration'")
|
||||
.get();
|
||||
if (row && (row as any).value !== "true") {
|
||||
if (row && (row as Record<string, unknown>).value !== "true") {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ error: "Registration is currently disabled" });
|
||||
@@ -174,7 +180,7 @@ router.post("/create", async (req, res) => {
|
||||
const countResult = db.$client
|
||||
.prepare("SELECT COUNT(*) as count FROM users")
|
||||
.get();
|
||||
isFirstUser = ((countResult as any)?.count || 0) === 0;
|
||||
isFirstUser = ((countResult as { count?: number })?.count || 0) === 0;
|
||||
|
||||
const saltRounds = parseInt(process.env.SALT || "10", 10);
|
||||
const password_hash = await bcrypt.hash(password, saltRounds);
|
||||
@@ -238,7 +244,7 @@ router.post("/create", async (req, res) => {
|
||||
// Route: Create OIDC provider configuration (admin only)
|
||||
// POST /users/oidc-config
|
||||
router.post("/oidc-config", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
if (!user || user.length === 0 || !user[0].is_admin) {
|
||||
@@ -378,7 +384,7 @@ router.post("/oidc-config", authenticateJWT, async (req, res) => {
|
||||
// Route: Disable OIDC configuration (admin only)
|
||||
// DELETE /users/oidc-config
|
||||
router.delete("/oidc-config", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
if (!user || user.length === 0 || !user[0].is_admin) {
|
||||
@@ -408,7 +414,7 @@ router.get("/oidc-config", async (req, res) => {
|
||||
return res.json(null);
|
||||
}
|
||||
|
||||
let config = JSON.parse((row as any).value);
|
||||
let config = JSON.parse((row as Record<string, unknown>).value as string);
|
||||
|
||||
if (config.client_secret) {
|
||||
if (config.client_secret.startsWith("encrypted:")) {
|
||||
@@ -485,7 +491,7 @@ router.get("/oidc/authorize", async (req, res) => {
|
||||
return res.status(404).json({ error: "OIDC not configured" });
|
||||
}
|
||||
|
||||
const config = JSON.parse((row as any).value);
|
||||
const config = JSON.parse((row as Record<string, unknown>).value as string);
|
||||
const state = nanoid();
|
||||
const nonce = nanoid();
|
||||
|
||||
@@ -540,7 +546,8 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
.status(400)
|
||||
.json({ error: "Invalid state parameter - redirect URI not found" });
|
||||
}
|
||||
const redirectUri = (storedRedirectRow as any).value;
|
||||
const redirectUri = (storedRedirectRow as Record<string, unknown>)
|
||||
.value as string;
|
||||
|
||||
try {
|
||||
const storedNonce = db.$client
|
||||
@@ -564,7 +571,9 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
return res.status(500).json({ error: "OIDC not configured" });
|
||||
}
|
||||
|
||||
const config = JSON.parse((configRow as any).value);
|
||||
const config = JSON.parse(
|
||||
(configRow as Record<string, unknown>).value as string,
|
||||
);
|
||||
|
||||
const tokenResponse = await fetch(config.token_url, {
|
||||
method: "POST",
|
||||
@@ -590,9 +599,9 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
.json({ error: "Failed to exchange authorization code" });
|
||||
}
|
||||
|
||||
const tokenData = (await tokenResponse.json()) as any;
|
||||
const tokenData = (await tokenResponse.json()) as Record<string, unknown>;
|
||||
|
||||
let userInfo: any = null;
|
||||
let userInfo: Record<string, unknown> = null;
|
||||
const userInfoUrls: string[] = [];
|
||||
|
||||
const normalizedIssuerUrl = config.issuer_url.endsWith("/")
|
||||
@@ -604,9 +613,12 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
const discoveryUrl = `${normalizedIssuerUrl}/.well-known/openid-configuration`;
|
||||
const discoveryResponse = await fetch(discoveryUrl);
|
||||
if (discoveryResponse.ok) {
|
||||
const discovery = (await discoveryResponse.json()) as any;
|
||||
const discovery = (await discoveryResponse.json()) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
if (discovery.userinfo_endpoint) {
|
||||
userInfoUrls.push(discovery.userinfo_endpoint);
|
||||
userInfoUrls.push(discovery.userinfo_endpoint as string);
|
||||
}
|
||||
}
|
||||
} catch (discoveryError) {
|
||||
@@ -631,14 +643,14 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
if (tokenData.id_token) {
|
||||
try {
|
||||
userInfo = await verifyOIDCToken(
|
||||
tokenData.id_token,
|
||||
tokenData.id_token as string,
|
||||
config.issuer_url,
|
||||
config.client_id,
|
||||
);
|
||||
} catch {
|
||||
// Fallback to manual decoding
|
||||
try {
|
||||
const parts = tokenData.id_token.split(".");
|
||||
const parts = (tokenData.id_token as string).split(".");
|
||||
if (parts.length === 3) {
|
||||
const payload = JSON.parse(
|
||||
Buffer.from(parts[1], "base64").toString(),
|
||||
@@ -661,7 +673,10 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
});
|
||||
|
||||
if (userInfoResponse.ok) {
|
||||
userInfo = await userInfoResponse.json();
|
||||
userInfo = (await userInfoResponse.json()) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
break;
|
||||
} else {
|
||||
authLogger.error(
|
||||
@@ -684,24 +699,25 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
return res.status(400).json({ error: "Failed to get user information" });
|
||||
}
|
||||
|
||||
const getNestedValue = (obj: any, path: string): any => {
|
||||
const getNestedValue = (
|
||||
obj: Record<string, unknown>,
|
||||
path: string,
|
||||
): 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(
|
||||
@@ -725,7 +741,7 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
const countResult = db.$client
|
||||
.prepare("SELECT COUNT(*) as count FROM users")
|
||||
.get();
|
||||
isFirstUser = ((countResult as any)?.count || 0) === 0;
|
||||
isFirstUser = ((countResult as { count?: number })?.count || 0) === 0;
|
||||
|
||||
const id = nanoid();
|
||||
await db.insert(users).values({
|
||||
@@ -735,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 {
|
||||
@@ -787,7 +803,10 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
expiresIn: "50d",
|
||||
});
|
||||
|
||||
let frontendUrl = redirectUri.replace("/users/oidc/callback", "");
|
||||
let frontendUrl = (redirectUri as string).replace(
|
||||
"/users/oidc/callback",
|
||||
"",
|
||||
);
|
||||
|
||||
if (frontendUrl.includes("localhost")) {
|
||||
frontendUrl = "http://localhost:5173";
|
||||
@@ -806,7 +825,10 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
} catch (err) {
|
||||
authLogger.error("OIDC callback failed", err);
|
||||
|
||||
let frontendUrl = redirectUri.replace("/users/oidc/callback", "");
|
||||
let frontendUrl = (redirectUri as string).replace(
|
||||
"/users/oidc/callback",
|
||||
"",
|
||||
);
|
||||
|
||||
if (frontendUrl.includes("localhost")) {
|
||||
frontendUrl = "http://localhost:5173";
|
||||
@@ -931,7 +953,7 @@ router.post("/login", async (req, res) => {
|
||||
dataUnlocked: true,
|
||||
});
|
||||
|
||||
const response: any = {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
is_admin: !!userRecord.is_admin,
|
||||
username: userRecord.username,
|
||||
@@ -962,7 +984,7 @@ router.post("/login", async (req, res) => {
|
||||
// POST /users/logout
|
||||
router.post("/logout", async (req, res) => {
|
||||
try {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (userId) {
|
||||
authManager.logoutUser(userId);
|
||||
@@ -984,7 +1006,7 @@ router.post("/logout", async (req, res) => {
|
||||
// Route: Get current user's info using JWT
|
||||
// GET /users/me
|
||||
router.get("/me", authenticateJWT, async (req: Request, res: Response) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
if (!isNonEmptyString(userId)) {
|
||||
authLogger.warn("Invalid userId in JWT for /users/me");
|
||||
return res.status(401).json({ error: "Invalid userId" });
|
||||
@@ -1019,7 +1041,7 @@ router.get("/setup-required", async (req, res) => {
|
||||
const countResult = db.$client
|
||||
.prepare("SELECT COUNT(*) as count FROM users")
|
||||
.get();
|
||||
const count = (countResult as any)?.count || 0;
|
||||
const count = (countResult as { count?: number })?.count || 0;
|
||||
|
||||
res.json({
|
||||
setup_required: count === 0,
|
||||
@@ -1033,7 +1055,7 @@ router.get("/setup-required", async (req, res) => {
|
||||
// Route: Count users (admin only - for dashboard statistics)
|
||||
// GET /users/count
|
||||
router.get("/count", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
if (!user[0] || !user[0].is_admin) {
|
||||
@@ -1043,7 +1065,7 @@ router.get("/count", authenticateJWT, async (req, res) => {
|
||||
const countResult = db.$client
|
||||
.prepare("SELECT COUNT(*) as count FROM users")
|
||||
.get();
|
||||
const count = (countResult as any)?.count || 0;
|
||||
const count = (countResult as { count?: number })?.count || 0;
|
||||
res.json({ count });
|
||||
} catch (err) {
|
||||
authLogger.error("Failed to count users", err);
|
||||
@@ -1070,7 +1092,9 @@ router.get("/registration-allowed", async (req, res) => {
|
||||
const row = db.$client
|
||||
.prepare("SELECT value FROM settings WHERE key = 'allow_registration'")
|
||||
.get();
|
||||
res.json({ allowed: row ? (row as any).value === "true" : true });
|
||||
res.json({
|
||||
allowed: row ? (row as Record<string, unknown>).value === "true" : true,
|
||||
});
|
||||
} catch (err) {
|
||||
authLogger.error("Failed to get registration allowed", err);
|
||||
res.status(500).json({ error: "Failed to get registration allowed" });
|
||||
@@ -1080,7 +1104,7 @@ router.get("/registration-allowed", async (req, res) => {
|
||||
// Route: Set registration allowed status (admin only)
|
||||
// PATCH /users/registration-allowed
|
||||
router.patch("/registration-allowed", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
if (!user || user.length === 0 || !user[0].is_admin) {
|
||||
@@ -1107,7 +1131,9 @@ router.get("/password-login-allowed", async (req, res) => {
|
||||
const row = db.$client
|
||||
.prepare("SELECT value FROM settings WHERE key = 'allow_password_login'")
|
||||
.get();
|
||||
res.json({ allowed: row ? (row as { value: string }).value === "true" : true });
|
||||
res.json({
|
||||
allowed: row ? (row as { value: string }).value === "true" : true,
|
||||
});
|
||||
} catch (err) {
|
||||
authLogger.error("Failed to get password login allowed", err);
|
||||
res.status(500).json({ error: "Failed to get password login allowed" });
|
||||
@@ -1117,7 +1143,7 @@ router.get("/password-login-allowed", async (req, res) => {
|
||||
// Route: Set password login allowed status (admin only)
|
||||
// PATCH /users/password-login-allowed
|
||||
router.patch("/password-login-allowed", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
if (!user || user.length === 0 || !user[0].is_admin) {
|
||||
@@ -1128,7 +1154,9 @@ router.patch("/password-login-allowed", authenticateJWT, async (req, res) => {
|
||||
return res.status(400).json({ error: "Invalid value for allowed" });
|
||||
}
|
||||
db.$client
|
||||
.prepare("UPDATE settings SET value = ? WHERE key = 'allow_password_login'")
|
||||
.prepare(
|
||||
"UPDATE settings SET value = ? WHERE key = 'allow_password_login'",
|
||||
)
|
||||
.run(allowed ? "true" : "false");
|
||||
res.json({ allowed });
|
||||
} catch (err) {
|
||||
@@ -1140,7 +1168,7 @@ router.patch("/password-login-allowed", authenticateJWT, async (req, res) => {
|
||||
// Route: Delete user account
|
||||
// DELETE /users/delete-account
|
||||
router.delete("/delete-account", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { password } = req.body;
|
||||
|
||||
if (!isNonEmptyString(password)) {
|
||||
@@ -1176,7 +1204,7 @@ router.delete("/delete-account", authenticateJWT, async (req, res) => {
|
||||
const adminCount = db.$client
|
||||
.prepare("SELECT COUNT(*) as count FROM users WHERE is_admin = 1")
|
||||
.get();
|
||||
if ((adminCount as any)?.count <= 1) {
|
||||
if (((adminCount as { count?: number })?.count || 0) <= 1) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ error: "Cannot delete the last admin user" });
|
||||
@@ -1266,7 +1294,9 @@ router.post("/verify-reset-code", async (req, res) => {
|
||||
.json({ error: "No reset code found for this user" });
|
||||
}
|
||||
|
||||
const resetData = JSON.parse((resetDataRow as any).value);
|
||||
const resetData = JSON.parse(
|
||||
(resetDataRow as Record<string, unknown>).value as string,
|
||||
);
|
||||
const now = new Date();
|
||||
const expiresAt = new Date(resetData.expiresAt);
|
||||
|
||||
@@ -1324,7 +1354,9 @@ router.post("/complete-reset", async (req, res) => {
|
||||
return res.status(400).json({ error: "No temporary token found" });
|
||||
}
|
||||
|
||||
const tempTokenData = JSON.parse((tempTokenRow as any).value);
|
||||
const tempTokenData = JSON.parse(
|
||||
(tempTokenRow as Record<string, unknown>).value as string,
|
||||
);
|
||||
const now = new Date();
|
||||
const expiresAt = new Date(tempTokenData.expiresAt);
|
||||
|
||||
@@ -1412,7 +1444,7 @@ router.post("/complete-reset", async (req, res) => {
|
||||
// Route: List all users (admin only)
|
||||
// GET /users/list
|
||||
router.get("/list", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
if (!user || user.length === 0 || !user[0].is_admin) {
|
||||
@@ -1438,7 +1470,7 @@ router.get("/list", authenticateJWT, async (req, res) => {
|
||||
// Route: Make user admin (admin only)
|
||||
// POST /users/make-admin
|
||||
router.post("/make-admin", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { username } = req.body;
|
||||
|
||||
if (!isNonEmptyString(username)) {
|
||||
@@ -1481,7 +1513,7 @@ router.post("/make-admin", authenticateJWT, async (req, res) => {
|
||||
// Route: Remove admin status (admin only)
|
||||
// POST /users/remove-admin
|
||||
router.post("/remove-admin", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { username } = req.body;
|
||||
|
||||
if (!isNonEmptyString(username)) {
|
||||
@@ -1638,7 +1670,7 @@ router.post("/totp/verify-login", async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const response: any = {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
is_admin: !!userRecord.is_admin,
|
||||
username: userRecord.username,
|
||||
@@ -1668,7 +1700,7 @@ router.post("/totp/verify-login", async (req, res) => {
|
||||
// Route: Setup TOTP
|
||||
// POST /users/totp/setup
|
||||
router.post("/totp/setup", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
@@ -1707,7 +1739,7 @@ router.post("/totp/setup", authenticateJWT, async (req, res) => {
|
||||
// Route: Enable TOTP
|
||||
// POST /users/totp/enable
|
||||
router.post("/totp/enable", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { totp_code } = req.body;
|
||||
|
||||
if (!totp_code) {
|
||||
@@ -1766,7 +1798,7 @@ router.post("/totp/enable", authenticateJWT, async (req, res) => {
|
||||
// Route: Disable TOTP
|
||||
// POST /users/totp/disable
|
||||
router.post("/totp/disable", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { password, totp_code } = req.body;
|
||||
|
||||
if (!password && !totp_code) {
|
||||
@@ -1824,7 +1856,7 @@ router.post("/totp/disable", authenticateJWT, async (req, res) => {
|
||||
// Route: Generate new backup codes
|
||||
// POST /users/totp/backup-codes
|
||||
router.post("/totp/backup-codes", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { password, totp_code } = req.body;
|
||||
|
||||
if (!password && !totp_code) {
|
||||
@@ -1882,7 +1914,7 @@ router.post("/totp/backup-codes", authenticateJWT, async (req, res) => {
|
||||
// Route: Delete user (admin only)
|
||||
// DELETE /users/delete-user
|
||||
router.delete("/delete-user", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { username } = req.body;
|
||||
|
||||
if (!isNonEmptyString(username)) {
|
||||
@@ -1911,7 +1943,7 @@ router.delete("/delete-user", authenticateJWT, async (req, res) => {
|
||||
const adminCount = db.$client
|
||||
.prepare("SELECT COUNT(*) as count FROM users WHERE is_admin = 1")
|
||||
.get();
|
||||
if ((adminCount as any)?.count <= 1) {
|
||||
if (((adminCount as { count?: number })?.count || 0) <= 1) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ error: "Cannot delete the last admin user" });
|
||||
@@ -1968,7 +2000,7 @@ router.delete("/delete-user", authenticateJWT, async (req, res) => {
|
||||
// Route: User data unlock - used when session expires
|
||||
// POST /users/unlock-data
|
||||
router.post("/unlock-data", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { password } = req.body;
|
||||
|
||||
if (!password) {
|
||||
@@ -2001,7 +2033,7 @@ router.post("/unlock-data", authenticateJWT, async (req, res) => {
|
||||
// Route: Check user data unlock status
|
||||
// GET /users/data-status
|
||||
router.get("/data-status", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
try {
|
||||
const isUnlocked = authManager.isUserUnlocked(userId);
|
||||
@@ -2023,7 +2055,7 @@ router.get("/data-status", authenticateJWT, async (req, res) => {
|
||||
// Route: Change user password (re-encrypt data keys)
|
||||
// POST /users/change-password
|
||||
router.post("/change-password", authenticateJWT, async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { currentPassword, newPassword } = req.body;
|
||||
|
||||
if (!currentPassword || !newPassword) {
|
||||
|
||||
@@ -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<string, unknown> = {
|
||||
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) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { eq, and } from "drizzle-orm";
|
||||
import { statsLogger } 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";
|
||||
|
||||
interface PooledConnection {
|
||||
client: Client;
|
||||
@@ -60,7 +61,7 @@ class SSHConnectionPool {
|
||||
return client;
|
||||
}
|
||||
|
||||
return new Promise((resolve, _reject) => {
|
||||
return new Promise((resolve) => {
|
||||
const checkAvailable = () => {
|
||||
const available = connections.find((conn) => !conn.inUse);
|
||||
if (available) {
|
||||
@@ -119,7 +120,11 @@ class SSHConnectionPool {
|
||||
},
|
||||
);
|
||||
client.end();
|
||||
reject(new Error("TOTP authentication required but not supported in Server Stats"));
|
||||
reject(
|
||||
new Error(
|
||||
"TOTP authentication required but not supported in Server Stats",
|
||||
),
|
||||
);
|
||||
} else if (host.password) {
|
||||
const responses = prompts.map(() => host.password || "");
|
||||
finish(responses);
|
||||
@@ -189,7 +194,7 @@ class SSHConnectionPool {
|
||||
}
|
||||
|
||||
class RequestQueue {
|
||||
private queues = new Map<number, Array<() => Promise<any>>>();
|
||||
private queues = new Map<number, Array<() => Promise<unknown>>>();
|
||||
private processing = new Set<number>();
|
||||
|
||||
async queueRequest<T>(hostId: number, request: () => Promise<T>): Promise<T> {
|
||||
@@ -233,7 +238,7 @@ class RequestQueue {
|
||||
}
|
||||
|
||||
interface CachedMetrics {
|
||||
data: any;
|
||||
data: unknown;
|
||||
timestamp: number;
|
||||
hostId: number;
|
||||
}
|
||||
@@ -242,7 +247,7 @@ class MetricsCache {
|
||||
private cache = new Map<number, CachedMetrics>();
|
||||
private ttl = 30000;
|
||||
|
||||
get(hostId: number): any | null {
|
||||
get(hostId: number): unknown | null {
|
||||
const cached = this.cache.get(hostId);
|
||||
if (cached && Date.now() - cached.timestamp < this.ttl) {
|
||||
return cached.data;
|
||||
@@ -250,7 +255,7 @@ class MetricsCache {
|
||||
return null;
|
||||
}
|
||||
|
||||
set(hostId: number, data: any): void {
|
||||
set(hostId: number, data: unknown): void {
|
||||
this.cache.set(hostId, {
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
@@ -293,7 +298,8 @@ interface SSHHostWithCredentials {
|
||||
enableTunnel: boolean;
|
||||
enableFileManager: boolean;
|
||||
defaultPath: string;
|
||||
tunnelConnections: any[];
|
||||
tunnelConnections: unknown[];
|
||||
statsConfig?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
userId: string;
|
||||
@@ -427,11 +433,11 @@ async function fetchHostById(
|
||||
}
|
||||
|
||||
async function resolveHostCredentials(
|
||||
host: any,
|
||||
host: Record<string, unknown>,
|
||||
userId: string,
|
||||
): Promise<SSHHostWithCredentials | undefined> {
|
||||
try {
|
||||
const baseHost: any = {
|
||||
const baseHost: Record<string, unknown> = {
|
||||
id: host.id,
|
||||
name: host.name,
|
||||
ip: host.ip,
|
||||
@@ -451,8 +457,9 @@ async function resolveHostCredentials(
|
||||
enableFileManager: !!host.enableFileManager,
|
||||
defaultPath: host.defaultPath || "/",
|
||||
tunnelConnections: host.tunnelConnections
|
||||
? JSON.parse(host.tunnelConnections)
|
||||
? JSON.parse(host.tunnelConnections as string)
|
||||
: [],
|
||||
statsConfig: host.statsConfig || undefined,
|
||||
createdAt: host.createdAt,
|
||||
updatedAt: host.updatedAt,
|
||||
userId: host.userId,
|
||||
@@ -466,7 +473,7 @@ async function resolveHostCredentials(
|
||||
.from(sshCredentials)
|
||||
.where(
|
||||
and(
|
||||
eq(sshCredentials.id, host.credentialId),
|
||||
eq(sshCredentials.id, host.credentialId as number),
|
||||
eq(sshCredentials.userId, userId),
|
||||
),
|
||||
),
|
||||
@@ -506,7 +513,7 @@ async function resolveHostCredentials(
|
||||
addLegacyCredentials(baseHost, host);
|
||||
}
|
||||
|
||||
return baseHost;
|
||||
return baseHost as unknown as SSHHostWithCredentials;
|
||||
} catch (error) {
|
||||
statsLogger.error(
|
||||
`Failed to resolve host credentials for host ${host.id}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
@@ -515,7 +522,10 @@ async function resolveHostCredentials(
|
||||
}
|
||||
}
|
||||
|
||||
function addLegacyCredentials(baseHost: any, host: any): void {
|
||||
function addLegacyCredentials(
|
||||
baseHost: Record<string, unknown>,
|
||||
host: Record<string, unknown>,
|
||||
): void {
|
||||
baseHost.password = host.password || null;
|
||||
baseHost.key = host.key || null;
|
||||
baseHost.keyPassword = host.key_password || host.keyPassword || null;
|
||||
@@ -567,7 +577,7 @@ function buildSshConfig(host: SSHHostWithCredentials): ConnectConfig {
|
||||
if (!host.password) {
|
||||
throw new Error(`No password available for host ${host.ip}`);
|
||||
}
|
||||
(base as any).password = host.password;
|
||||
(base as Record<string, unknown>).password = host.password;
|
||||
} else if (host.authType === "key") {
|
||||
if (!host.key) {
|
||||
throw new Error(`No SSH key available for host ${host.ip}`);
|
||||
@@ -583,10 +593,13 @@ function buildSshConfig(host: SSHHostWithCredentials): ConnectConfig {
|
||||
.replace(/\r\n/g, "\n")
|
||||
.replace(/\r/g, "\n");
|
||||
|
||||
(base as any).privateKey = Buffer.from(cleanKey, "utf8");
|
||||
(base as Record<string, unknown>).privateKey = Buffer.from(
|
||||
cleanKey,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
if (host.keyPassword) {
|
||||
(base as any).passphrase = host.keyPassword;
|
||||
(base as Record<string, unknown>).passphrase = host.keyPassword;
|
||||
}
|
||||
} catch (keyError) {
|
||||
statsLogger.error(
|
||||
@@ -684,178 +697,383 @@ async function collectMetrics(host: SSHHostWithCredentials): Promise<{
|
||||
percent: number | null;
|
||||
usedHuman: string | null;
|
||||
totalHuman: string | null;
|
||||
availableHuman: string | null;
|
||||
};
|
||||
network: {
|
||||
interfaces: Array<{
|
||||
name: string;
|
||||
ip: string;
|
||||
state: string;
|
||||
rxBytes: string | null;
|
||||
txBytes: string | null;
|
||||
}>;
|
||||
};
|
||||
uptime: {
|
||||
seconds: number | null;
|
||||
formatted: string | null;
|
||||
};
|
||||
processes: {
|
||||
total: number | null;
|
||||
running: number | null;
|
||||
top: Array<{
|
||||
pid: string;
|
||||
user: string;
|
||||
cpu: string;
|
||||
mem: string;
|
||||
command: string;
|
||||
}>;
|
||||
};
|
||||
system: {
|
||||
hostname: string | null;
|
||||
kernel: string | null;
|
||||
os: string | null;
|
||||
};
|
||||
}> {
|
||||
const cached = metricsCache.get(host.id);
|
||||
if (cached) {
|
||||
return cached;
|
||||
return cached as ReturnType<typeof collectMetrics> extends Promise<infer T>
|
||||
? T
|
||||
: never;
|
||||
}
|
||||
|
||||
return requestQueue.queueRequest(host.id, async () => {
|
||||
try {
|
||||
return await withSshConnection(host, async (client) => {
|
||||
let cpuPercent: number | null = null;
|
||||
let cores: number | null = null;
|
||||
let loadTriplet: [number, number, number] | null = null;
|
||||
let cpuPercent: number | null = null;
|
||||
let cores: number | null = null;
|
||||
let loadTriplet: [number, number, number] | null = null;
|
||||
|
||||
try {
|
||||
const [stat1, loadAvgOut, coresOut] = await Promise.all([
|
||||
execCommand(client, "cat /proc/stat"),
|
||||
execCommand(client, "cat /proc/loadavg"),
|
||||
execCommand(
|
||||
client,
|
||||
"nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo",
|
||||
),
|
||||
]);
|
||||
try {
|
||||
const [stat1, loadAvgOut, coresOut] = await Promise.all([
|
||||
execCommand(client, "cat /proc/stat"),
|
||||
execCommand(client, "cat /proc/loadavg"),
|
||||
execCommand(
|
||||
client,
|
||||
"nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo",
|
||||
),
|
||||
]);
|
||||
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
const stat2 = await execCommand(client, "cat /proc/stat");
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
const stat2 = await execCommand(client, "cat /proc/stat");
|
||||
|
||||
const cpuLine1 = (
|
||||
stat1.stdout.split("\n").find((l) => l.startsWith("cpu ")) || ""
|
||||
).trim();
|
||||
const cpuLine2 = (
|
||||
stat2.stdout.split("\n").find((l) => l.startsWith("cpu ")) || ""
|
||||
).trim();
|
||||
const a = parseCpuLine(cpuLine1);
|
||||
const b = parseCpuLine(cpuLine2);
|
||||
if (a && b) {
|
||||
const totalDiff = b.total - a.total;
|
||||
const idleDiff = b.idle - a.idle;
|
||||
const used = totalDiff - idleDiff;
|
||||
if (totalDiff > 0)
|
||||
cpuPercent = Math.max(0, Math.min(100, (used / totalDiff) * 100));
|
||||
}
|
||||
|
||||
const laParts = loadAvgOut.stdout.trim().split(/\s+/);
|
||||
if (laParts.length >= 3) {
|
||||
loadTriplet = [
|
||||
Number(laParts[0]),
|
||||
Number(laParts[1]),
|
||||
Number(laParts[2]),
|
||||
].map((v) => (Number.isFinite(v) ? Number(v) : 0)) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
];
|
||||
}
|
||||
|
||||
const coresNum = Number((coresOut.stdout || "").trim());
|
||||
cores = Number.isFinite(coresNum) && coresNum > 0 ? coresNum : null;
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect CPU metrics for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
cpuPercent = null;
|
||||
cores = null;
|
||||
loadTriplet = null;
|
||||
}
|
||||
|
||||
let memPercent: number | null = null;
|
||||
let usedGiB: number | null = null;
|
||||
let totalGiB: number | null = null;
|
||||
try {
|
||||
const memInfo = await execCommand(client, "cat /proc/meminfo");
|
||||
const lines = memInfo.stdout.split("\n");
|
||||
const getVal = (key: string) => {
|
||||
const line = lines.find((l) => l.startsWith(key));
|
||||
if (!line) return null;
|
||||
const m = line.match(/\d+/);
|
||||
return m ? Number(m[0]) : null;
|
||||
};
|
||||
const totalKb = getVal("MemTotal:");
|
||||
const availKb = getVal("MemAvailable:");
|
||||
if (totalKb && availKb && totalKb > 0) {
|
||||
const usedKb = totalKb - availKb;
|
||||
memPercent = Math.max(0, Math.min(100, (usedKb / totalKb) * 100));
|
||||
usedGiB = kibToGiB(usedKb);
|
||||
totalGiB = kibToGiB(totalKb);
|
||||
}
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect memory metrics for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
memPercent = null;
|
||||
usedGiB = null;
|
||||
totalGiB = null;
|
||||
}
|
||||
|
||||
let diskPercent: number | null = null;
|
||||
let usedHuman: string | null = null;
|
||||
let totalHuman: string | null = null;
|
||||
let availableHuman: string | null = null;
|
||||
try {
|
||||
const [diskOutHuman, diskOutBytes] = await Promise.all([
|
||||
execCommand(client, "df -h -P / | tail -n +2"),
|
||||
execCommand(client, "df -B1 -P / | tail -n +2"),
|
||||
]);
|
||||
|
||||
const humanLine =
|
||||
diskOutHuman.stdout
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean)[0] || "";
|
||||
const bytesLine =
|
||||
diskOutBytes.stdout
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean)[0] || "";
|
||||
|
||||
const humanParts = humanLine.split(/\s+/);
|
||||
const bytesParts = bytesLine.split(/\s+/);
|
||||
|
||||
if (humanParts.length >= 6 && bytesParts.length >= 6) {
|
||||
totalHuman = humanParts[1] || null;
|
||||
usedHuman = humanParts[2] || null;
|
||||
availableHuman = humanParts[3] || null;
|
||||
|
||||
const totalBytes = Number(bytesParts[1]);
|
||||
const usedBytes = Number(bytesParts[2]);
|
||||
|
||||
if (
|
||||
Number.isFinite(totalBytes) &&
|
||||
Number.isFinite(usedBytes) &&
|
||||
totalBytes > 0
|
||||
) {
|
||||
diskPercent = Math.max(
|
||||
0,
|
||||
Math.min(100, (usedBytes / totalBytes) * 100),
|
||||
);
|
||||
const cpuLine1 = (
|
||||
stat1.stdout.split("\n").find((l) => l.startsWith("cpu ")) || ""
|
||||
).trim();
|
||||
const cpuLine2 = (
|
||||
stat2.stdout.split("\n").find((l) => l.startsWith("cpu ")) || ""
|
||||
).trim();
|
||||
const a = parseCpuLine(cpuLine1);
|
||||
const b = parseCpuLine(cpuLine2);
|
||||
if (a && b) {
|
||||
const totalDiff = b.total - a.total;
|
||||
const idleDiff = b.idle - a.idle;
|
||||
const used = totalDiff - idleDiff;
|
||||
if (totalDiff > 0)
|
||||
cpuPercent = Math.max(0, Math.min(100, (used / totalDiff) * 100));
|
||||
}
|
||||
|
||||
const laParts = loadAvgOut.stdout.trim().split(/\s+/);
|
||||
if (laParts.length >= 3) {
|
||||
loadTriplet = [
|
||||
Number(laParts[0]),
|
||||
Number(laParts[1]),
|
||||
Number(laParts[2]),
|
||||
].map((v) => (Number.isFinite(v) ? Number(v) : 0)) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
];
|
||||
}
|
||||
|
||||
const coresNum = Number((coresOut.stdout || "").trim());
|
||||
cores = Number.isFinite(coresNum) && coresNum > 0 ? coresNum : null;
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect CPU metrics for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
cpuPercent = null;
|
||||
cores = null;
|
||||
loadTriplet = null;
|
||||
}
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect disk metrics for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
diskPercent = null;
|
||||
usedHuman = null;
|
||||
totalHuman = null;
|
||||
availableHuman = null;
|
||||
}
|
||||
|
||||
const result = {
|
||||
cpu: { percent: toFixedNum(cpuPercent, 0), cores, load: loadTriplet },
|
||||
memory: {
|
||||
percent: toFixedNum(memPercent, 0),
|
||||
usedGiB: usedGiB ? toFixedNum(usedGiB, 2) : null,
|
||||
totalGiB: totalGiB ? toFixedNum(totalGiB, 2) : null,
|
||||
},
|
||||
disk: {
|
||||
percent: toFixedNum(diskPercent, 0),
|
||||
usedHuman,
|
||||
totalHuman,
|
||||
availableHuman,
|
||||
},
|
||||
};
|
||||
let memPercent: number | null = null;
|
||||
let usedGiB: number | null = null;
|
||||
let totalGiB: number | null = null;
|
||||
try {
|
||||
const memInfo = await execCommand(client, "cat /proc/meminfo");
|
||||
const lines = memInfo.stdout.split("\n");
|
||||
const getVal = (key: string) => {
|
||||
const line = lines.find((l) => l.startsWith(key));
|
||||
if (!line) return null;
|
||||
const m = line.match(/\d+/);
|
||||
return m ? Number(m[0]) : null;
|
||||
};
|
||||
const totalKb = getVal("MemTotal:");
|
||||
const availKb = getVal("MemAvailable:");
|
||||
if (totalKb && availKb && totalKb > 0) {
|
||||
const usedKb = totalKb - availKb;
|
||||
memPercent = Math.max(0, Math.min(100, (usedKb / totalKb) * 100));
|
||||
usedGiB = kibToGiB(usedKb);
|
||||
totalGiB = kibToGiB(totalKb);
|
||||
}
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect memory metrics for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
memPercent = null;
|
||||
usedGiB = null;
|
||||
totalGiB = null;
|
||||
}
|
||||
|
||||
metricsCache.set(host.id, result);
|
||||
return result;
|
||||
});
|
||||
let diskPercent: number | null = null;
|
||||
let usedHuman: string | null = null;
|
||||
let totalHuman: string | null = null;
|
||||
let availableHuman: string | null = null;
|
||||
try {
|
||||
const [diskOutHuman, diskOutBytes] = await Promise.all([
|
||||
execCommand(client, "df -h -P / | tail -n +2"),
|
||||
execCommand(client, "df -B1 -P / | tail -n +2"),
|
||||
]);
|
||||
|
||||
const humanLine =
|
||||
diskOutHuman.stdout
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean)[0] || "";
|
||||
const bytesLine =
|
||||
diskOutBytes.stdout
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean)[0] || "";
|
||||
|
||||
const humanParts = humanLine.split(/\s+/);
|
||||
const bytesParts = bytesLine.split(/\s+/);
|
||||
|
||||
if (humanParts.length >= 6 && bytesParts.length >= 6) {
|
||||
totalHuman = humanParts[1] || null;
|
||||
usedHuman = humanParts[2] || null;
|
||||
availableHuman = humanParts[3] || null;
|
||||
|
||||
const totalBytes = Number(bytesParts[1]);
|
||||
const usedBytes = Number(bytesParts[2]);
|
||||
|
||||
if (
|
||||
Number.isFinite(totalBytes) &&
|
||||
Number.isFinite(usedBytes) &&
|
||||
totalBytes > 0
|
||||
) {
|
||||
diskPercent = Math.max(
|
||||
0,
|
||||
Math.min(100, (usedBytes / totalBytes) * 100),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect disk metrics for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
diskPercent = null;
|
||||
usedHuman = null;
|
||||
totalHuman = null;
|
||||
availableHuman = null;
|
||||
}
|
||||
|
||||
// Collect network interfaces
|
||||
const interfaces: Array<{
|
||||
name: string;
|
||||
ip: string;
|
||||
state: string;
|
||||
rxBytes: string | null;
|
||||
txBytes: string | null;
|
||||
}> = [];
|
||||
try {
|
||||
const ifconfigOut = await execCommand(
|
||||
client,
|
||||
"ip -o addr show | awk '{print $2,$4}' | grep -v '^lo'",
|
||||
);
|
||||
const netStatOut = await execCommand(
|
||||
client,
|
||||
"ip -o link show | awk '{print $2,$9}' | sed 's/:$//'",
|
||||
);
|
||||
|
||||
const addrs = ifconfigOut.stdout
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
const states = netStatOut.stdout
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const ifMap = new Map<string, { ip: string; state: string }>();
|
||||
for (const line of addrs) {
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 2) {
|
||||
const name = parts[0];
|
||||
const ip = parts[1].split("/")[0];
|
||||
if (!ifMap.has(name)) ifMap.set(name, { ip, state: "UNKNOWN" });
|
||||
}
|
||||
}
|
||||
for (const line of states) {
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 2) {
|
||||
const name = parts[0];
|
||||
const state = parts[1];
|
||||
const existing = ifMap.get(name);
|
||||
if (existing) {
|
||||
existing.state = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [name, data] of ifMap.entries()) {
|
||||
interfaces.push({
|
||||
name,
|
||||
ip: data.ip,
|
||||
state: data.state,
|
||||
rxBytes: null,
|
||||
txBytes: null,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect network metrics for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
// Collect uptime
|
||||
let uptimeSeconds: number | null = null;
|
||||
let uptimeFormatted: string | null = null;
|
||||
try {
|
||||
const uptimeOut = await execCommand(client, "cat /proc/uptime");
|
||||
const uptimeParts = uptimeOut.stdout.trim().split(/\s+/);
|
||||
if (uptimeParts.length >= 1) {
|
||||
uptimeSeconds = Number(uptimeParts[0]);
|
||||
if (Number.isFinite(uptimeSeconds)) {
|
||||
const days = Math.floor(uptimeSeconds / 86400);
|
||||
const hours = Math.floor((uptimeSeconds % 86400) / 3600);
|
||||
const minutes = Math.floor((uptimeSeconds % 3600) / 60);
|
||||
uptimeFormatted = `${days}d ${hours}h ${minutes}m`;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
statsLogger.warn(`Failed to collect uptime for host ${host.id}`, e);
|
||||
}
|
||||
|
||||
// Collect process information
|
||||
let totalProcesses: number | null = null;
|
||||
let runningProcesses: number | null = null;
|
||||
const topProcesses: Array<{
|
||||
pid: string;
|
||||
user: string;
|
||||
cpu: string;
|
||||
mem: string;
|
||||
command: string;
|
||||
}> = [];
|
||||
try {
|
||||
const psOut = await execCommand(
|
||||
client,
|
||||
"ps aux --sort=-%cpu | head -n 11",
|
||||
);
|
||||
const psLines = psOut.stdout
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
if (psLines.length > 1) {
|
||||
for (let i = 1; i < Math.min(psLines.length, 11); i++) {
|
||||
const parts = psLines[i].split(/\s+/);
|
||||
if (parts.length >= 11) {
|
||||
topProcesses.push({
|
||||
pid: parts[1],
|
||||
user: parts[0],
|
||||
cpu: parts[2],
|
||||
mem: parts[3],
|
||||
command: parts.slice(10).join(" ").substring(0, 50),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const procCount = await execCommand(client, "ps aux | wc -l");
|
||||
const runningCount = await execCommand(
|
||||
client,
|
||||
"ps aux | grep -c ' R '",
|
||||
);
|
||||
totalProcesses = Number(procCount.stdout.trim()) - 1;
|
||||
runningProcesses = Number(runningCount.stdout.trim());
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect process info for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
// Collect system information
|
||||
let hostname: string | null = null;
|
||||
let kernel: string | null = null;
|
||||
let os: string | null = null;
|
||||
try {
|
||||
const hostnameOut = await execCommand(client, "hostname");
|
||||
const kernelOut = await execCommand(client, "uname -r");
|
||||
const osOut = await execCommand(
|
||||
client,
|
||||
"cat /etc/os-release | grep '^PRETTY_NAME=' | cut -d'\"' -f2",
|
||||
);
|
||||
|
||||
hostname = hostnameOut.stdout.trim() || null;
|
||||
kernel = kernelOut.stdout.trim() || null;
|
||||
os = osOut.stdout.trim() || null;
|
||||
} catch (e) {
|
||||
statsLogger.warn(
|
||||
`Failed to collect system info for host ${host.id}`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
const result = {
|
||||
cpu: { percent: toFixedNum(cpuPercent, 0), cores, load: loadTriplet },
|
||||
memory: {
|
||||
percent: toFixedNum(memPercent, 0),
|
||||
usedGiB: usedGiB ? toFixedNum(usedGiB, 2) : null,
|
||||
totalGiB: totalGiB ? toFixedNum(totalGiB, 2) : null,
|
||||
},
|
||||
disk: {
|
||||
percent: toFixedNum(diskPercent, 0),
|
||||
usedHuman,
|
||||
totalHuman,
|
||||
availableHuman,
|
||||
},
|
||||
network: {
|
||||
interfaces,
|
||||
},
|
||||
uptime: {
|
||||
seconds: uptimeSeconds,
|
||||
formatted: uptimeFormatted,
|
||||
},
|
||||
processes: {
|
||||
total: totalProcesses,
|
||||
running: runningProcesses,
|
||||
top: topProcesses,
|
||||
},
|
||||
system: {
|
||||
hostname,
|
||||
kernel,
|
||||
os,
|
||||
},
|
||||
};
|
||||
|
||||
metricsCache.set(host.id, result);
|
||||
return result;
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes("TOTP authentication required")) {
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("TOTP authentication required")
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
@@ -909,8 +1127,6 @@ async function pollStatusesOnce(userId?: string): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const checks = hosts.map(async (h) => {
|
||||
const isOnline = await tcpPing(h.ip, h.port, 5000);
|
||||
const now = new Date().toISOString();
|
||||
@@ -936,7 +1152,7 @@ async function pollStatusesOnce(userId?: string): Promise<void> {
|
||||
}
|
||||
|
||||
app.get("/status", async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!SimpleDBOps.isUserDataUnlocked(userId)) {
|
||||
return res.status(401).json({
|
||||
@@ -957,7 +1173,7 @@ app.get("/status", async (req, res) => {
|
||||
|
||||
app.get("/status/:id", validateHostId, async (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!SimpleDBOps.isUserDataUnlocked(userId)) {
|
||||
return res.status(401).json({
|
||||
@@ -988,7 +1204,7 @@ app.get("/status/:id", validateHostId, async (req, res) => {
|
||||
});
|
||||
|
||||
app.post("/refresh", async (req, res) => {
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!SimpleDBOps.isUserDataUnlocked(userId)) {
|
||||
return res.status(401).json({
|
||||
@@ -1003,7 +1219,7 @@ app.post("/refresh", async (req, res) => {
|
||||
|
||||
app.get("/metrics/:id", validateHostId, async (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
const userId = (req as any).userId;
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
|
||||
if (!SimpleDBOps.isUserDataUnlocked(userId)) {
|
||||
return res.status(401).json({
|
||||
@@ -1024,7 +1240,16 @@ app.get("/metrics/:id", validateHostId, async (req, res) => {
|
||||
error: "Host is offline",
|
||||
cpu: { percent: null, cores: null, load: null },
|
||||
memory: { percent: null, usedGiB: null, totalGiB: null },
|
||||
disk: { percent: null, usedHuman: null, totalHuman: null },
|
||||
disk: {
|
||||
percent: null,
|
||||
usedHuman: null,
|
||||
totalHuman: null,
|
||||
availableHuman: null,
|
||||
},
|
||||
network: { interfaces: [] },
|
||||
uptime: { seconds: null, formatted: null },
|
||||
processes: { total: null, running: null, top: [] },
|
||||
system: { hostname: null, kernel: null, os: null },
|
||||
lastChecked: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
@@ -1032,13 +1257,25 @@ app.get("/metrics/:id", validateHostId, async (req, res) => {
|
||||
const metrics = await collectMetrics(host);
|
||||
res.json({ ...metrics, lastChecked: new Date().toISOString() });
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.message.includes("TOTP authentication required")) {
|
||||
if (
|
||||
err instanceof Error &&
|
||||
err.message.includes("TOTP authentication required")
|
||||
) {
|
||||
return res.status(403).json({
|
||||
error: "TOTP_REQUIRED",
|
||||
message: "Server Stats unavailable for TOTP-enabled servers",
|
||||
cpu: { percent: null, cores: null, load: null },
|
||||
memory: { percent: null, usedGiB: null, totalGiB: null },
|
||||
disk: { percent: null, usedHuman: null, totalHuman: null },
|
||||
disk: {
|
||||
percent: null,
|
||||
usedHuman: null,
|
||||
totalHuman: null,
|
||||
availableHuman: null,
|
||||
},
|
||||
network: { interfaces: [] },
|
||||
uptime: { seconds: null, formatted: null },
|
||||
processes: { total: null, running: null, top: [] },
|
||||
system: { hostname: null, kernel: null, os: null },
|
||||
lastChecked: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
@@ -1050,7 +1287,16 @@ app.get("/metrics/:id", validateHostId, async (req, res) => {
|
||||
error: "Metrics collection timeout",
|
||||
cpu: { percent: null, cores: null, load: null },
|
||||
memory: { percent: null, usedGiB: null, totalGiB: null },
|
||||
disk: { percent: null, usedHuman: null, totalHuman: null },
|
||||
disk: {
|
||||
percent: null,
|
||||
usedHuman: null,
|
||||
totalHuman: null,
|
||||
availableHuman: null,
|
||||
},
|
||||
network: { interfaces: [] },
|
||||
uptime: { seconds: null, formatted: null },
|
||||
processes: { total: null, running: null, top: [] },
|
||||
system: { hostname: null, kernel: null, os: null },
|
||||
lastChecked: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
@@ -1059,7 +1305,16 @@ app.get("/metrics/:id", validateHostId, async (req, res) => {
|
||||
error: "Failed to collect metrics",
|
||||
cpu: { percent: null, cores: null, load: null },
|
||||
memory: { percent: null, usedGiB: null, totalGiB: null },
|
||||
disk: { percent: null, usedHuman: null, totalHuman: null },
|
||||
disk: {
|
||||
percent: null,
|
||||
usedHuman: null,
|
||||
totalHuman: null,
|
||||
availableHuman: null,
|
||||
},
|
||||
network: { interfaces: [] },
|
||||
uptime: { seconds: null, formatted: null },
|
||||
processes: { total: null, running: null, top: [] },
|
||||
system: { hostname: null, kernel: null, os: null },
|
||||
lastChecked: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
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";
|
||||
@@ -9,6 +14,42 @@ import { SimpleDBOps } from "../utils/simple-db-ops.js";
|
||||
import { AuthManager } 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,6 @@ const wss = new WebSocketServer({
|
||||
|
||||
wss.on("connection", async (ws: WebSocket, req) => {
|
||||
let userId: string | undefined;
|
||||
let userPayload: any;
|
||||
|
||||
try {
|
||||
const url = parseUrl(req.url!, true);
|
||||
@@ -113,7 +153,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}
|
||||
|
||||
userId = payload.userId;
|
||||
userPayload = payload;
|
||||
} catch (error) {
|
||||
sshLogger.error(
|
||||
"WebSocket JWT verification failed during connection",
|
||||
@@ -187,9 +226,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 +242,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 +264,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 +323,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 +333,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
sshLogger.warn("Unknown message type received", {
|
||||
@@ -294,26 +344,8 @@ 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;
|
||||
}) {
|
||||
const { cols, rows, hostConfig, initialPath, executeCommand } = data;
|
||||
async function handleConnectToHost(data: ConnectToHostData) {
|
||||
const { hostConfig, initialPath, executeCommand } = data;
|
||||
const {
|
||||
id,
|
||||
ip,
|
||||
@@ -403,12 +435,19 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
if (credentials.length > 0) {
|
||||
const credential = credentials[0];
|
||||
resolvedCredentials = {
|
||||
password: credential.password,
|
||||
key:
|
||||
credential.private_key || credential.privateKey || credential.key,
|
||||
keyPassword: credential.key_password || credential.keyPassword,
|
||||
keyType: credential.key_type || credential.keyType,
|
||||
authType: credential.auth_type || credential.authType,
|
||||
password: credential.password as string | undefined,
|
||||
key: (credential.private_key ||
|
||||
credential.privateKey ||
|
||||
credential.key) as string | undefined,
|
||||
keyPassword: (credential.key_password || credential.keyPassword) as
|
||||
| string
|
||||
| undefined,
|
||||
keyType: (credential.key_type || credential.keyType) as
|
||||
| string
|
||||
| undefined,
|
||||
authType: (credential.auth_type || credential.authType) as
|
||||
| string
|
||||
| undefined,
|
||||
};
|
||||
} else {
|
||||
sshLogger.warn(`No credentials found for host ${id}`, {
|
||||
@@ -617,20 +656,25 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
);
|
||||
} else {
|
||||
if (resolvedCredentials.password) {
|
||||
const responses = prompts.map(() => resolvedCredentials.password || "");
|
||||
const responses = prompts.map(
|
||||
() => resolvedCredentials.password || "",
|
||||
);
|
||||
finish(responses);
|
||||
} else {
|
||||
sshLogger.warn("Keyboard-interactive requires password but none available", {
|
||||
operation: "ssh_keyboard_interactive_no_password",
|
||||
hostId: id,
|
||||
});
|
||||
sshLogger.warn(
|
||||
"Keyboard-interactive requires password but none available",
|
||||
{
|
||||
operation: "ssh_keyboard_interactive_no_password",
|
||||
hostId: id,
|
||||
},
|
||||
);
|
||||
finish(prompts.map(() => ""));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const connectConfig: any = {
|
||||
const connectConfig: ConnectConfig = {
|
||||
host: ip,
|
||||
port,
|
||||
username,
|
||||
@@ -638,9 +682,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",
|
||||
@@ -653,7 +694,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
LC_COLLATE: "en_US.UTF-8",
|
||||
COLORTERM: "truecolor",
|
||||
},
|
||||
|
||||
algorithms: {
|
||||
kex: [
|
||||
"diffie-hellman-group14-sha256",
|
||||
@@ -676,6 +716,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",
|
||||
@@ -714,13 +763,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(
|
||||
@@ -754,7 +796,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(
|
||||
@@ -776,8 +818,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;
|
||||
}
|
||||
@@ -785,8 +830,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;
|
||||
}
|
||||
@@ -797,8 +845,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,12 +515,17 @@ async function connectSSHTunnel(
|
||||
if (credentials.length > 0) {
|
||||
const credential = credentials[0];
|
||||
resolvedSourceCredentials = {
|
||||
password: credential.password,
|
||||
sshKey:
|
||||
credential.private_key || credential.privateKey || credential.key,
|
||||
keyPassword: credential.key_password || credential.keyPassword,
|
||||
keyType: credential.key_type || credential.keyType,
|
||||
authMethod: credential.auth_type || credential.authType,
|
||||
password: credential.password as string | undefined,
|
||||
sshKey: (credential.private_key ||
|
||||
credential.privateKey ||
|
||||
credential.key) as string | undefined,
|
||||
keyPassword: (credential.key_password || credential.keyPassword) as
|
||||
| string
|
||||
| undefined,
|
||||
keyType: (credential.key_type || credential.keyType) as
|
||||
| string
|
||||
| undefined,
|
||||
authMethod: (credential.auth_type || credential.authType) as string,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -593,12 +598,17 @@ async function connectSSHTunnel(
|
||||
if (credentials.length > 0) {
|
||||
const credential = credentials[0];
|
||||
resolvedEndpointCredentials = {
|
||||
password: credential.password,
|
||||
sshKey:
|
||||
credential.private_key || credential.privateKey || credential.key,
|
||||
keyPassword: credential.key_password || credential.keyPassword,
|
||||
keyType: credential.key_type || credential.keyType,
|
||||
authMethod: credential.auth_type || credential.authType,
|
||||
password: credential.password as string | undefined,
|
||||
sshKey: (credential.private_key ||
|
||||
credential.privateKey ||
|
||||
credential.key) as string | undefined,
|
||||
keyPassword: (credential.key_password || credential.keyPassword) as
|
||||
| string
|
||||
| undefined,
|
||||
keyType: (credential.key_type || credential.keyType) as
|
||||
| string
|
||||
| undefined,
|
||||
authMethod: (credential.auth_type || credential.authType) as string,
|
||||
};
|
||||
} else {
|
||||
tunnelLogger.warn("No endpoint credentials found in database", {
|
||||
@@ -827,7 +837,7 @@ async function connectSSHTunnel(
|
||||
}
|
||||
});
|
||||
|
||||
stream.stdout?.on("data", (data: Buffer) => {
|
||||
stream.stdout?.on("data", () => {
|
||||
// Silently consume stdout data
|
||||
});
|
||||
|
||||
@@ -893,7 +903,7 @@ async function connectSSHTunnel(
|
||||
});
|
||||
});
|
||||
|
||||
const connOptions: any = {
|
||||
const connOptions: Record<string, unknown> = {
|
||||
host: tunnelConfig.sourceIP,
|
||||
port: tunnelConfig.sourceSSHPort,
|
||||
username: tunnelConfig.sourceUsername,
|
||||
@@ -1031,12 +1041,17 @@ async function killRemoteTunnelByMarker(
|
||||
if (credentials.length > 0) {
|
||||
const credential = credentials[0];
|
||||
resolvedSourceCredentials = {
|
||||
password: credential.password,
|
||||
sshKey:
|
||||
credential.private_key || credential.privateKey || credential.key,
|
||||
keyPassword: credential.key_password || credential.keyPassword,
|
||||
keyType: credential.key_type || credential.keyType,
|
||||
authMethod: credential.auth_type || credential.authType,
|
||||
password: credential.password as string | undefined,
|
||||
sshKey: (credential.private_key ||
|
||||
credential.privateKey ||
|
||||
credential.key) as string | undefined,
|
||||
keyPassword: (credential.key_password || credential.keyPassword) as
|
||||
| string
|
||||
| undefined,
|
||||
keyType: (credential.key_type || credential.keyType) as
|
||||
| string
|
||||
| undefined,
|
||||
authMethod: (credential.auth_type || credential.authType) as string,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1050,7 +1065,7 @@ async function killRemoteTunnelByMarker(
|
||||
}
|
||||
|
||||
const conn = new Client();
|
||||
const connOptions: any = {
|
||||
const connOptions: Record<string, unknown> = {
|
||||
host: tunnelConfig.sourceIP,
|
||||
port: tunnelConfig.sourceSSHPort,
|
||||
username: tunnelConfig.sourceUsername,
|
||||
@@ -1424,14 +1439,6 @@ async function initializeAutoStartTunnels(): Promise<void> {
|
||||
isPinned: host.pin,
|
||||
};
|
||||
|
||||
const hasSourcePassword = host.autostartPassword;
|
||||
const hasSourceKey = host.autostartKey;
|
||||
const hasEndpointPassword =
|
||||
tunnelConnection.endpointPassword ||
|
||||
endpointHost.autostartPassword;
|
||||
const hasEndpointKey =
|
||||
tunnelConnection.endpointKey || endpointHost.autostartKey;
|
||||
|
||||
autoStartTunnels.push(tunnelConfig);
|
||||
} else {
|
||||
tunnelLogger.error(
|
||||
@@ -1454,10 +1461,10 @@ async function initializeAutoStartTunnels(): Promise<void> {
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
tunnelLogger.error(
|
||||
"Failed to initialize auto-start tunnels:",
|
||||
error.message,
|
||||
error instanceof Error ? error.message : "Unknown error",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ import { systemLogger, versionLogger } from "./utils/logger.js";
|
||||
version = foundVersion;
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ import { systemLogger, versionLogger } from "./utils/logger.js";
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on("unhandledRejection", (reason, promise) => {
|
||||
process.on("unhandledRejection", (reason) => {
|
||||
systemLogger.error("Unhandled promise rejection", reason, {
|
||||
operation: "error_handling",
|
||||
});
|
||||
|
||||
@@ -23,6 +23,18 @@ interface JWTPayload {
|
||||
exp?: number;
|
||||
}
|
||||
|
||||
interface AuthenticatedRequest extends Request {
|
||||
userId?: string;
|
||||
pendingTOTP?: boolean;
|
||||
dataKey?: Buffer;
|
||||
}
|
||||
|
||||
interface RequestWithHeaders extends Request {
|
||||
headers: Request["headers"] & {
|
||||
"x-forwarded-proto"?: string;
|
||||
};
|
||||
}
|
||||
|
||||
class AuthManager {
|
||||
private static instance: AuthManager;
|
||||
private systemCrypto: SystemCrypto;
|
||||
@@ -163,7 +175,10 @@ class AuthManager {
|
||||
});
|
||||
}
|
||||
|
||||
getSecureCookieOptions(req: any, maxAge: number = 24 * 60 * 60 * 1000) {
|
||||
getSecureCookieOptions(
|
||||
req: RequestWithHeaders,
|
||||
maxAge: number = 24 * 60 * 60 * 1000,
|
||||
) {
|
||||
return {
|
||||
httpOnly: false,
|
||||
secure: req.secure || req.headers["x-forwarded-proto"] === "https",
|
||||
@@ -175,10 +190,11 @@ class AuthManager {
|
||||
|
||||
createAuthMiddleware() {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
let token = req.cookies?.jwt;
|
||||
const authReq = req as AuthenticatedRequest;
|
||||
let token = authReq.cookies?.jwt;
|
||||
|
||||
if (!token) {
|
||||
const authHeader = req.headers["authorization"];
|
||||
const authHeader = authReq.headers["authorization"];
|
||||
if (authHeader?.startsWith("Bearer ")) {
|
||||
token = authHeader.split(" ")[1];
|
||||
}
|
||||
@@ -194,15 +210,16 @@ class AuthManager {
|
||||
return res.status(401).json({ error: "Invalid token" });
|
||||
}
|
||||
|
||||
(req as any).userId = payload.userId;
|
||||
(req as any).pendingTOTP = payload.pendingTOTP;
|
||||
authReq.userId = payload.userId;
|
||||
authReq.pendingTOTP = payload.pendingTOTP;
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
createDataAccessMiddleware() {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const userId = (req as any).userId;
|
||||
const authReq = req as AuthenticatedRequest;
|
||||
const userId = authReq.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: "Authentication required" });
|
||||
}
|
||||
@@ -215,7 +232,7 @@ class AuthManager {
|
||||
});
|
||||
}
|
||||
|
||||
(req as any).dataKey = dataKey;
|
||||
authReq.dataKey = dataKey;
|
||||
next();
|
||||
};
|
||||
}
|
||||
@@ -256,8 +273,9 @@ class AuthManager {
|
||||
return res.status(403).json({ error: "Admin access required" });
|
||||
}
|
||||
|
||||
(req as any).userId = payload.userId;
|
||||
(req as any).pendingTOTP = payload.pendingTOTP;
|
||||
const authReq = req as AuthenticatedRequest;
|
||||
authReq.userId = payload.userId;
|
||||
authReq.pendingTOTP = payload.pendingTOTP;
|
||||
next();
|
||||
} catch (error) {
|
||||
databaseLogger.error("Failed to verify admin privileges", error, {
|
||||
|
||||
@@ -101,7 +101,7 @@ export class AutoSSLSetup {
|
||||
try {
|
||||
try {
|
||||
execSync("openssl version", { stdio: "pipe" });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
throw new Error(
|
||||
"OpenSSL is not installed or not available in PATH. Please install OpenSSL to enable SSL certificate generation.",
|
||||
);
|
||||
|
||||
@@ -3,6 +3,19 @@ import { LazyFieldEncryption } from "./lazy-field-encryption.js";
|
||||
import { UserCrypto } from "./user-crypto.js";
|
||||
import { databaseLogger } from "./logger.js";
|
||||
|
||||
interface DatabaseInstance {
|
||||
prepare: (sql: string) => {
|
||||
all: (param?: unknown) => DatabaseRecord[];
|
||||
get: (param?: unknown) => DatabaseRecord;
|
||||
run: (...params: unknown[]) => unknown;
|
||||
};
|
||||
}
|
||||
|
||||
interface DatabaseRecord {
|
||||
id: number | string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
class DataCrypto {
|
||||
private static userCrypto: UserCrypto;
|
||||
|
||||
@@ -10,13 +23,13 @@ class DataCrypto {
|
||||
this.userCrypto = UserCrypto.getInstance();
|
||||
}
|
||||
|
||||
static encryptRecord(
|
||||
static encryptRecord<T extends Record<string, unknown>>(
|
||||
tableName: string,
|
||||
record: any,
|
||||
record: T,
|
||||
userId: string,
|
||||
userDataKey: Buffer,
|
||||
): any {
|
||||
const encryptedRecord = { ...record };
|
||||
): T {
|
||||
const encryptedRecord: Record<string, unknown> = { ...record };
|
||||
const recordId = record.id || "temp-" + Date.now();
|
||||
|
||||
for (const [fieldName, value] of Object.entries(record)) {
|
||||
@@ -24,24 +37,24 @@ class DataCrypto {
|
||||
encryptedRecord[fieldName] = FieldCrypto.encryptField(
|
||||
value as string,
|
||||
userDataKey,
|
||||
recordId,
|
||||
recordId as string,
|
||||
fieldName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return encryptedRecord;
|
||||
return encryptedRecord as T;
|
||||
}
|
||||
|
||||
static decryptRecord(
|
||||
static decryptRecord<T extends Record<string, unknown>>(
|
||||
tableName: string,
|
||||
record: any,
|
||||
record: T,
|
||||
userId: string,
|
||||
userDataKey: Buffer,
|
||||
): any {
|
||||
): T {
|
||||
if (!record) return record;
|
||||
|
||||
const decryptedRecord = { ...record };
|
||||
const decryptedRecord: Record<string, unknown> = { ...record };
|
||||
const recordId = record.id;
|
||||
|
||||
for (const [fieldName, value] of Object.entries(record)) {
|
||||
@@ -49,21 +62,21 @@ class DataCrypto {
|
||||
decryptedRecord[fieldName] = LazyFieldEncryption.safeGetFieldValue(
|
||||
value as string,
|
||||
userDataKey,
|
||||
recordId,
|
||||
recordId as string,
|
||||
fieldName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return decryptedRecord;
|
||||
return decryptedRecord as T;
|
||||
}
|
||||
|
||||
static decryptRecords(
|
||||
static decryptRecords<T extends Record<string, unknown>>(
|
||||
tableName: string,
|
||||
records: any[],
|
||||
records: T[],
|
||||
userId: string,
|
||||
userDataKey: Buffer,
|
||||
): any[] {
|
||||
): T[] {
|
||||
if (!Array.isArray(records)) return records;
|
||||
return records.map((record) =>
|
||||
this.decryptRecord(tableName, record, userId, userDataKey),
|
||||
@@ -73,7 +86,7 @@ class DataCrypto {
|
||||
static async migrateUserSensitiveFields(
|
||||
userId: string,
|
||||
userDataKey: Buffer,
|
||||
db: any,
|
||||
db: DatabaseInstance,
|
||||
): Promise<{
|
||||
migrated: boolean;
|
||||
migratedTables: string[];
|
||||
@@ -84,7 +97,7 @@ class DataCrypto {
|
||||
let migratedFieldsCount = 0;
|
||||
|
||||
try {
|
||||
const { needsMigration, plaintextFields } =
|
||||
const { needsMigration } =
|
||||
await LazyFieldEncryption.checkUserNeedsMigration(
|
||||
userId,
|
||||
userDataKey,
|
||||
@@ -97,7 +110,7 @@ class DataCrypto {
|
||||
|
||||
const sshDataRecords = db
|
||||
.prepare("SELECT * FROM ssh_data WHERE user_id = ?")
|
||||
.all(userId);
|
||||
.all(userId) as DatabaseRecord[];
|
||||
for (const record of sshDataRecords) {
|
||||
const sensitiveFields =
|
||||
LazyFieldEncryption.getSensitiveFieldsForTable("ssh_data");
|
||||
@@ -132,7 +145,7 @@ class DataCrypto {
|
||||
|
||||
const sshCredentialsRecords = db
|
||||
.prepare("SELECT * FROM ssh_credentials WHERE user_id = ?")
|
||||
.all(userId);
|
||||
.all(userId) as DatabaseRecord[];
|
||||
for (const record of sshCredentialsRecords) {
|
||||
const sensitiveFields =
|
||||
LazyFieldEncryption.getSensitiveFieldsForTable("ssh_credentials");
|
||||
@@ -169,7 +182,7 @@ class DataCrypto {
|
||||
|
||||
const userRecord = db
|
||||
.prepare("SELECT * FROM users WHERE id = ?")
|
||||
.get(userId);
|
||||
.get(userId) as DatabaseRecord | undefined;
|
||||
if (userRecord) {
|
||||
const sensitiveFields =
|
||||
LazyFieldEncryption.getSensitiveFieldsForTable("users");
|
||||
@@ -220,7 +233,7 @@ class DataCrypto {
|
||||
static async reencryptUserDataAfterPasswordReset(
|
||||
userId: string,
|
||||
newUserDataKey: Buffer,
|
||||
db: any,
|
||||
db: DatabaseInstance,
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
reencryptedTables: string[];
|
||||
@@ -262,17 +275,21 @@ class DataCrypto {
|
||||
try {
|
||||
const records = db
|
||||
.prepare(`SELECT * FROM ${table} WHERE user_id = ?`)
|
||||
.all(userId);
|
||||
.all(userId) as DatabaseRecord[];
|
||||
|
||||
for (const record of records) {
|
||||
const recordId = record.id.toString();
|
||||
const updatedRecord: DatabaseRecord = { ...record };
|
||||
let needsUpdate = false;
|
||||
const updatedRecord = { ...record };
|
||||
|
||||
for (const fieldName of fields) {
|
||||
const fieldValue = record[fieldName];
|
||||
|
||||
if (fieldValue && fieldValue.trim() !== "") {
|
||||
if (
|
||||
fieldValue &&
|
||||
typeof fieldValue === "string" &&
|
||||
fieldValue.trim() !== ""
|
||||
) {
|
||||
try {
|
||||
const reencryptedValue = FieldCrypto.encryptField(
|
||||
fieldValue,
|
||||
@@ -384,29 +401,29 @@ class DataCrypto {
|
||||
return userDataKey;
|
||||
}
|
||||
|
||||
static encryptRecordForUser(
|
||||
static encryptRecordForUser<T extends Record<string, unknown>>(
|
||||
tableName: string,
|
||||
record: any,
|
||||
record: T,
|
||||
userId: string,
|
||||
): any {
|
||||
): T {
|
||||
const userDataKey = this.validateUserAccess(userId);
|
||||
return this.encryptRecord(tableName, record, userId, userDataKey);
|
||||
}
|
||||
|
||||
static decryptRecordForUser(
|
||||
static decryptRecordForUser<T extends Record<string, unknown>>(
|
||||
tableName: string,
|
||||
record: any,
|
||||
record: T,
|
||||
userId: string,
|
||||
): any {
|
||||
): T {
|
||||
const userDataKey = this.validateUserAccess(userId);
|
||||
return this.decryptRecord(tableName, record, userId, userDataKey);
|
||||
}
|
||||
|
||||
static decryptRecordsForUser(
|
||||
static decryptRecordsForUser<T extends Record<string, unknown>>(
|
||||
tableName: string,
|
||||
records: any[],
|
||||
records: T[],
|
||||
userId: string,
|
||||
): any[] {
|
||||
): T[] {
|
||||
const userDataKey = this.validateUserAccess(userId);
|
||||
return this.decryptRecords(tableName, records, userId, userDataKey);
|
||||
}
|
||||
@@ -435,7 +452,7 @@ class DataCrypto {
|
||||
);
|
||||
|
||||
return decrypted === testData;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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([
|
||||
@@ -301,7 +309,6 @@ class DatabaseFileEncryption {
|
||||
const metadata: EncryptedFileMetadata = JSON.parse(metadataContent);
|
||||
|
||||
const fileStats = fs.statSync(encryptedPath);
|
||||
const currentFingerprint = "termix-v1-file";
|
||||
|
||||
return {
|
||||
version: metadata.version,
|
||||
|
||||
@@ -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<string, unknown>[];
|
||||
|
||||
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<string, unknown>[]) => {
|
||||
for (const row of dataRows) {
|
||||
const values = columns.map((col) => row[col]);
|
||||
insertStmt.run(values);
|
||||
|
||||
@@ -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<string, string> = {
|
||||
key_password: "keyPassword",
|
||||
@@ -182,12 +190,12 @@ export class LazyFieldEncryption {
|
||||
}
|
||||
|
||||
static migrateRecordSensitiveFields(
|
||||
record: any,
|
||||
record: Record<string, unknown>,
|
||||
sensitiveFields: string[],
|
||||
userKEK: Buffer,
|
||||
recordId: string,
|
||||
): {
|
||||
updatedRecord: any;
|
||||
updatedRecord: Record<string, unknown>;
|
||||
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<string, unknown> & { 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<string, unknown> & { 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,
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface LogContext {
|
||||
sessionId?: string;
|
||||
requestId?: string;
|
||||
duration?: number;
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const SENSITIVE_FIELDS = [
|
||||
|
||||
@@ -5,7 +5,8 @@ import type { SQLiteTable } from "drizzle-orm/sqlite-core";
|
||||
type TableName = "users" | "ssh_data" | "ssh_credentials";
|
||||
|
||||
class SimpleDBOps {
|
||||
static async insert<T extends Record<string, any>>(
|
||||
static async insert<T extends Record<string, unknown>>(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
table: SQLiteTable<any>,
|
||||
tableName: TableName,
|
||||
data: T,
|
||||
@@ -44,8 +45,8 @@ class SimpleDBOps {
|
||||
return decryptedResult as T;
|
||||
}
|
||||
|
||||
static async select<T extends Record<string, any>>(
|
||||
query: any,
|
||||
static async select<T extends Record<string, unknown>>(
|
||||
query: unknown,
|
||||
tableName: TableName,
|
||||
userId: string,
|
||||
): Promise<T[]> {
|
||||
@@ -56,9 +57,9 @@ class SimpleDBOps {
|
||||
|
||||
const results = await query;
|
||||
|
||||
const decryptedResults = DataCrypto.decryptRecords(
|
||||
const decryptedResults = DataCrypto.decryptRecords<T>(
|
||||
tableName,
|
||||
results,
|
||||
results as T[],
|
||||
userId,
|
||||
userDataKey,
|
||||
);
|
||||
@@ -66,8 +67,8 @@ class SimpleDBOps {
|
||||
return decryptedResults;
|
||||
}
|
||||
|
||||
static async selectOne<T extends Record<string, any>>(
|
||||
query: any,
|
||||
static async selectOne<T extends Record<string, unknown>>(
|
||||
query: unknown,
|
||||
tableName: TableName,
|
||||
userId: string,
|
||||
): Promise<T | undefined> {
|
||||
@@ -79,9 +80,9 @@ class SimpleDBOps {
|
||||
const result = await query;
|
||||
if (!result) return undefined;
|
||||
|
||||
const decryptedResult = DataCrypto.decryptRecord(
|
||||
const decryptedResult = DataCrypto.decryptRecord<T>(
|
||||
tableName,
|
||||
result,
|
||||
result as T,
|
||||
userId,
|
||||
userDataKey,
|
||||
);
|
||||
@@ -89,10 +90,11 @@ class SimpleDBOps {
|
||||
return decryptedResult;
|
||||
}
|
||||
|
||||
static async update<T extends Record<string, any>>(
|
||||
static async update<T extends Record<string, unknown>>(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
table: SQLiteTable<any>,
|
||||
tableName: TableName,
|
||||
where: any,
|
||||
where: unknown,
|
||||
data: Partial<T>,
|
||||
userId: string,
|
||||
): Promise<T[]> {
|
||||
@@ -108,7 +110,8 @@ class SimpleDBOps {
|
||||
const result = await getDb()
|
||||
.update(table)
|
||||
.set(encryptedData)
|
||||
.where(where)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
.where(where as any)
|
||||
.returning();
|
||||
|
||||
DatabaseSaveTrigger.triggerSave(`update_${tableName}`);
|
||||
@@ -124,12 +127,16 @@ class SimpleDBOps {
|
||||
}
|
||||
|
||||
static async delete(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
table: SQLiteTable<any>,
|
||||
tableName: TableName,
|
||||
where: any,
|
||||
_userId: string,
|
||||
): Promise<any[]> {
|
||||
const result = await getDb().delete(table).where(where).returning();
|
||||
where: unknown,
|
||||
): Promise<unknown[]> {
|
||||
const result = await getDb()
|
||||
.delete(table)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
.where(where as any)
|
||||
.returning();
|
||||
|
||||
DatabaseSaveTrigger.triggerSave(`delete_${tableName}`);
|
||||
|
||||
@@ -144,13 +151,10 @@ class SimpleDBOps {
|
||||
return DataCrypto.getUserDataKey(userId) !== null;
|
||||
}
|
||||
|
||||
static async selectEncrypted(
|
||||
query: any,
|
||||
_tableName: TableName,
|
||||
): Promise<any[]> {
|
||||
static async selectEncrypted(query: unknown): Promise<unknown[]> {
|
||||
const results = await query;
|
||||
|
||||
return results;
|
||||
return results as unknown[];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ function detectKeyTypeFromContent(keyContent: string): string {
|
||||
}
|
||||
|
||||
return "ssh-rsa";
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return "ssh-rsa";
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ export function parseSSHKey(
|
||||
} else {
|
||||
publicKey = "";
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
publicKey = "";
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ export function detectKeyType(privateKeyData: string): string {
|
||||
return "unknown";
|
||||
}
|
||||
return parsedKey.type || "unknown";
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ class UserCrypto {
|
||||
DEK.fill(0);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
await this.setupOIDCUserEncryption(userId);
|
||||
return true;
|
||||
}
|
||||
@@ -275,7 +275,7 @@ class UserCrypto {
|
||||
this.logoutUser(userId);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -298,7 +298,7 @@ class UserCrypto {
|
||||
DEK.fill(0);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -417,7 +417,7 @@ class UserCrypto {
|
||||
}
|
||||
|
||||
return JSON.parse(result[0].value);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -457,7 +457,7 @@ class UserCrypto {
|
||||
}
|
||||
|
||||
return JSON.parse(result[0].value);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ interface UserExportData {
|
||||
userId: string;
|
||||
username: string;
|
||||
userData: {
|
||||
sshHosts: any[];
|
||||
sshCredentials: any[];
|
||||
sshHosts: unknown[];
|
||||
sshCredentials: unknown[];
|
||||
fileManagerData: {
|
||||
recent: any[];
|
||||
pinned: any[];
|
||||
shortcuts: any[];
|
||||
recent: unknown[];
|
||||
pinned: unknown[];
|
||||
shortcuts: unknown[];
|
||||
};
|
||||
dismissedAlerts: any[];
|
||||
dismissedAlerts: unknown[];
|
||||
};
|
||||
metadata: {
|
||||
totalRecords: number;
|
||||
@@ -83,7 +83,7 @@ class UserDataExport {
|
||||
)
|
||||
: sshHosts;
|
||||
|
||||
let sshCredentialsData: any[] = [];
|
||||
let sshCredentialsData: unknown[] = [];
|
||||
if (includeCredentials) {
|
||||
const credentials = await getDb()
|
||||
.select()
|
||||
@@ -185,7 +185,10 @@ class UserDataExport {
|
||||
return JSON.stringify(exportData, null, pretty ? 2 : 0);
|
||||
}
|
||||
|
||||
static validateExportData(data: any): { valid: boolean; errors: string[] } {
|
||||
static validateExportData(data: unknown): {
|
||||
valid: boolean;
|
||||
errors: string[];
|
||||
} {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!data || typeof data !== "object") {
|
||||
@@ -193,23 +196,26 @@ class UserDataExport {
|
||||
return { valid: false, errors };
|
||||
}
|
||||
|
||||
if (!data.version) {
|
||||
const dataObj = data as Record<string, unknown>;
|
||||
|
||||
if (!dataObj.version) {
|
||||
errors.push("Missing version field");
|
||||
}
|
||||
|
||||
if (!data.userId) {
|
||||
if (!dataObj.userId) {
|
||||
errors.push("Missing userId field");
|
||||
}
|
||||
|
||||
if (!data.userData || typeof data.userData !== "object") {
|
||||
if (!dataObj.userData || typeof dataObj.userData !== "object") {
|
||||
errors.push("Missing or invalid userData field");
|
||||
}
|
||||
|
||||
if (!data.metadata || typeof data.metadata !== "object") {
|
||||
if (!dataObj.metadata || typeof dataObj.metadata !== "object") {
|
||||
errors.push("Missing or invalid metadata field");
|
||||
}
|
||||
|
||||
if (data.userData) {
|
||||
if (dataObj.userData) {
|
||||
const userData = dataObj.userData as Record<string, unknown>;
|
||||
const requiredFields = [
|
||||
"sshHosts",
|
||||
"sshCredentials",
|
||||
@@ -218,23 +224,24 @@ class UserDataExport {
|
||||
];
|
||||
for (const field of requiredFields) {
|
||||
if (
|
||||
!Array.isArray(data.userData[field]) &&
|
||||
!(
|
||||
field === "fileManagerData" &&
|
||||
typeof data.userData[field] === "object"
|
||||
)
|
||||
!Array.isArray(userData[field]) &&
|
||||
!(field === "fileManagerData" && typeof userData[field] === "object")
|
||||
) {
|
||||
errors.push(`Missing or invalid userData.${field} field`);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
data.userData.fileManagerData &&
|
||||
typeof data.userData.fileManagerData === "object"
|
||||
userData.fileManagerData &&
|
||||
typeof userData.fileManagerData === "object"
|
||||
) {
|
||||
const fileManagerData = userData.fileManagerData as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const fmFields = ["recent", "pinned", "shortcuts"];
|
||||
for (const field of fmFields) {
|
||||
if (!Array.isArray(data.userData.fileManagerData[field])) {
|
||||
if (!Array.isArray(fileManagerData[field])) {
|
||||
errors.push(
|
||||
`Missing or invalid userData.fileManagerData.${field} field`,
|
||||
);
|
||||
|
||||
@@ -89,7 +89,7 @@ class UserDataImport {
|
||||
) {
|
||||
const importStats = await this.importSshHosts(
|
||||
targetUserId,
|
||||
exportData.userData.sshHosts,
|
||||
exportData.userData.sshHosts as Record<string, unknown>[],
|
||||
{ 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<string, unknown>[],
|
||||
{ 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<string, unknown>[],
|
||||
{ replaceExisting, dryRun },
|
||||
);
|
||||
result.summary.dismissedAlertsImported = importStats.imported;
|
||||
@@ -159,7 +159,7 @@ class UserDataImport {
|
||||
|
||||
private static async importSshHosts(
|
||||
targetUserId: string,
|
||||
sshHosts: any[],
|
||||
sshHosts: Record<string, unknown>[],
|
||||
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<string, unknown>[],
|
||||
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<string, unknown>,
|
||||
options: { replaceExisting: boolean; dryRun: boolean },
|
||||
) {
|
||||
let imported = 0;
|
||||
@@ -356,7 +362,7 @@ class UserDataImport {
|
||||
|
||||
private static async importDismissedAlerts(
|
||||
targetUserId: string,
|
||||
alerts: any[],
|
||||
alerts: Record<string, unknown>[],
|
||||
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++;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
|
||||
type Theme = "dark" | "light" | "system";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
24
src/components/ui/chart.tsx
Normal file
24
src/components/ui/chart.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as React from "react";
|
||||
import * as RechartsPrimitive from "recharts";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// Chart Container
|
||||
const ChartContainer = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
ChartContainer.displayName = "ChartContainer";
|
||||
|
||||
export { ChartContainer, RechartsPrimitive };
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
|
||||
@@ -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<HTMLInputElement> {}
|
||||
type PasswordInputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
||||
|
||||
export const PasswordInput = React.forwardRef<
|
||||
HTMLInputElement,
|
||||
|
||||
@@ -17,10 +17,7 @@ export const Status = ({ className, status, ...props }: StatusProps) => (
|
||||
|
||||
export type StatusIndicatorProps = HTMLAttributes<HTMLSpanElement>;
|
||||
|
||||
export const StatusIndicator = ({
|
||||
className,
|
||||
...props
|
||||
}: StatusIndicatorProps) => (
|
||||
export const StatusIndicator = ({ ...props }: StatusIndicatorProps) => (
|
||||
<span className="relative flex h-2 w-2" {...props}>
|
||||
<span
|
||||
className={cn(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
@@ -8,13 +9,6 @@ import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from "@/components/ui/sheet";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
Tooltip,
|
||||
@@ -26,7 +20,6 @@ import {
|
||||
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
||||
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
||||
const SIDEBAR_WIDTH = "16rem";
|
||||
const SIDEBAR_WIDTH_MOBILE = "18rem";
|
||||
const SIDEBAR_WIDTH_ICON = "3rem";
|
||||
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
||||
|
||||
@@ -161,7 +154,7 @@ function Sidebar({
|
||||
variant?: "sidebar" | "floating" | "inset";
|
||||
collapsible?: "offcanvas" | "icon" | "none";
|
||||
}) {
|
||||
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
||||
const { state } = useSidebar();
|
||||
|
||||
if (collapsible === "none") {
|
||||
return (
|
||||
|
||||
@@ -8,7 +8,10 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
||||
|
||||
const originalToast = toast;
|
||||
|
||||
const rateLimitedToast = (message: string, options?: any) => {
|
||||
const rateLimitedToast = (
|
||||
message: string,
|
||||
options?: Record<string, unknown>,
|
||||
) => {
|
||||
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<string, unknown>) =>
|
||||
rateLimitedToast(message, { ...options, type: "success" }),
|
||||
error: (message: string, options?: any) =>
|
||||
error: (message: string, options?: Record<string, unknown>) =>
|
||||
rateLimitedToast(message, { ...options, type: "error" }),
|
||||
warning: (message: string, options?: any) =>
|
||||
warning: (message: string, options?: Record<string, unknown>) =>
|
||||
rateLimitedToast(message, { ...options, type: "warning" }),
|
||||
info: (message: string, options?: any) =>
|
||||
info: (message: string, options?: Record<string, unknown>) =>
|
||||
rateLimitedToast(message, { ...options, type: "info" }),
|
||||
message: rateLimitedToast,
|
||||
});
|
||||
|
||||
@@ -2,8 +2,7 @@ import * as React from "react";
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
||||
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { VersionAlert } from "@/components/ui/version-alert.tsx";
|
||||
import { RefreshCw, X } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { checkElectronUpdate, isElectron } from "@/ui/main-axios.ts";
|
||||
|
||||
interface VersionCheckModalProps {
|
||||
onDismiss: () => void;
|
||||
onContinue: () => void;
|
||||
isAuthenticated?: boolean;
|
||||
}
|
||||
|
||||
export function VersionCheckModal({
|
||||
onDismiss,
|
||||
onContinue,
|
||||
isAuthenticated = false,
|
||||
}: VersionCheckModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [versionInfo, setVersionInfo] = useState<any>(null);
|
||||
const [versionInfo, setVersionInfo] = useState<Record<
|
||||
string,
|
||||
unknown
|
||||
> | null>(null);
|
||||
const [versionChecking, setVersionChecking] = useState(false);
|
||||
const [versionDismissed, setVersionDismissed] = useState(false);
|
||||
const [versionDismissed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isElectron()) {
|
||||
@@ -47,10 +47,6 @@ export function VersionCheckModal({
|
||||
}
|
||||
};
|
||||
|
||||
const handleVersionDismiss = () => {
|
||||
setVersionDismissed(true);
|
||||
};
|
||||
|
||||
const handleDownloadUpdate = () => {
|
||||
if (versionInfo?.latest_release?.html_url) {
|
||||
window.open(versionInfo.latest_release.html_url, "_blank");
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface LogContext {
|
||||
errorCode?: string;
|
||||
errorMessage?: string;
|
||||
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
class FrontendLogger {
|
||||
@@ -218,7 +218,6 @@ class FrontendLogger {
|
||||
context?: LogContext,
|
||||
): void {
|
||||
const cleanUrl = this.sanitizeUrl(url);
|
||||
const shortUrl = this.getShortUrl(cleanUrl);
|
||||
const statusIcon = this.getStatusIcon(status);
|
||||
const performanceIcon = this.getPerformanceIcon(responseTime);
|
||||
|
||||
@@ -244,7 +243,6 @@ class FrontendLogger {
|
||||
context?: LogContext,
|
||||
): void {
|
||||
const cleanUrl = this.sanitizeUrl(url);
|
||||
const shortUrl = this.getShortUrl(cleanUrl);
|
||||
const statusIcon = this.getStatusIcon(status);
|
||||
|
||||
this.error(`← ${statusIcon} ${status} ${errorMessage}`, undefined, {
|
||||
@@ -265,7 +263,6 @@ class FrontendLogger {
|
||||
context?: LogContext,
|
||||
): void {
|
||||
const cleanUrl = this.sanitizeUrl(url);
|
||||
const shortUrl = this.getShortUrl(cleanUrl);
|
||||
|
||||
this.error(`🌐 Network Error: ${errorMessage}`, undefined, {
|
||||
...context,
|
||||
@@ -279,7 +276,6 @@ class FrontendLogger {
|
||||
|
||||
authError(method: string, url: string, context?: LogContext): void {
|
||||
const cleanUrl = this.sanitizeUrl(url);
|
||||
const shortUrl = this.getShortUrl(cleanUrl);
|
||||
|
||||
this.security(`🔐 Authentication Required`, {
|
||||
...context,
|
||||
@@ -298,7 +294,6 @@ class FrontendLogger {
|
||||
context?: LogContext,
|
||||
): void {
|
||||
const cleanUrl = this.sanitizeUrl(url);
|
||||
const shortUrl = this.getShortUrl(cleanUrl);
|
||||
|
||||
this.retry(`🔄 Retry ${attempt}/${maxAttempts}`, {
|
||||
...context,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -706,14 +706,34 @@
|
||||
"terminal": "Terminal",
|
||||
"tunnel": "Tunnel",
|
||||
"fileManager": "File Manager",
|
||||
"serverStats": "Server Stats",
|
||||
"hostViewer": "Host Viewer",
|
||||
"enableServerStats": "Enable Server Stats",
|
||||
"enableServerStatsDesc": "Enable/disable server statistics collection for this host",
|
||||
"displayItems": "Display Items",
|
||||
"displayItemsDesc": "Choose which metrics to display on the server stats page",
|
||||
"enableCpu": "CPU Usage",
|
||||
"enableMemory": "Memory Usage",
|
||||
"enableDisk": "Disk Usage",
|
||||
"enableNetwork": "Network Statistics (Coming Soon)",
|
||||
"enableProcesses": "Process Count (Coming Soon)",
|
||||
"enableUptime": "Uptime (Coming Soon)",
|
||||
"enableHostname": "Hostname (Coming Soon)",
|
||||
"enableOs": "Operating System (Coming Soon)",
|
||||
"customCommands": "Custom Commands (Coming Soon)",
|
||||
"customCommandsDesc": "Define custom shutdown and reboot commands for this server",
|
||||
"shutdownCommand": "Shutdown Command",
|
||||
"rebootCommand": "Reboot Command",
|
||||
"confirmRemoveFromFolder": "Are you sure you want to remove \"{{name}}\" from folder \"{{folder}}\"? The host will be moved to \"No Folder\".",
|
||||
"removedFromFolder": "Host \"{{name}}\" removed from folder successfully",
|
||||
"failedToRemoveFromFolder": "Failed to remove host from folder",
|
||||
"folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully",
|
||||
"failedToRenameFolder": "Failed to rename folder",
|
||||
"movedToFolder": "Host \"{{name}}\" moved to \"{{folder}}\" successfully",
|
||||
"failedToMoveToFolder": "Failed to move host to folder"
|
||||
"failedToMoveToFolder": "Failed to move host to folder",
|
||||
"statistics": "Statistics",
|
||||
"enabledWidgets": "Enabled Widgets",
|
||||
"enabledWidgetsDesc": "Select which statistics widgets to display for this host"
|
||||
},
|
||||
"terminal": {
|
||||
"title": "Terminal",
|
||||
@@ -1126,6 +1146,7 @@
|
||||
"loadAverageNA": "Avg: N/A",
|
||||
"cpuUsage": "CPU Usage",
|
||||
"memoryUsage": "Memory Usage",
|
||||
"diskUsage": "Disk Usage",
|
||||
"rootStorageSpace": "Root Storage Space",
|
||||
"of": "of",
|
||||
"feedbackMessage": "Have ideas for what should come next for server management? Share them on",
|
||||
@@ -1141,7 +1162,25 @@
|
||||
"totpUnavailable": "Server Stats unavailable for TOTP-enabled servers",
|
||||
"load": "Load",
|
||||
"free": "Free",
|
||||
"available": "Available"
|
||||
"available": "Available",
|
||||
"editLayout": "Edit Layout",
|
||||
"cancelEdit": "Cancel",
|
||||
"addWidget": "Add Widget",
|
||||
"saveLayout": "Save Layout",
|
||||
"unsavedChanges": "Unsaved changes",
|
||||
"layoutSaved": "Layout saved successfully",
|
||||
"failedToSaveLayout": "Failed to save layout",
|
||||
"systemInfo": "System Information",
|
||||
"hostname": "Hostname",
|
||||
"operatingSystem": "Operating System",
|
||||
"kernel": "Kernel",
|
||||
"totalUptime": "Total Uptime",
|
||||
"seconds": "seconds",
|
||||
"networkInterfaces": "Network Interfaces",
|
||||
"noInterfacesFound": "No network interfaces found",
|
||||
"totalProcesses": "Total Processes",
|
||||
"running": "Running",
|
||||
"noProcessesFound": "No processes found"
|
||||
},
|
||||
"auth": {
|
||||
"loginTitle": "Login to Termix",
|
||||
|
||||
@@ -728,13 +728,34 @@
|
||||
"terminal": "终端",
|
||||
"tunnel": "隧道",
|
||||
"fileManager": "文件管理器",
|
||||
"serverStats": "服务器统计",
|
||||
"hostViewer": "主机查看器",
|
||||
"enableServerStats": "启用服务器统计",
|
||||
"enableServerStatsDesc": "启用/禁用此主机的服务器统计信息收集",
|
||||
"displayItems": "显示项目",
|
||||
"displayItemsDesc": "选择在服务器统计页面上显示哪些指标",
|
||||
"enableCpu": "CPU使用率",
|
||||
"enableMemory": "内存使用率",
|
||||
"enableDisk": "磁盘使用率",
|
||||
"enableNetwork": "网络统计(即将推出)",
|
||||
"enableProcesses": "进程数(即将推出)",
|
||||
"enableUptime": "运行时间(即将推出)",
|
||||
"enableHostname": "主机名(即将推出)",
|
||||
"enableOs": "操作系统(即将推出)",
|
||||
"customCommands": "自定义命令(即将推出)",
|
||||
"customCommandsDesc": "为此服务器定义自定义关机和重启命令",
|
||||
"shutdownCommand": "关机命令",
|
||||
"rebootCommand": "重启命令",
|
||||
"confirmRemoveFromFolder": "确定要将\"{{name}}\"从文件夹\"{{folder}}\"中移除吗?主机将被移动到\"无文件夹\"。",
|
||||
"removedFromFolder": "主机\"{{name}}\"已成功从文件夹中移除",
|
||||
"failedToRemoveFromFolder": "从文件夹中移除主机失败",
|
||||
"folderRenamed": "文件夹\"{{oldName}}\"已成功重命名为\"{{newName}}\"",
|
||||
"failedToRenameFolder": "重命名文件夹失败",
|
||||
"movedToFolder": "主机\"{{name}}\"已成功移动到\"{{folder}}\"",
|
||||
"failedToMoveToFolder": "移动主机到文件夹失败"
|
||||
"failedToMoveToFolder": "移动主机到文件夹失败",
|
||||
"statistics": "统计",
|
||||
"enabledWidgets": "已启用组件",
|
||||
"enabledWidgetsDesc": "选择要为此主机显示的统计组件"
|
||||
},
|
||||
"terminal": {
|
||||
"title": "终端",
|
||||
@@ -1106,6 +1127,7 @@
|
||||
"loadAverageNA": "平均: N/A",
|
||||
"cpuUsage": "CPU 使用率",
|
||||
"memoryUsage": "内存使用率",
|
||||
"diskUsage": "磁盘使用率",
|
||||
"rootStorageSpace": "根目录存储空间",
|
||||
"of": "的",
|
||||
"feedbackMessage": "对服务器管理的下一步功能有想法?在这里分享吧",
|
||||
@@ -1118,7 +1140,27 @@
|
||||
"cannotFetchMetrics": "无法从离线服务器获取指标",
|
||||
"totpRequired": "需要 TOTP 认证",
|
||||
"totpUnavailable": "启用了 TOTP 的服务器无法使用服务器统计功能",
|
||||
"load": "负载"
|
||||
"load": "负载",
|
||||
"free": "空闲",
|
||||
"available": "可用",
|
||||
"editLayout": "编辑布局",
|
||||
"cancelEdit": "取消",
|
||||
"addWidget": "添加小组件",
|
||||
"saveLayout": "保存布局",
|
||||
"unsavedChanges": "有未保存的更改",
|
||||
"layoutSaved": "布局保存成功",
|
||||
"failedToSaveLayout": "保存布局失败",
|
||||
"systemInfo": "系统信息",
|
||||
"hostname": "主机名",
|
||||
"operatingSystem": "操作系统",
|
||||
"kernel": "内核",
|
||||
"totalUptime": "总运行时间",
|
||||
"seconds": "秒",
|
||||
"networkInterfaces": "网络接口",
|
||||
"noInterfacesFound": "未找到网络接口",
|
||||
"totalProcesses": "总进程数",
|
||||
"running": "运行中",
|
||||
"noProcessesFound": "未找到进程"
|
||||
},
|
||||
"auth": {
|
||||
"loginTitle": "登录 Termix",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { StrictMode, useEffect, useState, useRef } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./index.css";
|
||||
@@ -9,7 +10,6 @@ import { isElectron } from "./ui/main-axios.ts";
|
||||
|
||||
function useWindowWidth() {
|
||||
const [width, setWidth] = useState(window.innerWidth);
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
|
||||
const lastSwitchTime = useRef(0);
|
||||
const isCurrentlyMobile = useRef(window.innerWidth < 768);
|
||||
const hasSwitchedOnce = useRef(false);
|
||||
@@ -36,7 +36,6 @@ function useWindowWidth() {
|
||||
isCurrentlyMobile.current = newIsMobile;
|
||||
hasSwitchedOnce.current = true;
|
||||
setWidth(newWidth);
|
||||
setIsMobile(newIsMobile);
|
||||
} else {
|
||||
setWidth(newWidth);
|
||||
}
|
||||
|
||||
43
src/types/electron.d.ts
vendored
43
src/types/electron.d.ts
vendored
@@ -1,22 +1,49 @@
|
||||
interface ServerConfig {
|
||||
serverUrl?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface ConnectionTestResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DialogOptions {
|
||||
title?: string;
|
||||
defaultPath?: string;
|
||||
buttonLabel?: string;
|
||||
filters?: Array<{ name: string; extensions: string[] }>;
|
||||
properties?: string[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DialogResult {
|
||||
canceled: boolean;
|
||||
filePath?: string;
|
||||
filePaths?: string[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ElectronAPI {
|
||||
getAppVersion: () => Promise<string>;
|
||||
getPlatform: () => Promise<string>;
|
||||
|
||||
getServerConfig: () => Promise<any>;
|
||||
saveServerConfig: (config: any) => Promise<any>;
|
||||
testServerConnection: (serverUrl: string) => Promise<any>;
|
||||
getServerConfig: () => Promise<ServerConfig>;
|
||||
saveServerConfig: (config: ServerConfig) => Promise<{ success: boolean }>;
|
||||
testServerConnection: (serverUrl: string) => Promise<ConnectionTestResult>;
|
||||
|
||||
showSaveDialog: (options: any) => Promise<any>;
|
||||
showOpenDialog: (options: any) => Promise<any>;
|
||||
showSaveDialog: (options: DialogOptions) => Promise<DialogResult>;
|
||||
showOpenDialog: (options: DialogOptions) => Promise<DialogResult>;
|
||||
|
||||
onUpdateAvailable: (callback: Function) => void;
|
||||
onUpdateDownloaded: (callback: Function) => void;
|
||||
onUpdateAvailable: (callback: () => void) => void;
|
||||
onUpdateDownloaded: (callback: () => void) => void;
|
||||
|
||||
removeAllListeners: (channel: string) => void;
|
||||
isElectron: boolean;
|
||||
isDev: boolean;
|
||||
|
||||
invoke: (channel: string, ...args: any[]) => Promise<any>;
|
||||
invoke: (channel: string, ...args: unknown[]) => Promise<unknown>;
|
||||
|
||||
createTempFile: (fileData: {
|
||||
fileName: string;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// This file contains all shared interfaces and types used across the application
|
||||
|
||||
import type { Client } from "ssh2";
|
||||
import type { Request } from "express";
|
||||
|
||||
// ============================================================================
|
||||
// SSH HOST TYPES
|
||||
@@ -35,6 +36,7 @@ export interface SSHHost {
|
||||
enableFileManager: boolean;
|
||||
defaultPath: string;
|
||||
tunnelConnections: TunnelConnection[];
|
||||
statsConfig?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
@@ -57,7 +59,8 @@ export interface SSHHostData {
|
||||
enableTunnel?: boolean;
|
||||
enableFileManager?: boolean;
|
||||
defaultPath?: string;
|
||||
tunnelConnections?: any[];
|
||||
tunnelConnections?: TunnelConnection[];
|
||||
statsConfig?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -261,8 +264,8 @@ export interface TabContextTab {
|
||||
| "file_manager"
|
||||
|
|
||||
| "user_profile";
|
||||
title: string;
|
||||
hostConfig?: any;
|
||||
terminalRef?: React.RefObject<any>;
|
||||
hostConfig?: SSHHost;
|
||||
terminalRef?: React.RefObject<TerminalHandle>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -303,7 +306,7 @@ export type KeyType = "rsa" | "ecdsa" | "ed25519";
|
||||
// API RESPONSE TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
export interface ApiResponse<T = unknown> {
|
||||
data?: T;
|
||||
error?: string;
|
||||
message?: string;
|
||||
@@ -366,13 +369,13 @@ export interface SSHTunnelViewerProps {
|
||||
action: "connect" | "disconnect" | "cancel",
|
||||
host: SSHHost,
|
||||
tunnelIndex: number,
|
||||
) => Promise<any>
|
||||
) => Promise<void>
|
||||
>;
|
||||
onTunnelAction?: (
|
||||
action: "connect" | "disconnect" | "cancel",
|
||||
host: SSHHost,
|
||||
tunnelIndex: number,
|
||||
) => Promise<any>;
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface FileManagerProps {
|
||||
@@ -400,7 +403,7 @@ export interface SSHTunnelObjectProps {
|
||||
action: "connect" | "disconnect" | "cancel",
|
||||
host: SSHHost,
|
||||
tunnelIndex: number,
|
||||
) => Promise<any>;
|
||||
) => Promise<void>;
|
||||
compact?: boolean;
|
||||
bare?: boolean;
|
||||
}
|
||||
@@ -459,3 +462,95 @@ export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
export type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
||||
|
||||
export type PartialExcept<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||
|
||||
// ============================================================================
|
||||
// EXPRESS REQUEST TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface AuthenticatedRequest extends Request {
|
||||
userId: string;
|
||||
user?: {
|
||||
id: string;
|
||||
username: string;
|
||||
isAdmin: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GITHUB API TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface GitHubAsset {
|
||||
id: number;
|
||||
name: string;
|
||||
size: number;
|
||||
download_count: number;
|
||||
browser_download_url: string;
|
||||
}
|
||||
|
||||
export interface GitHubRelease {
|
||||
id: number;
|
||||
tag_name: string;
|
||||
name: string;
|
||||
body: string;
|
||||
published_at: string;
|
||||
html_url: string;
|
||||
assets: GitHubAsset[];
|
||||
prerelease: boolean;
|
||||
draft: boolean;
|
||||
}
|
||||
|
||||
export interface GitHubAPIResponse<T> {
|
||||
data: T;
|
||||
cached: boolean;
|
||||
cache_age?: number;
|
||||
timestamp?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CACHE TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface CacheEntry<T = unknown> {
|
||||
data: T;
|
||||
timestamp: number;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DATABASE EXPORT/IMPORT TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface ExportSummary {
|
||||
sshHostsImported: number;
|
||||
sshCredentialsImported: number;
|
||||
fileManagerItemsImported: number;
|
||||
dismissedAlertsImported: number;
|
||||
credentialUsageImported: number;
|
||||
settingsImported: number;
|
||||
skippedItems: number;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface ImportResult {
|
||||
success: boolean;
|
||||
summary: ExportSummary;
|
||||
}
|
||||
|
||||
export interface ExportRequestBody {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ImportRequestBody {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ExportPreviewBody {
|
||||
scope?: string;
|
||||
includeCredentials?: boolean;
|
||||
}
|
||||
|
||||
export interface RestoreRequestBody {
|
||||
backupPath: string;
|
||||
targetPath?: string;
|
||||
}
|
||||
|
||||
16
src/types/stats-widgets.ts
Normal file
16
src/types/stats-widgets.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export type WidgetType =
|
||||
| "cpu"
|
||||
| "memory"
|
||||
| "disk"
|
||||
| "network"
|
||||
| "uptime"
|
||||
| "processes"
|
||||
| "system";
|
||||
|
||||
export interface StatsConfig {
|
||||
enabledWidgets: WidgetType[];
|
||||
}
|
||||
|
||||
export const DEFAULT_STATS_CONFIG: StatsConfig = {
|
||||
enabledWidgets: ["cpu", "memory", "disk", "network", "uptime", "system"],
|
||||
};
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
Trash2,
|
||||
Users,
|
||||
Database,
|
||||
Key,
|
||||
Lock,
|
||||
Download,
|
||||
Upload,
|
||||
@@ -46,7 +45,6 @@ import {
|
||||
makeUserAdmin,
|
||||
removeAdminStatus,
|
||||
deleteUser,
|
||||
getCookie,
|
||||
isElectron,
|
||||
} from "@/ui/main-axios.ts";
|
||||
|
||||
@@ -96,8 +94,6 @@ export function AdminSettings({
|
||||
null,
|
||||
);
|
||||
|
||||
const [securityInitialized, setSecurityInitialized] = React.useState(true);
|
||||
|
||||
const [exportLoading, setExportLoading] = React.useState(false);
|
||||
const [importLoading, setImportLoading] = React.useState(false);
|
||||
const [importFile, setImportFile] = React.useState<File | null>(null);
|
||||
@@ -107,7 +103,8 @@ export function AdminSettings({
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isElectron()) {
|
||||
const serverUrl = (window as any).configuredServerUrl;
|
||||
const serverUrl = (window as { configuredServerUrl?: string })
|
||||
.configuredServerUrl;
|
||||
if (!serverUrl) {
|
||||
return;
|
||||
}
|
||||
@@ -127,7 +124,8 @@ export function AdminSettings({
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isElectron()) {
|
||||
const serverUrl = (window as any).configuredServerUrl;
|
||||
const serverUrl = (window as { configuredServerUrl?: string })
|
||||
.configuredServerUrl;
|
||||
if (!serverUrl) {
|
||||
return;
|
||||
}
|
||||
@@ -148,7 +146,8 @@ export function AdminSettings({
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isElectron()) {
|
||||
const serverUrl = (window as any).configuredServerUrl;
|
||||
const serverUrl = (window as { configuredServerUrl?: string })
|
||||
.configuredServerUrl;
|
||||
if (!serverUrl) {
|
||||
return;
|
||||
}
|
||||
@@ -169,7 +168,8 @@ export function AdminSettings({
|
||||
|
||||
const fetchUsers = async () => {
|
||||
if (isElectron()) {
|
||||
const serverUrl = (window as any).configuredServerUrl;
|
||||
const serverUrl = (window as { configuredServerUrl?: string })
|
||||
.configuredServerUrl;
|
||||
if (!serverUrl) {
|
||||
return;
|
||||
}
|
||||
@@ -234,9 +234,10 @@ export function AdminSettings({
|
||||
try {
|
||||
await updateOIDCConfig(oidcConfig);
|
||||
toast.success(t("admin.oidcConfigurationUpdated"));
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
setOidcError(
|
||||
err?.response?.data?.error || t("admin.failedToUpdateOidcConfig"),
|
||||
(err as { response?: { data?: { error?: string } } })?.response?.data
|
||||
?.error || t("admin.failedToUpdateOidcConfig"),
|
||||
);
|
||||
} finally {
|
||||
setOidcLoading(false);
|
||||
@@ -257,9 +258,10 @@ export function AdminSettings({
|
||||
toast.success(t("admin.userIsNowAdmin", { username: newAdminUsername }));
|
||||
setNewAdminUsername("");
|
||||
fetchUsers();
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
setMakeAdminError(
|
||||
err?.response?.data?.error || t("admin.failedToMakeUserAdmin"),
|
||||
(err as { response?: { data?: { error?: string } } })?.response?.data
|
||||
?.error || t("admin.failedToMakeUserAdmin"),
|
||||
);
|
||||
} finally {
|
||||
setMakeAdminLoading(false);
|
||||
@@ -272,7 +274,7 @@ export function AdminSettings({
|
||||
await removeAdminStatus(username);
|
||||
toast.success(t("admin.adminStatusRemoved", { username }));
|
||||
fetchUsers();
|
||||
} catch (err: any) {
|
||||
} catch {
|
||||
toast.error(t("admin.failedToRemoveAdminStatus"));
|
||||
}
|
||||
});
|
||||
@@ -286,7 +288,7 @@ export function AdminSettings({
|
||||
await deleteUser(username);
|
||||
toast.success(t("admin.userDeletedSuccessfully", { username }));
|
||||
fetchUsers();
|
||||
} catch (err: any) {
|
||||
} catch {
|
||||
toast.error(t("admin.failedToDeleteUser"));
|
||||
}
|
||||
},
|
||||
@@ -316,7 +318,7 @@ export function AdminSettings({
|
||||
window.location.hostname === "127.0.0.1");
|
||||
|
||||
const apiUrl = isElectron()
|
||||
? `${(window as any).configuredServerUrl}/database/export`
|
||||
? `${(window as { configuredServerUrl?: string }).configuredServerUrl}/database/export`
|
||||
: isDev
|
||||
? `http://localhost:30001/database/export`
|
||||
: `${window.location.protocol}//${window.location.host}/database/export`;
|
||||
@@ -357,7 +359,7 @@ export function AdminSettings({
|
||||
toast.error(error.error || t("admin.databaseExportFailed"));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("admin.databaseExportFailed"));
|
||||
} finally {
|
||||
setExportLoading(false);
|
||||
@@ -386,7 +388,7 @@ export function AdminSettings({
|
||||
window.location.hostname === "127.0.0.1");
|
||||
|
||||
const apiUrl = isElectron()
|
||||
? `${(window as any).configuredServerUrl}/database/import`
|
||||
? `${(window as { configuredServerUrl?: string }).configuredServerUrl}/database/import`
|
||||
: isDev
|
||||
? `http://localhost:30001/database/import`
|
||||
: `${window.location.protocol}//${window.location.host}/database/import`;
|
||||
@@ -449,7 +451,7 @@ export function AdminSettings({
|
||||
toast.error(error.error || t("admin.databaseImportFailed"));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("admin.databaseImportFailed"));
|
||||
} finally {
|
||||
setImportLoading(false);
|
||||
@@ -713,9 +715,13 @@ export function AdminSettings({
|
||||
try {
|
||||
await disableOIDCConfig();
|
||||
toast.success(t("admin.oidcConfigurationDisabled"));
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
setOidcError(
|
||||
err?.response?.data?.error ||
|
||||
(
|
||||
err as {
|
||||
response?: { data?: { error?: string } };
|
||||
}
|
||||
)?.response?.data?.error ||
|
||||
t("admin.failedToDisableOidcConfig"),
|
||||
);
|
||||
} finally {
|
||||
|
||||
@@ -42,9 +42,9 @@ export function CredentialEditor({
|
||||
onFormSubmit,
|
||||
}: CredentialEditorProps) {
|
||||
const { t } = useTranslation();
|
||||
const [credentials, setCredentials] = useState<Credential[]>([]);
|
||||
const [, setCredentials] = useState<Credential[]>([]);
|
||||
const [folders, setFolders] = useState<string[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [, setLoading] = useState(true);
|
||||
const [fullCredentialDetails, setFullCredentialDetails] =
|
||||
useState<Credential | null>(null);
|
||||
|
||||
@@ -95,7 +95,7 @@ export function CredentialEditor({
|
||||
try {
|
||||
const fullDetails = await getCredentialDetails(editingCredential.id);
|
||||
setFullCredentialDetails(fullDetails);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("credentials.failedToFetchCredentialDetails"));
|
||||
}
|
||||
} else {
|
||||
@@ -155,7 +155,9 @@ export function CredentialEditor({
|
||||
type FormData = z.infer<typeof formSchema>;
|
||||
|
||||
const form = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema) as any,
|
||||
resolver: zodResolver(formSchema) as unknown as Parameters<
|
||||
typeof useForm<FormData>
|
||||
>[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);
|
||||
|
||||
@@ -35,7 +35,7 @@ export function CredentialSelector({
|
||||
? data
|
||||
: data.credentials || data.data || [];
|
||||
setCredentials(credentialsArray);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
const { toast } = await import("sonner");
|
||||
toast.error(t("credentials.failedToFetchCredentials"));
|
||||
setCredentials([]);
|
||||
|
||||
@@ -70,7 +70,7 @@ const CredentialViewer: React.FC<CredentialViewerProps> = ({
|
||||
try {
|
||||
const response = await getCredentialDetails(credential.id);
|
||||
setCredentialDetails(response);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("credentials.failedToFetchCredentialDetails"));
|
||||
}
|
||||
};
|
||||
@@ -79,7 +79,7 @@ const CredentialViewer: React.FC<CredentialViewerProps> = ({
|
||||
try {
|
||||
const response = await getCredentialHosts(credential.id);
|
||||
setHostsUsing(response);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("credentials.failedToFetchHostsUsing"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -97,7 +97,7 @@ const CredentialViewer: React.FC<CredentialViewerProps> = ({
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
toast.success(t("copiedToClipboard", { field: fieldName }));
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("credentials.failedToCopy"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,21 +9,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from "@/components/ui/sheet";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Sheet, SheetContent } from "@/components/ui/sheet";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -37,7 +23,6 @@ import {
|
||||
Edit,
|
||||
Trash2,
|
||||
Shield,
|
||||
Pin,
|
||||
Tag,
|
||||
Info,
|
||||
FolderMinus,
|
||||
@@ -75,9 +60,7 @@ export function CredentialsManager({
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [showViewer, setShowViewer] = useState(false);
|
||||
const [viewingCredential, setViewingCredential] = useState<Credential | null>(
|
||||
null,
|
||||
);
|
||||
const [viewingCredential] = useState<Credential | null>(null);
|
||||
const [draggedCredential, setDraggedCredential] = useState<Credential | null>(
|
||||
null,
|
||||
);
|
||||
@@ -88,7 +71,15 @@ export function CredentialsManager({
|
||||
const [showDeployDialog, setShowDeployDialog] = useState(false);
|
||||
const [deployingCredential, setDeployingCredential] =
|
||||
useState<Credential | null>(null);
|
||||
const [availableHosts, setAvailableHosts] = useState<any[]>([]);
|
||||
const [availableHosts, setAvailableHosts] = useState<
|
||||
Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
ip: string;
|
||||
port: number;
|
||||
username: string;
|
||||
}>
|
||||
>([]);
|
||||
const [selectedHostId, setSelectedHostId] = useState<string>("");
|
||||
const [deployLoading, setDeployLoading] = useState(false);
|
||||
const [hostSearchQuery, setHostSearchQuery] = useState("");
|
||||
@@ -153,7 +144,7 @@ export function CredentialsManager({
|
||||
const data = await getCredentials();
|
||||
setCredentials(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
setError(t("credentials.failedToFetchCredentials"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -224,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"));
|
||||
@@ -256,7 +250,7 @@ export function CredentialsManager({
|
||||
);
|
||||
await fetchCredentials();
|
||||
window.dispatchEvent(new CustomEvent("credentials:changed"));
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("credentials.failedToRemoveFromFolder"));
|
||||
} finally {
|
||||
setOperationLoading(false);
|
||||
@@ -285,7 +279,7 @@ export function CredentialsManager({
|
||||
window.dispatchEvent(new CustomEvent("credentials:changed"));
|
||||
setEditingFolder(null);
|
||||
setEditingFolderName("");
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("credentials.failedToRenameFolder"));
|
||||
} finally {
|
||||
setOperationLoading(false);
|
||||
@@ -325,7 +319,7 @@ export function CredentialsManager({
|
||||
setDragOverFolder(folderName);
|
||||
};
|
||||
|
||||
const handleDragLeave = (e: React.DragEvent) => {
|
||||
const handleDragLeave = () => {
|
||||
dragCounter.current--;
|
||||
if (dragCounter.current === 0) {
|
||||
setDragOverFolder(null);
|
||||
@@ -359,7 +353,7 @@ export function CredentialsManager({
|
||||
);
|
||||
await fetchCredentials();
|
||||
window.dispatchEvent(new CustomEvent("credentials:changed"));
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("credentials.failedToMoveToFolder"));
|
||||
} finally {
|
||||
setOperationLoading(false);
|
||||
|
||||
@@ -23,8 +23,6 @@ import {
|
||||
Search,
|
||||
Grid3X3,
|
||||
List,
|
||||
Eye,
|
||||
Settings,
|
||||
} from "lucide-react";
|
||||
import { TerminalWindow } from "./components/TerminalWindow";
|
||||
import type { SSHHost, FileItem } from "../../../types/index.js";
|
||||
@@ -87,9 +85,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
const { t } = useTranslation();
|
||||
const { confirmWithToast } = useConfirmation();
|
||||
|
||||
const [currentHost, setCurrentHost] = useState<SSHHost | null>(
|
||||
initialHost || null,
|
||||
);
|
||||
const [currentHost] = useState<SSHHost | null>(initialHost || null);
|
||||
const [currentPath, setCurrentPath] = useState(
|
||||
initialHost?.defaultPath || "/",
|
||||
);
|
||||
@@ -145,10 +141,9 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
const [createIntent, setCreateIntent] = useState<CreateIntent | null>(null);
|
||||
const [editingFile, setEditingFile] = useState<FileItem | null>(null);
|
||||
|
||||
const { selectedFiles, selectFile, selectAll, clearSelection, setSelection } =
|
||||
useFileSelection();
|
||||
const { selectedFiles, clearSelection, setSelection } = useFileSelection();
|
||||
|
||||
const { isDragging, dragHandlers } = useDragAndDrop({
|
||||
const { dragHandlers } = useDragAndDrop({
|
||||
onFilesDropped: handleFilesDropped,
|
||||
onError: (error) => toast.error(error),
|
||||
maxFileSize: 5120,
|
||||
@@ -311,10 +306,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
setFiles(files);
|
||||
clearSelection();
|
||||
initialLoadDoneRef.current = true;
|
||||
} catch (dirError: any) {
|
||||
} catch (dirError: unknown) {
|
||||
console.error("Failed to load initial directory:", dirError);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error("SSH connection failed:", error);
|
||||
handleCloseWithError(
|
||||
t("fileManager.failedToConnect") + ": " + (error.message || error),
|
||||
@@ -353,7 +348,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
|
||||
setFiles(files);
|
||||
clearSelection();
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
if (currentLoadingPathRef.current === path) {
|
||||
console.error("Failed to load directory:", error);
|
||||
|
||||
@@ -535,7 +530,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
t("fileManager.fileUploadedSuccessfully", { name: file.name }),
|
||||
);
|
||||
handleRefreshDirectory();
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
toast.dismiss(progressToast);
|
||||
|
||||
if (
|
||||
@@ -584,7 +579,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
t("fileManager.fileDownloadedSuccessfully", { name: file.name }),
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
if (
|
||||
error.message?.includes("connection") ||
|
||||
error.message?.includes("established")
|
||||
@@ -665,7 +660,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
);
|
||||
handleRefreshDirectory();
|
||||
clearSelection();
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
if (
|
||||
error.message?.includes("connection") ||
|
||||
error.message?.includes("established")
|
||||
@@ -775,7 +770,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
component: createWindowComponent,
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
toast.error(
|
||||
error?.response?.data?.error ||
|
||||
error?.message ||
|
||||
@@ -784,7 +779,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
}
|
||||
};
|
||||
|
||||
async function handleFileOpen(file: FileItem, editMode: boolean = false) {
|
||||
async function handleFileOpen(file: FileItem) {
|
||||
if (file.type === "directory") {
|
||||
setCurrentPath(file.path);
|
||||
} else if (file.type === "link") {
|
||||
@@ -834,14 +829,6 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileEdit(file: FileItem) {
|
||||
handleFileOpen(file, true);
|
||||
}
|
||||
|
||||
function handleFileView(file: FileItem) {
|
||||
handleFileOpen(file, false);
|
||||
}
|
||||
|
||||
function handleContextMenu(event: React.MouseEvent, file?: FileItem) {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -914,7 +901,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error(`Failed to ${operation} file ${file.name}:`, error);
|
||||
toast.error(
|
||||
t("fileManager.operationFailed", {
|
||||
@@ -1015,7 +1002,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
if (operation === "cut") {
|
||||
setClipboard(null);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
toast.error(
|
||||
`${t("fileManager.pasteFailed")}: ${error.message || t("fileManager.unknownError")}`,
|
||||
);
|
||||
@@ -1050,7 +1037,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
currentHost?.userId?.toString(),
|
||||
);
|
||||
successCount++;
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
`Failed to delete copied file ${copiedFile.targetName}:`,
|
||||
error,
|
||||
@@ -1092,7 +1079,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
currentHost?.userId?.toString(),
|
||||
);
|
||||
successCount++;
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
`Failed to move back file ${movedFile.targetName}:`,
|
||||
error,
|
||||
@@ -1132,7 +1119,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
}
|
||||
|
||||
handleRefreshDirectory();
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
toast.error(
|
||||
`${t("fileManager.undoOperationFailed")}: ${error.message || t("fileManager.unknownError")}`,
|
||||
);
|
||||
@@ -1204,7 +1191,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
|
||||
setCreateIntent(null);
|
||||
handleRefreshDirectory();
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error("Create failed:", error);
|
||||
toast.error(t("fileManager.failedToCreateItem"));
|
||||
}
|
||||
@@ -1233,7 +1220,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
);
|
||||
setEditingFile(null);
|
||||
handleRefreshDirectory();
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error("Rename failed:", error);
|
||||
toast.error(t("fileManager.failedToRenameItem"));
|
||||
}
|
||||
@@ -1269,11 +1256,11 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
clearSelection();
|
||||
initialLoadDoneRef.current = true;
|
||||
toast.success(t("fileManager.connectedSuccessfully"));
|
||||
} catch (dirError: any) {
|
||||
} catch (dirError: unknown) {
|
||||
console.error("Failed to load initial directory:", dirError);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error("TOTP verification failed:", error);
|
||||
toast.error(t("fileManager.totpVerificationFailed"));
|
||||
} finally {
|
||||
@@ -1340,7 +1327,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
movedItems.push(file.name);
|
||||
successCount++;
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error(`Failed to move file ${file.name}:`, error);
|
||||
toast.error(
|
||||
t("fileManager.moveFileFailed", { name: file.name }) +
|
||||
@@ -1351,18 +1338,16 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
}
|
||||
|
||||
if (successCount > 0) {
|
||||
const movedFiles = draggedFiles
|
||||
.slice(0, successCount)
|
||||
.map((file, index) => {
|
||||
const targetPath = targetFolder.path.endsWith("/")
|
||||
? `${targetFolder.path}${file.name}`
|
||||
: `${targetFolder.path}/${file.name}`;
|
||||
return {
|
||||
originalPath: file.path,
|
||||
targetPath: targetPath,
|
||||
targetName: file.name,
|
||||
};
|
||||
});
|
||||
const movedFiles = draggedFiles.slice(0, successCount).map((file) => {
|
||||
const targetPath = targetFolder.path.endsWith("/")
|
||||
? `${targetFolder.path}${file.name}`
|
||||
: `${targetFolder.path}/${file.name}`;
|
||||
return {
|
||||
originalPath: file.path,
|
||||
targetPath: targetPath,
|
||||
targetName: file.name,
|
||||
};
|
||||
});
|
||||
|
||||
const undoAction: UndoAction = {
|
||||
type: "cut",
|
||||
@@ -1388,7 +1373,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
handleRefreshDirectory();
|
||||
clearSelection();
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error("Drag move operation failed:", error);
|
||||
toast.error(t("fileManager.moveOperationFailed") + ": " + error.message);
|
||||
}
|
||||
@@ -1459,7 +1444,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
await dragToDesktop.dragFilesToDesktop(files);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error("Drag to desktop failed:", error);
|
||||
toast.error(
|
||||
t("fileManager.dragFailed") +
|
||||
@@ -1554,7 +1539,9 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
|
||||
try {
|
||||
const pinnedData = await getPinnedFiles(currentHost.id);
|
||||
const pinnedPaths = new Set(pinnedData.map((item: any) => item.path));
|
||||
const pinnedPaths = new Set(
|
||||
pinnedData.map((item: Record<string, unknown>) => item.path),
|
||||
);
|
||||
setPinnedFiles(pinnedPaths);
|
||||
} catch (error) {
|
||||
console.error("Failed to load pinned files:", error);
|
||||
|
||||
@@ -13,8 +13,6 @@ import {
|
||||
RefreshCw,
|
||||
Clipboard,
|
||||
Eye,
|
||||
Share,
|
||||
ExternalLink,
|
||||
Terminal,
|
||||
Play,
|
||||
Star,
|
||||
@@ -190,7 +188,6 @@ export function FileManagerContextMenu({
|
||||
const isSingleFile = files.length === 1;
|
||||
const isMultipleFiles = files.length > 1;
|
||||
const hasFiles = files.some((f) => f.type === "file");
|
||||
const hasDirectories = files.some((f) => f.type === "directory");
|
||||
const hasExecutableFiles = files.some(
|
||||
(f) => f.type === "file" && f.executable,
|
||||
);
|
||||
|
||||
@@ -166,7 +166,6 @@ const getFileIcon = (file: FileItem, viewMode: "grid" | "list" = "grid") => {
|
||||
export function FileManagerGrid({
|
||||
files,
|
||||
selectedFiles,
|
||||
onFileSelect,
|
||||
onFileOpen,
|
||||
onSelectionChange,
|
||||
currentPath,
|
||||
@@ -188,7 +187,6 @@ export function FileManagerGrid({
|
||||
onUndo,
|
||||
onFileDrop,
|
||||
onFileDiff,
|
||||
onSystemDragStart,
|
||||
onSystemDragEnd,
|
||||
hasClipboard,
|
||||
createIntent,
|
||||
@@ -428,16 +426,6 @@ export function FileManagerGrid({
|
||||
setIsEditingPath(false);
|
||||
};
|
||||
|
||||
const handlePathInputKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
confirmEditingPath();
|
||||
} else if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
cancelEditingPath();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditingPath) {
|
||||
setEditPathValue(currentPath);
|
||||
@@ -617,7 +605,7 @@ export function FileManagerGrid({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleGlobalMouseUp = (e: MouseEvent) => {
|
||||
const handleGlobalMouseUp = () => {
|
||||
if (isSelecting) {
|
||||
setIsSelecting(false);
|
||||
setSelectionStart(null);
|
||||
|
||||
@@ -23,6 +23,35 @@ import {
|
||||
} from "@/ui/main-axios.ts";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface RecentFileData {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
lastOpened?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface PinnedFileData {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface ShortcutData {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DirectoryItemData {
|
||||
name: string;
|
||||
path: string;
|
||||
type: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface SidebarItem {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -37,7 +66,6 @@ interface FileManagerSidebarProps {
|
||||
currentHost: SSHHost;
|
||||
currentPath: string;
|
||||
onPathChange: (path: string) => void;
|
||||
onLoadDirectory?: (path: string) => void;
|
||||
onFileOpen?: (file: SidebarItem) => void;
|
||||
sshSessionId?: string;
|
||||
refreshTrigger?: number;
|
||||
@@ -47,7 +75,6 @@ export function FileManagerSidebar({
|
||||
currentHost,
|
||||
currentPath,
|
||||
onPathChange,
|
||||
onLoadDirectory,
|
||||
onFileOpen,
|
||||
sshSessionId,
|
||||
refreshTrigger,
|
||||
@@ -88,31 +115,37 @@ export function FileManagerSidebar({
|
||||
|
||||
try {
|
||||
const recentData = await getRecentFiles(currentHost.id);
|
||||
const recentItems = recentData.slice(0, 5).map((item: any) => ({
|
||||
id: `recent-${item.id}`,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: "recent" as const,
|
||||
lastAccessed: item.lastOpened,
|
||||
}));
|
||||
const recentItems = (recentData as RecentFileData[])
|
||||
.slice(0, 5)
|
||||
.map((item: RecentFileData) => ({
|
||||
id: `recent-${item.id}`,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: "recent" as const,
|
||||
lastAccessed: item.lastOpened,
|
||||
}));
|
||||
setRecentItems(recentItems);
|
||||
|
||||
const pinnedData = await getPinnedFiles(currentHost.id);
|
||||
const pinnedItems = pinnedData.map((item: any) => ({
|
||||
id: `pinned-${item.id}`,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: "pinned" as const,
|
||||
}));
|
||||
const pinnedItems = (pinnedData as PinnedFileData[]).map(
|
||||
(item: PinnedFileData) => ({
|
||||
id: `pinned-${item.id}`,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: "pinned" as const,
|
||||
}),
|
||||
);
|
||||
setPinnedItems(pinnedItems);
|
||||
|
||||
const shortcutData = await getFolderShortcuts(currentHost.id);
|
||||
const shortcutItems = shortcutData.map((item: any) => ({
|
||||
id: `shortcut-${item.id}`,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: "shortcut" as const,
|
||||
}));
|
||||
const shortcutItems = (shortcutData as ShortcutData[]).map(
|
||||
(item: ShortcutData) => ({
|
||||
id: `shortcut-${item.id}`,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: "shortcut" as const,
|
||||
}),
|
||||
);
|
||||
setShortcuts(shortcutItems);
|
||||
} catch (error) {
|
||||
console.error("Failed to load quick access data:", error);
|
||||
@@ -230,12 +263,12 @@ export function FileManagerSidebar({
|
||||
try {
|
||||
const response = await listSSHFiles(sshSessionId, "/");
|
||||
|
||||
const rootFiles = response.files || [];
|
||||
const rootFiles = (response.files || []) as DirectoryItemData[];
|
||||
const rootFolders = rootFiles.filter(
|
||||
(item: any) => item.type === "directory",
|
||||
(item: DirectoryItemData) => item.type === "directory",
|
||||
);
|
||||
|
||||
const rootTreeItems = rootFolders.map((folder: any) => ({
|
||||
const rootTreeItems = rootFolders.map((folder: DirectoryItemData) => ({
|
||||
id: `folder-${folder.name}`,
|
||||
name: folder.name,
|
||||
path: folder.path,
|
||||
@@ -298,12 +331,12 @@ export function FileManagerSidebar({
|
||||
try {
|
||||
const subResponse = await listSSHFiles(sshSessionId, folderPath);
|
||||
|
||||
const subFiles = subResponse.files || [];
|
||||
const subFiles = (subResponse.files || []) as DirectoryItemData[];
|
||||
const subFolders = subFiles.filter(
|
||||
(item: any) => item.type === "directory",
|
||||
(item: DirectoryItemData) => item.type === "directory",
|
||||
);
|
||||
|
||||
const subTreeItems = subFolders.map((folder: any) => ({
|
||||
const subTreeItems = subFolders.map((folder: DirectoryItemData) => ({
|
||||
id: `folder-${folder.path.replace(/\//g, "-")}`,
|
||||
name: folder.name,
|
||||
path: folder.path,
|
||||
|
||||
@@ -61,7 +61,7 @@ export function DiffViewer({
|
||||
userId: sshHost.userId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
await connectSSH(sshSessionId, {
|
||||
hostId: sshHost.id,
|
||||
ip: sshHost.ip,
|
||||
@@ -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")),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useRef, useCallback, useEffect } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Minus, Square, X, Maximize2, Minimize2 } from "lucide-react";
|
||||
import { Minus, X, Maximize2, Minimize2 } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface DraggableWindowProps {
|
||||
|
||||
@@ -60,7 +60,6 @@ import {
|
||||
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
||||
import { PhotoProvider, PhotoView } from "react-photo-view";
|
||||
import "react-photo-view/dist/react-photo-view.css";
|
||||
import ReactPlayer from "react-player";
|
||||
import AudioPlayer from "react-h5-audio-player";
|
||||
import "react-h5-audio-player/lib/styles.css";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
@@ -290,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));
|
||||
@@ -311,9 +310,7 @@ export function FileViewer({
|
||||
}: FileViewerProps) {
|
||||
const { t } = useTranslation();
|
||||
const [editedContent, setEditedContent] = useState(content);
|
||||
const [originalContent, setOriginalContent] = useState(
|
||||
savedContent || content,
|
||||
);
|
||||
const [, setOriginalContent] = useState(savedContent || content);
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [showLargeFileWarning, setShowLargeFileWarning] = useState(false);
|
||||
const [forceShowAsText, setForceShowAsText] = useState(false);
|
||||
@@ -326,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<any>(null);
|
||||
const editorRef = useRef<{
|
||||
view?: { dispatch: (transaction: unknown) => void };
|
||||
} | null>(null);
|
||||
|
||||
const fileTypeInfo = getFileType(file.name);
|
||||
|
||||
@@ -975,13 +974,7 @@ export function FileViewer({
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
code({
|
||||
node,
|
||||
inline,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}) {
|
||||
code({ inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(
|
||||
className || "",
|
||||
);
|
||||
@@ -1097,7 +1090,7 @@ export function FileViewer({
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
code({ inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
return !inline && match ? (
|
||||
<SyntaxHighlighter
|
||||
@@ -1380,8 +1373,7 @@ export function FileViewer({
|
||||
<div className="rounded-lg overflow-hidden">
|
||||
<AudioPlayer
|
||||
src={audioUrl}
|
||||
onLoadedMetadata={(e) => {
|
||||
const audio = e.currentTarget;
|
||||
onLoadedMetadata={() => {
|
||||
if (onMediaDimensionsChange) {
|
||||
onMediaDimensionsChange({
|
||||
width: 600,
|
||||
|
||||
@@ -56,7 +56,7 @@ export function FileWindow({
|
||||
initialY = 100,
|
||||
onFileNotFound,
|
||||
}: FileWindowProps) {
|
||||
const { closeWindow, maximizeWindow, focusWindow, updateWindow, windows } =
|
||||
const { closeWindow, maximizeWindow, focusWindow, windows } =
|
||||
useWindowManager();
|
||||
|
||||
const { t } = useTranslation();
|
||||
@@ -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"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,19 @@ export function TerminalWindow({
|
||||
executeCommand,
|
||||
}: TerminalWindowProps) {
|
||||
const { t } = useTranslation();
|
||||
const { closeWindow, minimizeWindow, maximizeWindow, focusWindow, windows } =
|
||||
const { closeWindow, maximizeWindow, focusWindow, windows } =
|
||||
useWindowManager();
|
||||
const terminalRef = React.useRef<any>(null);
|
||||
const terminalRef = React.useRef<{ fit?: () => void } | null>(null);
|
||||
const resizeTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (resizeTimeoutRef.current) {
|
||||
clearTimeout(resizeTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const currentWindow = windows.find((w) => w.id === windowId);
|
||||
if (!currentWindow) {
|
||||
return null;
|
||||
@@ -50,10 +58,6 @@ export function TerminalWindow({
|
||||
closeWindow(windowId);
|
||||
};
|
||||
|
||||
const handleMinimize = () => {
|
||||
minimizeWindow(windowId);
|
||||
};
|
||||
|
||||
const handleMaximize = () => {
|
||||
maximizeWindow(windowId);
|
||||
};
|
||||
@@ -74,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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import React, { useState, useCallback, useRef } from "react";
|
||||
|
||||
export interface WindowInstance {
|
||||
|
||||
@@ -15,14 +15,17 @@ import { useTranslation } from "react-i18next";
|
||||
import type { SSHHost, HostManagerProps } from "../../../types/index";
|
||||
|
||||
export function HostManager({
|
||||
onSelectView,
|
||||
isTopbarOpen,
|
||||
}: HostManagerProps): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState("host_viewer");
|
||||
const [editingHost, setEditingHost] = useState<SSHHost | null>(null);
|
||||
|
||||
const [editingCredential, setEditingCredential] = useState<any | null>(null);
|
||||
const [editingCredential, setEditingCredential] = useState<{
|
||||
id: number;
|
||||
name?: string;
|
||||
username: string;
|
||||
} | null>(null);
|
||||
const { state: sidebarState } = useSidebar();
|
||||
|
||||
const handleEditHost = (host: SSHHost) => {
|
||||
@@ -30,12 +33,16 @@ export function HostManager({
|
||||
setActiveTab("add_host");
|
||||
};
|
||||
|
||||
const handleFormSubmit = (updatedHost?: SSHHost) => {
|
||||
const handleFormSubmit = () => {
|
||||
setEditingHost(null);
|
||||
setActiveTab("host_viewer");
|
||||
};
|
||||
|
||||
const handleEditCredential = (credential: any) => {
|
||||
const handleEditCredential = (credential: {
|
||||
id: number;
|
||||
name?: string;
|
||||
username: string;
|
||||
}) => {
|
||||
setEditingCredential(credential);
|
||||
setActiveTab("add_credential");
|
||||
};
|
||||
|
||||
@@ -38,6 +38,9 @@ import { CredentialSelector } from "@/ui/Desktop/Apps/Credentials/CredentialSele
|
||||
import CodeMirror from "@uiw/react-codemirror";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import type { StatsConfig } from "@/types/stats-widgets";
|
||||
import { DEFAULT_STATS_CONFIG } from "@/types/stats-widgets";
|
||||
import { Checkbox } from "@/components/ui/checkbox.tsx";
|
||||
|
||||
interface SSHHost {
|
||||
id: number;
|
||||
@@ -57,7 +60,15 @@ 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;
|
||||
credentialId?: number;
|
||||
@@ -73,11 +84,11 @@ export function HostManagerEditor({
|
||||
onFormSubmit,
|
||||
}: SSHManagerHostEditorProps) {
|
||||
const { t } = useTranslation();
|
||||
const [hosts, setHosts] = useState<SSHHost[]>([]);
|
||||
const [folders, setFolders] = useState<string[]>([]);
|
||||
const [sshConfigurations, setSshConfigurations] = useState<string[]>([]);
|
||||
const [credentials, setCredentials] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [credentials, setCredentials] = useState<
|
||||
Array<{ id: number; username: string; authType: string }>
|
||||
>([]);
|
||||
|
||||
const [authTab, setAuthTab] = useState<"password" | "key" | "credential">(
|
||||
"password",
|
||||
@@ -92,12 +103,10 @@ export function HostManagerEditor({
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const [hostsData, credentialsData] = await Promise.all([
|
||||
getSSHHosts(),
|
||||
getCredentials(),
|
||||
]);
|
||||
setHosts(hostsData);
|
||||
setCredentials(credentialsData);
|
||||
|
||||
const uniqueFolders = [
|
||||
@@ -120,8 +129,6 @@ export function HostManagerEditor({
|
||||
setSshConfigurations(uniqueConfigurations);
|
||||
} catch {
|
||||
// Failed to load hosts data
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -131,9 +138,7 @@ export function HostManagerEditor({
|
||||
useEffect(() => {
|
||||
const handleCredentialChange = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const hostsData = await getSSHHosts();
|
||||
setHosts(hostsData);
|
||||
|
||||
const uniqueFolders = [
|
||||
...new Set(
|
||||
@@ -155,8 +160,6 @@ export function HostManagerEditor({
|
||||
setSshConfigurations(uniqueConfigurations);
|
||||
} catch {
|
||||
// Failed to reload hosts after credential change
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -210,6 +213,32 @@ export function HostManagerEditor({
|
||||
.default([]),
|
||||
enableFileManager: z.boolean().default(true),
|
||||
defaultPath: z.string().optional(),
|
||||
statsConfig: z
|
||||
.object({
|
||||
enabledWidgets: z
|
||||
.array(
|
||||
z.enum([
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk",
|
||||
"network",
|
||||
"uptime",
|
||||
"processes",
|
||||
"system",
|
||||
]),
|
||||
)
|
||||
.default(["cpu", "memory", "disk", "network", "uptime", "system"]),
|
||||
})
|
||||
.default({
|
||||
enabledWidgets: [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk",
|
||||
"network",
|
||||
"uptime",
|
||||
"system",
|
||||
],
|
||||
}),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.authType === "password") {
|
||||
@@ -272,7 +301,7 @@ export function HostManagerEditor({
|
||||
type FormData = z.infer<typeof formSchema>;
|
||||
|
||||
const form = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema) as any,
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
ip: "",
|
||||
@@ -292,6 +321,7 @@ export function HostManagerEditor({
|
||||
enableFileManager: true,
|
||||
defaultPath: "/",
|
||||
tunnelConnections: [],
|
||||
statsConfig: DEFAULT_STATS_CONFIG,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -348,6 +378,7 @@ export function HostManagerEditor({
|
||||
enableFileManager: Boolean(cleanedHost.enableFileManager),
|
||||
defaultPath: cleanedHost.defaultPath || "/",
|
||||
tunnelConnections: cleanedHost.tunnelConnections || [],
|
||||
statsConfig: cleanedHost.statsConfig || DEFAULT_STATS_CONFIG,
|
||||
};
|
||||
|
||||
if (defaultAuthType === "password") {
|
||||
@@ -355,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";
|
||||
@@ -383,6 +424,7 @@ export function HostManagerEditor({
|
||||
enableFileManager: true,
|
||||
defaultPath: "/",
|
||||
tunnelConnections: [],
|
||||
statsConfig: DEFAULT_STATS_CONFIG,
|
||||
};
|
||||
|
||||
form.reset(defaultFormData);
|
||||
@@ -407,7 +449,7 @@ export function HostManagerEditor({
|
||||
data.name = `${data.username}@${data.ip}`;
|
||||
}
|
||||
|
||||
const submitData: any = {
|
||||
const submitData: Record<string, unknown> = {
|
||||
name: data.name,
|
||||
ip: data.ip,
|
||||
port: data.port,
|
||||
@@ -421,6 +463,7 @@ export function HostManagerEditor({
|
||||
enableFileManager: Boolean(data.enableFileManager),
|
||||
defaultPath: data.defaultPath || "/",
|
||||
tunnelConnections: data.tunnelConnections || [],
|
||||
statsConfig: data.statsConfig || DEFAULT_STATS_CONFIG,
|
||||
};
|
||||
|
||||
submitData.credentialId = null;
|
||||
@@ -499,7 +542,7 @@ export function HostManagerEditor({
|
||||
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
|
||||
|
||||
form.reset();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("hosts.failedToSaveHost"));
|
||||
} finally {
|
||||
isSubmittingRef.current = false;
|
||||
@@ -669,6 +712,9 @@ export function HostManagerEditor({
|
||||
<TabsTrigger value="file_manager">
|
||||
{t("hosts.fileManager")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="statistics">
|
||||
{t("hosts.statistics")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="general" className="pt-2">
|
||||
<FormLabel className="mb-3 font-bold">
|
||||
@@ -1533,6 +1579,65 @@ export function HostManagerEditor({
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="statistics">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="statsConfig.enabledWidgets"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("hosts.enabledWidgets")}</FormLabel>
|
||||
<FormDescription>
|
||||
{t("hosts.enabledWidgetsDesc")}
|
||||
</FormDescription>
|
||||
<div className="space-y-3 mt-3">
|
||||
{(
|
||||
[
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk",
|
||||
"network",
|
||||
"uptime",
|
||||
"processes",
|
||||
"system",
|
||||
] as const
|
||||
).map((widget) => (
|
||||
<div
|
||||
key={widget}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(widget)}
|
||||
onCheckedChange={(checked) => {
|
||||
const currentWidgets = field.value || [];
|
||||
if (checked) {
|
||||
field.onChange([...currentWidgets, widget]);
|
||||
} else {
|
||||
field.onChange(
|
||||
currentWidgets.filter((w) => w !== widget),
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
{widget === "cpu" && t("serverStats.cpuUsage")}
|
||||
{widget === "memory" &&
|
||||
t("serverStats.memoryUsage")}
|
||||
{widget === "disk" && t("serverStats.diskUsage")}
|
||||
{widget === "network" &&
|
||||
t("serverStats.networkInterfaces")}
|
||||
{widget === "uptime" && t("serverStats.uptime")}
|
||||
{widget === "processes" &&
|
||||
t("serverStats.processes")}
|
||||
{widget === "system" &&
|
||||
t("serverStats.systemInfo")}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</ScrollArea>
|
||||
<footer className="shrink-0 w-full pb-0">
|
||||
|
||||
@@ -106,7 +106,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
|
||||
setHosts(cleanedHosts);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
setError(t("hosts.failedToLoadHosts"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -122,7 +122,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
toast.success(t("hosts.hostDeletedSuccessfully", { name: hostName }));
|
||||
await fetchHosts();
|
||||
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("hosts.failedToDeleteHost"));
|
||||
}
|
||||
},
|
||||
@@ -143,7 +143,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
});
|
||||
|
||||
confirmWithToast(confirmMessage, () => {
|
||||
performExport(host, actualAuthType);
|
||||
performExport(host);
|
||||
});
|
||||
return;
|
||||
} else if (actualAuthType === "password" || actualAuthType === "key") {
|
||||
@@ -152,21 +152,21 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
});
|
||||
|
||||
confirmWithToast(confirmMessage, () => {
|
||||
performExport(host, actualAuthType);
|
||||
performExport(host);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
performExport(host, actualAuthType);
|
||||
performExport(host);
|
||||
};
|
||||
|
||||
const performExport = async (host: SSHHost, actualAuthType: string) => {
|
||||
const performExport = async (host: SSHHost) => {
|
||||
try {
|
||||
const decryptedHost = await exportSSHHostWithCredentials(host.id);
|
||||
|
||||
const cleanExportData = Object.fromEntries(
|
||||
Object.entries(decryptedHost).filter(
|
||||
([_, value]) => value !== undefined,
|
||||
([, value]) => value !== undefined,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -185,7 +185,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
toast.success(
|
||||
`Exported host configuration for ${host.name || host.username}@${host.ip}`,
|
||||
);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("hosts.failedToExportHost"));
|
||||
}
|
||||
};
|
||||
@@ -222,7 +222,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
);
|
||||
await fetchHosts();
|
||||
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("hosts.failedToRemoveFromFolder"));
|
||||
} finally {
|
||||
setOperationLoading(false);
|
||||
@@ -251,7 +251,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
|
||||
setEditingFolder(null);
|
||||
setEditingFolderName("");
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("hosts.failedToRenameFolder"));
|
||||
} finally {
|
||||
setOperationLoading(false);
|
||||
@@ -291,7 +291,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
setDragOverFolder(folderName);
|
||||
};
|
||||
|
||||
const handleDragLeave = (e: React.DragEvent) => {
|
||||
const handleDragLeave = () => {
|
||||
dragCounter.current--;
|
||||
if (dragCounter.current === 0) {
|
||||
setDragOverFolder(null);
|
||||
@@ -325,7 +325,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
);
|
||||
await fetchHosts();
|
||||
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("hosts.failedToMoveToFolder"));
|
||||
} finally {
|
||||
setOperationLoading(false);
|
||||
|
||||
@@ -3,8 +3,6 @@ import { useSidebar } from "@/components/ui/sidebar.tsx";
|
||||
import { Status, StatusIndicator } from "@/components/ui/shadcn-io/status";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Progress } from "@/components/ui/progress.tsx";
|
||||
import { Cpu, HardDrive, MemoryStick } from "lucide-react";
|
||||
import { Tunnel } from "@/ui/Desktop/Apps/Tunnel/Tunnel.tsx";
|
||||
import {
|
||||
getServerStatusById,
|
||||
@@ -14,9 +12,43 @@ import {
|
||||
import { useTabs } from "@/ui/Desktop/Navigation/Tabs/TabContext.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
type WidgetType,
|
||||
type StatsConfig,
|
||||
DEFAULT_STATS_CONFIG,
|
||||
} from "@/types/stats-widgets";
|
||||
import {
|
||||
CpuWidget,
|
||||
MemoryWidget,
|
||||
DiskWidget,
|
||||
NetworkWidget,
|
||||
UptimeWidget,
|
||||
ProcessesWidget,
|
||||
SystemWidget,
|
||||
} from "./widgets";
|
||||
|
||||
interface HostConfig {
|
||||
id: number;
|
||||
name: string;
|
||||
ip: string;
|
||||
username: string;
|
||||
folder?: string;
|
||||
enableFileManager?: boolean;
|
||||
tunnelConnections?: unknown[];
|
||||
statsConfig?: string | StatsConfig;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface TabData {
|
||||
id: number;
|
||||
type: string;
|
||||
title?: string;
|
||||
hostConfig?: HostConfig;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface ServerProps {
|
||||
hostConfig?: any;
|
||||
hostConfig?: HostConfig;
|
||||
title?: string;
|
||||
isVisible?: boolean;
|
||||
isTopbarOpen?: boolean;
|
||||
@@ -32,20 +64,80 @@ export function Server({
|
||||
}: ServerProps): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const { state: sidebarState } = useSidebar();
|
||||
const { addTab, tabs } = useTabs() as any;
|
||||
const { addTab, tabs } = useTabs() as {
|
||||
addTab: (tab: { type: string; [key: string]: unknown }) => number;
|
||||
tabs: TabData[];
|
||||
};
|
||||
const [serverStatus, setServerStatus] = React.useState<"online" | "offline">(
|
||||
"offline",
|
||||
);
|
||||
const [metrics, setMetrics] = React.useState<ServerMetrics | null>(null);
|
||||
const [metricsHistory, setMetricsHistory] = React.useState<ServerMetrics[]>(
|
||||
[],
|
||||
);
|
||||
const [currentHostConfig, setCurrentHostConfig] = React.useState(hostConfig);
|
||||
const [isLoadingMetrics, setIsLoadingMetrics] = React.useState(false);
|
||||
const [isRefreshing, setIsRefreshing] = React.useState(false);
|
||||
const [showStatsUI, setShowStatsUI] = React.useState(true);
|
||||
|
||||
const enabledWidgets = React.useMemo((): WidgetType[] => {
|
||||
if (!currentHostConfig?.statsConfig) {
|
||||
return DEFAULT_STATS_CONFIG.enabledWidgets;
|
||||
}
|
||||
try {
|
||||
const parsed =
|
||||
typeof currentHostConfig.statsConfig === "string"
|
||||
? JSON.parse(currentHostConfig.statsConfig)
|
||||
: currentHostConfig.statsConfig;
|
||||
return parsed?.enabledWidgets || DEFAULT_STATS_CONFIG.enabledWidgets;
|
||||
} catch (error) {
|
||||
console.error("Failed to parse statsConfig:", error);
|
||||
return DEFAULT_STATS_CONFIG.enabledWidgets;
|
||||
}
|
||||
}, [currentHostConfig?.statsConfig]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setCurrentHostConfig(hostConfig);
|
||||
}, [hostConfig]);
|
||||
|
||||
const renderWidget = (widgetType: WidgetType) => {
|
||||
switch (widgetType) {
|
||||
case "cpu":
|
||||
return <CpuWidget metrics={metrics} metricsHistory={metricsHistory} />;
|
||||
|
||||
case "memory":
|
||||
return (
|
||||
<MemoryWidget metrics={metrics} metricsHistory={metricsHistory} />
|
||||
);
|
||||
|
||||
case "disk":
|
||||
return <DiskWidget metrics={metrics} metricsHistory={metricsHistory} />;
|
||||
|
||||
case "network":
|
||||
return (
|
||||
<NetworkWidget metrics={metrics} metricsHistory={metricsHistory} />
|
||||
);
|
||||
|
||||
case "uptime":
|
||||
return (
|
||||
<UptimeWidget metrics={metrics} metricsHistory={metricsHistory} />
|
||||
);
|
||||
|
||||
case "processes":
|
||||
return (
|
||||
<ProcessesWidget metrics={metrics} metricsHistory={metricsHistory} />
|
||||
);
|
||||
|
||||
case "system":
|
||||
return (
|
||||
<SystemWidget metrics={metrics} metricsHistory={metricsHistory} />
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchLatestHostConfig = async () => {
|
||||
if (hostConfig?.id) {
|
||||
@@ -56,7 +148,7 @@ export function Server({
|
||||
if (updatedHost) {
|
||||
setCurrentHostConfig(updatedHost);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("serverStats.failedToFetchHostConfig"));
|
||||
}
|
||||
}
|
||||
@@ -73,7 +165,7 @@ export function Server({
|
||||
if (updatedHost) {
|
||||
setCurrentHostConfig(updatedHost);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(t("serverStats.failedToFetchHostConfig"));
|
||||
}
|
||||
}
|
||||
@@ -94,13 +186,16 @@ export function Server({
|
||||
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("offline");
|
||||
} else if (error?.response?.status === 404) {
|
||||
} else if (err?.response?.status === 404) {
|
||||
setServerStatus("offline");
|
||||
} else {
|
||||
setServerStatus("offline");
|
||||
@@ -117,14 +212,26 @@ export function Server({
|
||||
const data = await getServerMetricsById(currentHostConfig.id);
|
||||
if (!cancelled) {
|
||||
setMetrics(data);
|
||||
setMetricsHistory((prev) => {
|
||||
const newHistory = [...prev, data];
|
||||
// Keep last 20 data points for chart
|
||||
return newHistory.slice(-20);
|
||||
});
|
||||
setShowStatsUI(true);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
if (!cancelled) {
|
||||
setMetrics(null);
|
||||
setShowStatsUI(false);
|
||||
if (error?.code === "TOTP_REQUIRED" ||
|
||||
(error?.response?.status === 403 && error?.response?.data?.error === "TOTP_REQUIRED")) {
|
||||
const err = error as {
|
||||
code?: string;
|
||||
response?: { status?: number; data?: { error?: string } };
|
||||
};
|
||||
if (
|
||||
err?.code === "TOTP_REQUIRED" ||
|
||||
(err?.response?.status === 403 &&
|
||||
err?.response?.data?.error === "TOTP_REQUIRED")
|
||||
) {
|
||||
toast.error(t("serverStats.totpUnavailable"));
|
||||
} else {
|
||||
toast.error(t("serverStats.failedToFetchMetrics"));
|
||||
@@ -159,7 +266,7 @@ export function Server({
|
||||
const isFileManagerAlreadyOpen = React.useMemo(() => {
|
||||
if (!currentHostConfig) return false;
|
||||
return tabs.some(
|
||||
(tab: any) =>
|
||||
(tab: TabData) =>
|
||||
tab.type === "file_manager" &&
|
||||
tab.hostConfig?.id === currentHostConfig.id,
|
||||
);
|
||||
@@ -214,21 +321,38 @@ export function Server({
|
||||
);
|
||||
setMetrics(data);
|
||||
setShowStatsUI(true);
|
||||
} catch (error: any) {
|
||||
if (error?.code === "TOTP_REQUIRED" ||
|
||||
(error?.response?.status === 403 && error?.response?.data?.error === "TOTP_REQUIRED")) {
|
||||
} catch (error: unknown) {
|
||||
const err = error as {
|
||||
code?: string;
|
||||
status?: number;
|
||||
response?: { status?: number; data?: { error?: string } };
|
||||
};
|
||||
if (
|
||||
err?.code === "TOTP_REQUIRED" ||
|
||||
(err?.response?.status === 403 &&
|
||||
err?.response?.data?.error === "TOTP_REQUIRED")
|
||||
) {
|
||||
toast.error(t("serverStats.totpUnavailable"));
|
||||
setMetrics(null);
|
||||
setShowStatsUI(false);
|
||||
} else if (error?.response?.status === 503 || error?.status === 503) {
|
||||
} else if (
|
||||
err?.response?.status === 503 ||
|
||||
err?.status === 503
|
||||
) {
|
||||
setServerStatus("offline");
|
||||
setMetrics(null);
|
||||
setShowStatsUI(false);
|
||||
} else if (error?.response?.status === 504 || error?.status === 504) {
|
||||
} else if (
|
||||
err?.response?.status === 504 ||
|
||||
err?.status === 504
|
||||
) {
|
||||
setServerStatus("offline");
|
||||
setMetrics(null);
|
||||
setShowStatsUI(false);
|
||||
} else if (error?.response?.status === 404 || error?.status === 404) {
|
||||
} else if (
|
||||
err?.response?.status === 404 ||
|
||||
err?.status === 404
|
||||
) {
|
||||
setServerStatus("offline");
|
||||
setMetrics(null);
|
||||
setShowStatsUI(false);
|
||||
@@ -310,154 +434,12 @@ export function Server({
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 lg:gap-6">
|
||||
{/* CPU Stats */}
|
||||
<div className="space-y-3 p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Cpu className="h-5 w-5 text-blue-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.cpuUsage")}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{enabledWidgets.map((widgetType) => (
|
||||
<div key={widgetType} className="h-[280px]">
|
||||
{renderWidget(widgetType)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-300">
|
||||
{(() => {
|
||||
const pct = metrics?.cpu?.percent;
|
||||
const cores = metrics?.cpu?.cores;
|
||||
const pctText =
|
||||
typeof pct === "number" ? `${pct}%` : "N/A";
|
||||
const coresText =
|
||||
typeof cores === "number"
|
||||
? t("serverStats.cpuCores", { count: cores })
|
||||
: t("serverStats.naCpus");
|
||||
return `${pctText} ${t("serverStats.of")} ${coresText}`;
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<Progress
|
||||
value={
|
||||
typeof metrics?.cpu?.percent === "number"
|
||||
? metrics!.cpu!.percent!
|
||||
: 0
|
||||
}
|
||||
className="h-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-gray-500">
|
||||
{metrics?.cpu?.load
|
||||
? `Load: ${metrics.cpu.load[0].toFixed(2)}, ${metrics.cpu.load[1].toFixed(2)}, ${metrics.cpu.load[2].toFixed(2)}`
|
||||
: "Load: N/A"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Memory Stats */}
|
||||
<div className="space-y-3 p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<MemoryStick className="h-5 w-5 text-green-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.memoryUsage")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-300">
|
||||
{(() => {
|
||||
const pct = metrics?.memory?.percent;
|
||||
const used = metrics?.memory?.usedGiB;
|
||||
const total = metrics?.memory?.totalGiB;
|
||||
const pctText =
|
||||
typeof pct === "number" ? `${pct}%` : "N/A";
|
||||
const usedText =
|
||||
typeof used === "number"
|
||||
? `${used.toFixed(1)} GiB`
|
||||
: "N/A";
|
||||
const totalText =
|
||||
typeof total === "number"
|
||||
? `${total.toFixed(1)} GiB`
|
||||
: "N/A";
|
||||
return `${pctText} (${usedText} ${t("serverStats.of")} ${totalText})`;
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<Progress
|
||||
value={
|
||||
typeof metrics?.memory?.percent === "number"
|
||||
? metrics!.memory!.percent!
|
||||
: 0
|
||||
}
|
||||
className="h-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-gray-500">
|
||||
{(() => {
|
||||
const used = metrics?.memory?.usedGiB;
|
||||
const total = metrics?.memory?.totalGiB;
|
||||
const free =
|
||||
typeof used === "number" && typeof total === "number"
|
||||
? (total - used).toFixed(1)
|
||||
: "N/A";
|
||||
return `Free: ${free} GiB`;
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Disk Stats */}
|
||||
<div className="space-y-3 p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<HardDrive className="h-5 w-5 text-orange-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.rootStorageSpace")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-300">
|
||||
{(() => {
|
||||
const pct = metrics?.disk?.percent;
|
||||
const used = metrics?.disk?.usedHuman;
|
||||
const total = metrics?.disk?.totalHuman;
|
||||
const pctText =
|
||||
typeof pct === "number" ? `${pct}%` : "N/A";
|
||||
const usedText = used ?? "N/A";
|
||||
const totalText = total ?? "N/A";
|
||||
return `${pctText} (${usedText} ${t("serverStats.of")} ${totalText})`;
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<Progress
|
||||
value={
|
||||
typeof metrics?.disk?.percent === "number"
|
||||
? metrics!.disk!.percent!
|
||||
: 0
|
||||
}
|
||||
className="h-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-gray-500">
|
||||
{(() => {
|
||||
const available = metrics?.disk?.availableHuman;
|
||||
return available
|
||||
? `Available: ${available}`
|
||||
: "Available: N/A";
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
102
src/ui/Desktop/Apps/Server/widgets/CpuWidget.tsx
Normal file
102
src/ui/Desktop/Apps/Server/widgets/CpuWidget.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from "react";
|
||||
import { Cpu } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ServerMetrics } from "@/ui/main-axios.ts";
|
||||
import { RechartsPrimitive } from "@/components/ui/chart.tsx";
|
||||
|
||||
const {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
} = RechartsPrimitive;
|
||||
|
||||
interface CpuWidgetProps {
|
||||
metrics: ServerMetrics | null;
|
||||
metricsHistory: ServerMetrics[];
|
||||
}
|
||||
|
||||
export function CpuWidget({ metrics, metricsHistory }: CpuWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Prepare chart data
|
||||
const chartData = React.useMemo(() => {
|
||||
return metricsHistory.map((m, index) => ({
|
||||
index,
|
||||
cpu: m.cpu?.percent || 0,
|
||||
}));
|
||||
}, [metricsHistory]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Cpu className="h-5 w-5 text-blue-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.cpuUsage")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 min-h-0 gap-2">
|
||||
<div className="flex items-baseline gap-3 flex-shrink-0">
|
||||
<div className="text-2xl font-bold text-blue-400">
|
||||
{typeof metrics?.cpu?.percent === "number"
|
||||
? `${metrics.cpu.percent}%`
|
||||
: "N/A"}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">
|
||||
{typeof metrics?.cpu?.cores === "number"
|
||||
? t("serverStats.cpuCores", { count: metrics.cpu.cores })
|
||||
: t("serverStats.naCpus")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 flex-shrink-0">
|
||||
{metrics?.cpu?.load
|
||||
? t("serverStats.loadAverage", {
|
||||
avg1: metrics.cpu.load[0].toFixed(2),
|
||||
avg5: metrics.cpu.load[1].toFixed(2),
|
||||
avg15: metrics.cpu.load[2].toFixed(2),
|
||||
})
|
||||
: t("serverStats.loadAverageNA")}
|
||||
</div>
|
||||
<div className="flex-1 min-h-0">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={chartData}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis
|
||||
dataKey="index"
|
||||
stroke="#9ca3af"
|
||||
tick={{ fill: "#9ca3af" }}
|
||||
hide
|
||||
/>
|
||||
<YAxis
|
||||
domain={[0, 100]}
|
||||
stroke="#9ca3af"
|
||||
tick={{ fill: "#9ca3af" }}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: "#1f2937",
|
||||
border: "1px solid #374151",
|
||||
borderRadius: "6px",
|
||||
color: "#fff",
|
||||
}}
|
||||
formatter={(value: number) => [`${value.toFixed(1)}%`, "CPU"]}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="cpu"
|
||||
stroke="#60a5fa"
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
animationDuration={300}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
100
src/ui/Desktop/Apps/Server/widgets/DiskWidget.tsx
Normal file
100
src/ui/Desktop/Apps/Server/widgets/DiskWidget.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React from "react";
|
||||
import { HardDrive } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ServerMetrics } from "@/ui/main-axios.ts";
|
||||
import { RechartsPrimitive } from "@/components/ui/chart.tsx";
|
||||
|
||||
const { RadialBarChart, RadialBar, PolarAngleAxis, ResponsiveContainer } =
|
||||
RechartsPrimitive;
|
||||
|
||||
interface DiskWidgetProps {
|
||||
metrics: ServerMetrics | null;
|
||||
metricsHistory: ServerMetrics[];
|
||||
}
|
||||
|
||||
export function DiskWidget({ metrics }: DiskWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Prepare radial chart data
|
||||
const radialData = React.useMemo(() => {
|
||||
const percent = metrics?.disk?.percent || 0;
|
||||
return [
|
||||
{
|
||||
name: "Disk",
|
||||
value: percent,
|
||||
fill: "#fb923c",
|
||||
},
|
||||
];
|
||||
}, [metrics]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<HardDrive className="h-5 w-5 text-orange-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.diskUsage")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 min-h-0">
|
||||
<div className="flex-1 min-h-0 flex items-center justify-center">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<RadialBarChart
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius="60%"
|
||||
outerRadius="90%"
|
||||
data={radialData}
|
||||
startAngle={90}
|
||||
endAngle={-270}
|
||||
>
|
||||
<PolarAngleAxis
|
||||
type="number"
|
||||
domain={[0, 100]}
|
||||
angleAxisId={0}
|
||||
tick={false}
|
||||
/>
|
||||
<RadialBar
|
||||
background
|
||||
dataKey="value"
|
||||
cornerRadius={10}
|
||||
fill="#fb923c"
|
||||
/>
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
className="text-2xl font-bold fill-orange-400"
|
||||
>
|
||||
{typeof metrics?.disk?.percent === "number"
|
||||
? `${metrics.disk.percent}%`
|
||||
: "N/A"}
|
||||
</text>
|
||||
</RadialBarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
<div className="flex-shrink-0 space-y-1 text-center pb-2">
|
||||
<div className="text-xs text-gray-400">
|
||||
{(() => {
|
||||
const used = metrics?.disk?.usedHuman;
|
||||
const total = metrics?.disk?.totalHuman;
|
||||
if (used && total) {
|
||||
return `${used} / ${total}`;
|
||||
}
|
||||
return "N/A";
|
||||
})()}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
{(() => {
|
||||
const available = metrics?.disk?.availableHuman;
|
||||
return available
|
||||
? `${t("serverStats.available")}: ${available}`
|
||||
: `${t("serverStats.available")}: N/A`;
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
118
src/ui/Desktop/Apps/Server/widgets/MemoryWidget.tsx
Normal file
118
src/ui/Desktop/Apps/Server/widgets/MemoryWidget.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import React from "react";
|
||||
import { MemoryStick } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ServerMetrics } from "@/ui/main-axios.ts";
|
||||
import { RechartsPrimitive } from "@/components/ui/chart.tsx";
|
||||
|
||||
const {
|
||||
AreaChart,
|
||||
Area,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
} = RechartsPrimitive;
|
||||
|
||||
interface MemoryWidgetProps {
|
||||
metrics: ServerMetrics | null;
|
||||
metricsHistory: ServerMetrics[];
|
||||
}
|
||||
|
||||
export function MemoryWidget({ metrics, metricsHistory }: MemoryWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Prepare chart data
|
||||
const chartData = React.useMemo(() => {
|
||||
return metricsHistory.map((m, index) => ({
|
||||
index,
|
||||
memory: m.memory?.percent || 0,
|
||||
}));
|
||||
}, [metricsHistory]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<MemoryStick className="h-5 w-5 text-green-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.memoryUsage")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 min-h-0 gap-2">
|
||||
<div className="flex items-baseline gap-3 flex-shrink-0">
|
||||
<div className="text-2xl font-bold text-green-400">
|
||||
{typeof metrics?.memory?.percent === "number"
|
||||
? `${metrics.memory.percent}%`
|
||||
: "N/A"}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">
|
||||
{(() => {
|
||||
const used = metrics?.memory?.usedGiB;
|
||||
const total = metrics?.memory?.totalGiB;
|
||||
if (typeof used === "number" && typeof total === "number") {
|
||||
return `${used.toFixed(1)} / ${total.toFixed(1)} GiB`;
|
||||
}
|
||||
return "N/A";
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 flex-shrink-0">
|
||||
{(() => {
|
||||
const used = metrics?.memory?.usedGiB;
|
||||
const total = metrics?.memory?.totalGiB;
|
||||
const free =
|
||||
typeof used === "number" && typeof total === "number"
|
||||
? (total - used).toFixed(1)
|
||||
: "N/A";
|
||||
return `${t("serverStats.free")}: ${free} GiB`;
|
||||
})()}
|
||||
</div>
|
||||
<div className="flex-1 min-h-0">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart data={chartData}>
|
||||
<defs>
|
||||
<linearGradient id="memoryGradient" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#34d399" stopOpacity={0.8} />
|
||||
<stop offset="95%" stopColor="#34d399" stopOpacity={0.1} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis
|
||||
dataKey="index"
|
||||
stroke="#9ca3af"
|
||||
tick={{ fill: "#9ca3af" }}
|
||||
hide
|
||||
/>
|
||||
<YAxis
|
||||
domain={[0, 100]}
|
||||
stroke="#9ca3af"
|
||||
tick={{ fill: "#9ca3af" }}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: "#1f2937",
|
||||
border: "1px solid #374151",
|
||||
borderRadius: "6px",
|
||||
color: "#fff",
|
||||
}}
|
||||
formatter={(value: number) => [
|
||||
`${value.toFixed(1)}%`,
|
||||
"Memory",
|
||||
]}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="memory"
|
||||
stroke="#34d399"
|
||||
strokeWidth={2}
|
||||
fill="url(#memoryGradient)"
|
||||
animationDuration={300}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
75
src/ui/Desktop/Apps/Server/widgets/NetworkWidget.tsx
Normal file
75
src/ui/Desktop/Apps/Server/widgets/NetworkWidget.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from "react";
|
||||
import { Network, Wifi, WifiOff } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ServerMetrics } from "@/ui/main-axios.ts";
|
||||
|
||||
interface NetworkWidgetProps {
|
||||
metrics: ServerMetrics | null;
|
||||
metricsHistory: ServerMetrics[];
|
||||
}
|
||||
|
||||
export function NetworkWidget({ metrics }: NetworkWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const metricsWithNetwork = metrics as ServerMetrics & {
|
||||
network?: {
|
||||
interfaces?: Array<{
|
||||
name: string;
|
||||
state: string;
|
||||
ip: string;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
const network = metricsWithNetwork?.network;
|
||||
const interfaces = network?.interfaces || [];
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Network className="h-5 w-5 text-indigo-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.networkInterfaces")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2.5 overflow-auto flex-1">
|
||||
{interfaces.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-gray-400">
|
||||
<WifiOff className="h-10 w-10 mb-3 opacity-50" />
|
||||
<p className="text-sm">{t("serverStats.noInterfacesFound")}</p>
|
||||
</div>
|
||||
) : (
|
||||
interfaces.map((iface, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-3 rounded-lg bg-dark-bg/50 border border-dark-border/30 hover:bg-dark-bg/60 transition-colors"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Wifi
|
||||
className={`h-4 w-4 ${iface.state === "UP" ? "text-green-400" : "text-gray-500"}`}
|
||||
/>
|
||||
<span className="text-sm font-semibold text-white font-mono">
|
||||
{iface.name}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
className={`text-xs px-2.5 py-0.5 rounded-full font-medium ${
|
||||
iface.state === "UP"
|
||||
? "bg-green-500/20 text-green-400"
|
||||
: "bg-gray-500/20 text-gray-500"
|
||||
}`}
|
||||
>
|
||||
{iface.state}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400 font-mono font-medium">
|
||||
{iface.ip}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
87
src/ui/Desktop/Apps/Server/widgets/ProcessesWidget.tsx
Normal file
87
src/ui/Desktop/Apps/Server/widgets/ProcessesWidget.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import React from "react";
|
||||
import { List, Activity } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ServerMetrics } from "@/ui/main-axios.ts";
|
||||
|
||||
interface ProcessesWidgetProps {
|
||||
metrics: ServerMetrics | null;
|
||||
metricsHistory: ServerMetrics[];
|
||||
}
|
||||
|
||||
export function ProcessesWidget({ metrics }: ProcessesWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const metricsWithProcesses = metrics as ServerMetrics & {
|
||||
processes?: {
|
||||
total?: number;
|
||||
running?: number;
|
||||
top?: Array<{
|
||||
pid: number;
|
||||
cpu: number;
|
||||
mem: number;
|
||||
command: string;
|
||||
user: string;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
const processes = metricsWithProcesses?.processes;
|
||||
const topProcesses = processes?.top || [];
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<List className="h-5 w-5 text-yellow-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.processes")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between mb-3 pb-2 border-b border-dark-border/30">
|
||||
<div className="text-sm text-gray-400">
|
||||
{t("serverStats.totalProcesses")}:{" "}
|
||||
<span className="text-white font-semibold">
|
||||
{processes?.total ?? "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
{t("serverStats.running")}:{" "}
|
||||
<span className="text-green-400 font-semibold">
|
||||
{processes?.running ?? "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-auto flex-1">
|
||||
{topProcesses.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-gray-400">
|
||||
<Activity className="h-10 w-10 mb-3 opacity-50" />
|
||||
<p className="text-sm">{t("serverStats.noProcessesFound")}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{topProcesses.map((proc, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-2.5 rounded-lg bg-dark-bg/30 hover:bg-dark-bg/50 transition-colors border border-dark-border/20"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<span className="text-xs font-mono text-gray-400 font-medium">
|
||||
PID: {proc.pid}
|
||||
</span>
|
||||
<div className="flex gap-3 text-xs font-medium">
|
||||
<span className="text-blue-400">CPU: {proc.cpu}%</span>
|
||||
<span className="text-green-400">MEM: {proc.mem}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-white font-mono truncate mb-1">
|
||||
{proc.command}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">User: {proc.user}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
71
src/ui/Desktop/Apps/Server/widgets/SystemWidget.tsx
Normal file
71
src/ui/Desktop/Apps/Server/widgets/SystemWidget.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from "react";
|
||||
import { Server, Info } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ServerMetrics } from "@/ui/main-axios.ts";
|
||||
|
||||
interface SystemWidgetProps {
|
||||
metrics: ServerMetrics | null;
|
||||
metricsHistory: ServerMetrics[];
|
||||
}
|
||||
|
||||
export function SystemWidget({ metrics }: SystemWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const metricsWithSystem = metrics as ServerMetrics & {
|
||||
system?: {
|
||||
hostname?: string;
|
||||
os?: string;
|
||||
kernel?: string;
|
||||
};
|
||||
};
|
||||
const system = metricsWithSystem?.system;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Server className="h-5 w-5 text-purple-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.systemInfo")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<Info className="h-4 w-4 text-purple-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-xs text-gray-400 mb-1.5">
|
||||
{t("serverStats.hostname")}
|
||||
</p>
|
||||
<p className="text-sm text-white font-mono truncate font-medium">
|
||||
{system?.hostname || "N/A"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<Info className="h-4 w-4 text-purple-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-xs text-gray-400 mb-1.5">
|
||||
{t("serverStats.operatingSystem")}
|
||||
</p>
|
||||
<p className="text-sm text-white font-mono truncate font-medium">
|
||||
{system?.os || "N/A"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<Info className="h-4 w-4 text-purple-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-xs text-gray-400 mb-1.5">
|
||||
{t("serverStats.kernel")}
|
||||
</p>
|
||||
<p className="text-sm text-white font-mono truncate font-medium">
|
||||
{system?.kernel || "N/A"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
55
src/ui/Desktop/Apps/Server/widgets/UptimeWidget.tsx
Normal file
55
src/ui/Desktop/Apps/Server/widgets/UptimeWidget.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from "react";
|
||||
import { Clock, Activity } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ServerMetrics } from "@/ui/main-axios.ts";
|
||||
|
||||
interface UptimeWidgetProps {
|
||||
metrics: ServerMetrics | null;
|
||||
metricsHistory: ServerMetrics[];
|
||||
}
|
||||
|
||||
export function UptimeWidget({ metrics }: UptimeWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const metricsWithUptime = metrics as ServerMetrics & {
|
||||
uptime?: {
|
||||
formatted?: string;
|
||||
seconds?: number;
|
||||
};
|
||||
};
|
||||
const uptime = metricsWithUptime?.uptime;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-dark-bg/50 border border-dark-border/50 hover:bg-dark-bg/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Clock className="h-5 w-5 text-cyan-400" />
|
||||
<h3 className="font-semibold text-lg text-white">
|
||||
{t("serverStats.uptime")}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center justify-center flex-1">
|
||||
<div className="relative mb-4">
|
||||
<div className="w-24 h-24 rounded-full bg-cyan-500/10 flex items-center justify-center">
|
||||
<Activity className="h-12 w-12 text-cyan-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold text-cyan-400 mb-2">
|
||||
{uptime?.formatted || "N/A"}
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
{t("serverStats.totalUptime")}
|
||||
</div>
|
||||
{uptime?.seconds && (
|
||||
<div className="text-xs text-gray-500 mt-2">
|
||||
{Math.floor(uptime.seconds).toLocaleString()}{" "}
|
||||
{t("serverStats.seconds")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
7
src/ui/Desktop/Apps/Server/widgets/index.ts
Normal file
7
src/ui/Desktop/Apps/Server/widgets/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export { CpuWidget } from "./CpuWidget";
|
||||
export { MemoryWidget } from "./MemoryWidget";
|
||||
export { DiskWidget } from "./DiskWidget";
|
||||
export { NetworkWidget } from "./NetworkWidget";
|
||||
export { UptimeWidget } from "./UptimeWidget";
|
||||
export { ProcessesWidget } from "./ProcessesWidget";
|
||||
export { SystemWidget } from "./SystemWidget";
|
||||
@@ -60,7 +60,7 @@ export function SnippetsSidebar({
|
||||
const data = await getSnippets();
|
||||
// Defensive: ensure data is an array
|
||||
setSnippets(Array.isArray(data) ? data : []);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("snippets.failedToFetch"));
|
||||
setSnippets([]);
|
||||
} finally {
|
||||
@@ -94,7 +94,7 @@ export function SnippetsSidebar({
|
||||
await deleteSnippet(snippet.id);
|
||||
toast.success(t("snippets.deleteSuccess"));
|
||||
fetchSnippets();
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(t("snippets.deleteFailed"));
|
||||
}
|
||||
},
|
||||
@@ -125,7 +125,7 @@ export function SnippetsSidebar({
|
||||
}
|
||||
setShowDialog(false);
|
||||
fetchSnippets();
|
||||
} catch (err) {
|
||||
} catch {
|
||||
toast.error(
|
||||
editingSnippet
|
||||
? t("snippets.updateFailed")
|
||||
@@ -155,10 +155,7 @@ export function SnippetsSidebar({
|
||||
transform: "translateZ(0)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="flex-1 cursor-pointer"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="flex-1 cursor-pointer" onClick={onClose} />
|
||||
|
||||
<div
|
||||
className="w-[400px] h-full bg-dark-bg border-l-2 border-dark-border flex flex-col shadow-2xl relative isolate z-[999999]"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,7 @@
|
||||
import React from "react";
|
||||
import { TunnelObject } from "./TunnelObject.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type {
|
||||
SSHHost,
|
||||
TunnelConnection,
|
||||
TunnelStatus,
|
||||
} from "../../../types/index.js";
|
||||
import type { SSHHost, TunnelStatus } from "../../../types/index.js";
|
||||
|
||||
interface SSHTunnelViewerProps {
|
||||
hosts: SSHHost[];
|
||||
@@ -15,7 +11,7 @@ interface SSHTunnelViewerProps {
|
||||
action: "connect" | "disconnect" | "cancel",
|
||||
host: SSHHost,
|
||||
tunnelIndex: number,
|
||||
) => Promise<any>;
|
||||
) => Promise<unknown>;
|
||||
}
|
||||
|
||||
export function TunnelViewer({
|
||||
@@ -63,7 +59,7 @@ export function TunnelViewer({
|
||||
}}
|
||||
tunnelStatuses={tunnelStatuses}
|
||||
tunnelActions={tunnelActions}
|
||||
onTunnelAction={(action, _host, _index) =>
|
||||
onTunnelAction={(action) =>
|
||||
onTunnelAction(action, activeHost, idx)
|
||||
}
|
||||
compact
|
||||
|
||||
@@ -12,13 +12,9 @@ import { AdminSettings } from "@/ui/Desktop/Admin/AdminSettings.tsx";
|
||||
import { UserProfile } from "@/ui/Desktop/User/UserProfile.tsx";
|
||||
import { Toaster } from "@/components/ui/sonner.tsx";
|
||||
import { VersionCheckModal } from "@/components/ui/version-check-modal.tsx";
|
||||
import { getUserInfo, getCookie } from "@/ui/main-axios.ts";
|
||||
import { getUserInfo } from "@/ui/main-axios.ts";
|
||||
|
||||
function AppContent() {
|
||||
const [view, setView] = useState<string>("homepage");
|
||||
const [mountedViews, setMountedViews] = useState<Set<string>>(
|
||||
new Set(["homepage"]),
|
||||
);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [username, setUsername] = useState<string | null>(null);
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
@@ -71,14 +67,8 @@ function AppContent() {
|
||||
localStorage.setItem("topNavbarOpen", JSON.stringify(isTopbarOpen));
|
||||
}, [isTopbarOpen]);
|
||||
|
||||
const handleSelectView = (nextView: string) => {
|
||||
setMountedViews((prev) => {
|
||||
if (prev.has(nextView)) return prev;
|
||||
const next = new Set(prev);
|
||||
next.add(nextView);
|
||||
return next;
|
||||
});
|
||||
setView(nextView);
|
||||
const handleSelectView = () => {
|
||||
// View switching is now handled by tabs context
|
||||
};
|
||||
|
||||
const handleAuthSuccess = (authData: {
|
||||
|
||||
@@ -74,7 +74,7 @@ export function ServerConfig({
|
||||
setConnectionStatus("error");
|
||||
setError(result.error || t("serverConfig.connectionFailed"));
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
setConnectionStatus("error");
|
||||
setError(t("serverConfig.connectionError"));
|
||||
} finally {
|
||||
@@ -117,7 +117,7 @@ export function ServerConfig({
|
||||
} else {
|
||||
setError(t("serverConfig.saveFailed"));
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
setError(t("serverConfig.saveError"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
@@ -4,7 +4,6 @@ import { HomepageUpdateLog } from "@/ui/Desktop/Homepage/HompageUpdateLog.tsx";
|
||||
import { HomepageAlertManager } from "@/ui/Desktop/Homepage/HomepageAlertManager.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { getUserInfo, getDatabaseHealth, getCookie } from "@/ui/main-axios.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface HomepageProps {
|
||||
onSelectView: (view: string) => void;
|
||||
@@ -25,8 +24,8 @@ export function Homepage({
|
||||
isTopbarOpen,
|
||||
}: HomepageProps): React.ReactElement {
|
||||
const [loggedIn, setLoggedIn] = useState(isAuthenticated);
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [username, setUsername] = useState<string | null>(null);
|
||||
const [, setIsAdmin] = useState(false);
|
||||
const [, setUsername] = useState<string | null>(null);
|
||||
const [userId, setUserId] = useState<string | null>(null);
|
||||
const [dbError, setDbError] = useState<string | null>(null);
|
||||
|
||||
@@ -40,8 +39,7 @@ export function Homepage({
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
const jwt = getCookie("jwt");
|
||||
if (jwt) {
|
||||
if (getCookie("jwt")) {
|
||||
getUserInfo()
|
||||
.then((meRes) => {
|
||||
setIsAdmin(!!meRes.is_admin);
|
||||
|
||||
@@ -17,7 +17,7 @@ export function HomepageAlertManager({
|
||||
const { t } = useTranslation();
|
||||
const [alerts, setAlerts] = useState<TermixAlert[]>([]);
|
||||
const [currentAlertIndex, setCurrentAlertIndex] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -52,7 +52,7 @@ export function HomepageAlertManager({
|
||||
|
||||
setAlerts(sortedAlerts);
|
||||
setCurrentAlertIndex(0);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
const { toast } = await import("sonner");
|
||||
toast.error(t("homepage.failedToLoadAlerts"));
|
||||
setError(t("homepage.failedToLoadAlerts"));
|
||||
@@ -77,7 +77,7 @@ export function HomepageAlertManager({
|
||||
return Math.max(0, newAlertsLength - 1);
|
||||
return prevIndex;
|
||||
});
|
||||
} catch (err) {
|
||||
} catch {
|
||||
setError(t("homepage.failedToDismissAlert"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,8 +19,6 @@ import {
|
||||
completePasswordReset,
|
||||
getOIDCAuthorizeUrl,
|
||||
verifyTOTPLogin,
|
||||
setCookie,
|
||||
getCookie,
|
||||
getServerConfig,
|
||||
isElectron,
|
||||
logoutUser,
|
||||
@@ -34,7 +32,6 @@ interface HomepageAuthProps extends React.ComponentProps<"div"> {
|
||||
setUserId: (userId: string | null) => void;
|
||||
loggedIn: boolean;
|
||||
authLoading: boolean;
|
||||
dbError: string | null;
|
||||
setDbError: (error: string | null) => void;
|
||||
onAuthSuccess: (authData: {
|
||||
isAdmin: boolean;
|
||||
@@ -51,7 +48,6 @@ export function HomepageAuth({
|
||||
setUserId,
|
||||
loggedIn,
|
||||
authLoading,
|
||||
dbError,
|
||||
setDbError,
|
||||
onAuthSuccess,
|
||||
...props
|
||||
@@ -65,17 +61,6 @@ export function HomepageAuth({
|
||||
const [signupConfirmPassword, setSignupConfirmPassword] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [oidcLoading, setOidcLoading] = useState(false);
|
||||
const [visibility, setVisibility] = useState({
|
||||
password: false,
|
||||
signupConfirm: false,
|
||||
resetNew: false,
|
||||
resetConfirm: false,
|
||||
});
|
||||
const toggleVisibility = (field: keyof typeof visibility) => {
|
||||
setVisibility((prev) => ({ ...prev, [field]: !prev[field] }));
|
||||
};
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [internalLoggedIn, setInternalLoggedIn] = useState(false);
|
||||
const [firstUser, setFirstUser] = useState(false);
|
||||
const [firstUserToastShown, setFirstUserToastShown] = useState(false);
|
||||
@@ -170,7 +155,6 @@ export function HomepageAuth({
|
||||
|
||||
async function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setLoading(true);
|
||||
|
||||
if (!localUsername.trim()) {
|
||||
@@ -180,7 +164,7 @@ export function HomepageAuth({
|
||||
}
|
||||
|
||||
try {
|
||||
let res, meRes;
|
||||
let res;
|
||||
if (tab === "login") {
|
||||
res = await loginUser(localUsername, password);
|
||||
} else {
|
||||
@@ -210,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);
|
||||
@@ -233,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);
|
||||
@@ -253,16 +243,19 @@ export function HomepageAuth({
|
||||
}
|
||||
|
||||
async function handleInitiatePasswordReset() {
|
||||
setError(null);
|
||||
setResetLoading(true);
|
||||
try {
|
||||
const result = await initiatePasswordReset(localUsername);
|
||||
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 {
|
||||
@@ -271,22 +264,21 @@ export function HomepageAuth({
|
||||
}
|
||||
|
||||
async function handleVerifyResetCode() {
|
||||
setError(null);
|
||||
setResetLoading(true);
|
||||
try {
|
||||
const response = await verifyPasswordResetCode(localUsername, resetCode);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCompletePasswordReset() {
|
||||
setError(null);
|
||||
setResetLoading(true);
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
@@ -309,16 +301,16 @@ export function HomepageAuth({
|
||||
setNewPassword("");
|
||||
setConfirmPassword("");
|
||||
setTempToken("");
|
||||
setError(null);
|
||||
|
||||
setResetSuccess(true);
|
||||
toast.success(t("messages.passwordResetSuccess"));
|
||||
|
||||
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);
|
||||
@@ -331,7 +323,6 @@ export function HomepageAuth({
|
||||
setNewPassword("");
|
||||
setConfirmPassword("");
|
||||
setTempToken("");
|
||||
setError(null);
|
||||
setResetSuccess(false);
|
||||
setSignupConfirmPassword("");
|
||||
}
|
||||
@@ -339,7 +330,6 @@ export function HomepageAuth({
|
||||
function clearFormFields() {
|
||||
setPassword("");
|
||||
setSignupConfirmPassword("");
|
||||
setError(null);
|
||||
}
|
||||
|
||||
async function handleTOTPVerification() {
|
||||
@@ -348,7 +338,6 @@ export function HomepageAuth({
|
||||
return;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setTotpLoading(true);
|
||||
|
||||
try {
|
||||
@@ -382,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") {
|
||||
@@ -404,7 +397,6 @@ export function HomepageAuth({
|
||||
}
|
||||
|
||||
async function handleOIDCLogin() {
|
||||
setError(null);
|
||||
setOidcLoading(true);
|
||||
try {
|
||||
const authResponse = await getOIDCAuthorizeUrl();
|
||||
@@ -415,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);
|
||||
@@ -428,7 +424,6 @@ export function HomepageAuth({
|
||||
useEffect(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const success = urlParams.get("success");
|
||||
const token = urlParams.get("token");
|
||||
const error = urlParams.get("error");
|
||||
|
||||
if (error) {
|
||||
@@ -440,7 +435,6 @@ export function HomepageAuth({
|
||||
|
||||
if (success) {
|
||||
setOidcLoading(true);
|
||||
setError(null);
|
||||
|
||||
getUserInfo()
|
||||
.then((meRes) => {
|
||||
@@ -462,7 +456,7 @@ export function HomepageAuth({
|
||||
window.location.pathname,
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(() => {
|
||||
setInternalLoggedIn(false);
|
||||
setLoggedIn(false);
|
||||
setIsAdmin(false);
|
||||
@@ -515,26 +509,6 @@ export function HomepageAuth({
|
||||
}
|
||||
}, [dbConnectionFailed, t]);
|
||||
|
||||
const retryDatabaseConnection = async () => {
|
||||
setDbHealthChecking(true);
|
||||
setDbConnectionFailed(false);
|
||||
try {
|
||||
const res = await getSetupRequired();
|
||||
if (res.setup_required) {
|
||||
setFirstUser(true);
|
||||
setTab("signup");
|
||||
} else {
|
||||
setFirstUser(false);
|
||||
}
|
||||
setDbError(null);
|
||||
toast.success(t("messages.databaseConnected"));
|
||||
} catch (error) {
|
||||
setDbConnectionFailed(true);
|
||||
} finally {
|
||||
setDbHealthChecking(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const checkServerConfig = async () => {
|
||||
if (isElectron()) {
|
||||
@@ -542,7 +516,7 @@ export function HomepageAuth({
|
||||
const config = await getServerConfig();
|
||||
setCurrentServerUrl(config?.serverUrl || "");
|
||||
setShowServerConfig(!config || !config.serverUrl);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
setShowServerConfig(true);
|
||||
}
|
||||
} else {
|
||||
@@ -713,7 +687,6 @@ export function HomepageAuth({
|
||||
setTotpRequired(false);
|
||||
setTotpCode("");
|
||||
setTotpTempToken("");
|
||||
setError(null);
|
||||
}}
|
||||
>
|
||||
{t("common.cancel")}
|
||||
|
||||
@@ -66,7 +66,7 @@ export function HomepageUpdateLog({ loggedIn }: HomepageUpdateLogProps) {
|
||||
setVersionInfo(versionRes);
|
||||
setError(null);
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(() => {
|
||||
setError(t("common.failedToFetchUpdateInfo"));
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
|
||||
@@ -10,14 +10,24 @@ import {
|
||||
} from "@/components/ui/resizable.tsx";
|
||||
import * as ResizablePrimitive from "react-resizable-panels";
|
||||
import { useSidebar } from "@/components/ui/sidebar.tsx";
|
||||
import {
|
||||
LucideRefreshCcw,
|
||||
LucideRefreshCw,
|
||||
RefreshCcw,
|
||||
RefreshCcwDot,
|
||||
} from "lucide-react";
|
||||
import { RefreshCcw } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
|
||||
interface TabData {
|
||||
id: number;
|
||||
type: string;
|
||||
title: string;
|
||||
terminalRef?: {
|
||||
current?: {
|
||||
fit?: () => void;
|
||||
notifyResize?: () => void;
|
||||
refresh?: () => void;
|
||||
};
|
||||
};
|
||||
hostConfig?: unknown;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface TerminalViewProps {
|
||||
isTopbarOpen?: boolean;
|
||||
}
|
||||
@@ -25,11 +35,16 @@ interface TerminalViewProps {
|
||||
export function AppView({
|
||||
isTopbarOpen = true,
|
||||
}: TerminalViewProps): React.ReactElement {
|
||||
const { tabs, currentTab, allSplitScreenTab, removeTab } = useTabs() as any;
|
||||
const { tabs, currentTab, allSplitScreenTab, removeTab } = useTabs() as {
|
||||
tabs: TabData[];
|
||||
currentTab: number;
|
||||
allSplitScreenTab: number[];
|
||||
removeTab: (id: number) => void;
|
||||
};
|
||||
const { state: sidebarState } = useSidebar();
|
||||
|
||||
const terminalTabs = tabs.filter(
|
||||
(tab: any) =>
|
||||
(tab: TabData) =>
|
||||
tab.type === "terminal" ||
|
||||
tab.type === "server" ||
|
||||
tab.type === "file_manager",
|
||||
@@ -59,7 +74,7 @@ export function AppView({
|
||||
const splitIds = allSplitScreenTab as number[];
|
||||
visibleIds.push(currentTab, ...splitIds.filter((i) => i !== currentTab));
|
||||
}
|
||||
terminalTabs.forEach((t: any) => {
|
||||
terminalTabs.forEach((t: TabData) => {
|
||||
if (visibleIds.includes(t.id)) {
|
||||
const ref = t.terminalRef?.current;
|
||||
if (ref?.fit) ref.fit();
|
||||
@@ -125,16 +140,16 @@ export function AppView({
|
||||
|
||||
const renderTerminalsLayer = () => {
|
||||
const styles: Record<number, React.CSSProperties> = {};
|
||||
const splitTabs = terminalTabs.filter((tab: any) =>
|
||||
const splitTabs = terminalTabs.filter((tab: TabData) =>
|
||||
allSplitScreenTab.includes(tab.id),
|
||||
);
|
||||
const mainTab = terminalTabs.find((tab: any) => tab.id === currentTab);
|
||||
const mainTab = terminalTabs.find((tab: TabData) => tab.id === currentTab);
|
||||
const layoutTabs = [
|
||||
mainTab,
|
||||
...splitTabs.filter(
|
||||
(t: any) => t && t.id !== (mainTab && (mainTab as any).id),
|
||||
(t: TabData) => t && t.id !== (mainTab && (mainTab as TabData).id),
|
||||
),
|
||||
].filter(Boolean) as any[];
|
||||
].filter((t): t is TabData => t !== null && t !== undefined);
|
||||
|
||||
if (allSplitScreenTab.length === 0 && mainTab) {
|
||||
const isFileManagerTab = mainTab.type === "file_manager";
|
||||
@@ -150,7 +165,7 @@ export function AppView({
|
||||
opacity: ready ? 1 : 0,
|
||||
};
|
||||
} else {
|
||||
layoutTabs.forEach((t: any) => {
|
||||
layoutTabs.forEach((t: TabData) => {
|
||||
const rect = panelRects[String(t.id)];
|
||||
const parentRect = containerRef.current?.getBoundingClientRect();
|
||||
if (rect && parentRect) {
|
||||
@@ -171,7 +186,7 @@ export function AppView({
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 z-[1]">
|
||||
{terminalTabs.map((t: any) => {
|
||||
{terminalTabs.map((t: TabData) => {
|
||||
const hasStyle = !!styles[t.id];
|
||||
const isVisible =
|
||||
hasStyle || (allSplitScreenTab.length === 0 && t.id === currentTab);
|
||||
@@ -241,16 +256,16 @@ export function AppView({
|
||||
};
|
||||
|
||||
const renderSplitOverlays = () => {
|
||||
const splitTabs = terminalTabs.filter((tab: any) =>
|
||||
const splitTabs = terminalTabs.filter((tab: TabData) =>
|
||||
allSplitScreenTab.includes(tab.id),
|
||||
);
|
||||
const mainTab = terminalTabs.find((tab: any) => tab.id === currentTab);
|
||||
const mainTab = terminalTabs.find((tab: TabData) => tab.id === currentTab);
|
||||
const layoutTabs = [
|
||||
mainTab,
|
||||
...splitTabs.filter(
|
||||
(t: any) => t && t.id !== (mainTab && (mainTab as any).id),
|
||||
(t: TabData) => t && t.id !== (mainTab && (mainTab as TabData).id),
|
||||
),
|
||||
].filter(Boolean) as any[];
|
||||
].filter((t): t is TabData => t !== null && t !== undefined);
|
||||
if (allSplitScreenTab.length === 0) return null;
|
||||
|
||||
const handleStyle = {
|
||||
@@ -258,13 +273,16 @@ export function AppView({
|
||||
zIndex: 12,
|
||||
background: "var(--color-dark-border)",
|
||||
} as React.CSSProperties;
|
||||
const commonGroupProps = {
|
||||
const commonGroupProps: {
|
||||
onLayout: () => void;
|
||||
onResize: () => void;
|
||||
} = {
|
||||
onLayout: scheduleMeasureAndFit,
|
||||
onResize: scheduleMeasureAndFit,
|
||||
} as any;
|
||||
};
|
||||
|
||||
if (layoutTabs.length === 2) {
|
||||
const [a, b] = layoutTabs as any[];
|
||||
const [a, b] = layoutTabs;
|
||||
return (
|
||||
<div className="absolute inset-0 z-[10] pointer-events-none">
|
||||
<ResizablePrimitive.PanelGroup
|
||||
@@ -316,7 +334,7 @@ export function AppView({
|
||||
);
|
||||
}
|
||||
if (layoutTabs.length === 3) {
|
||||
const [a, b, c] = layoutTabs as any[];
|
||||
const [a, b, c] = layoutTabs;
|
||||
return (
|
||||
<div className="absolute inset-0 z-[10] pointer-events-none">
|
||||
<ResizablePrimitive.PanelGroup
|
||||
@@ -404,7 +422,7 @@ export function AppView({
|
||||
);
|
||||
}
|
||||
if (layoutTabs.length === 4) {
|
||||
const [a, b, c, d] = layoutTabs as any[];
|
||||
const [a, b, c, d] = layoutTabs;
|
||||
return (
|
||||
<div className="absolute inset-0 z-[10] pointer-events-none">
|
||||
<ResizablePrimitive.PanelGroup
|
||||
@@ -529,7 +547,7 @@ export function AppView({
|
||||
return null;
|
||||
};
|
||||
|
||||
const currentTabData = tabs.find((tab: any) => tab.id === currentTab);
|
||||
const currentTabData = tabs.find((tab: TabData) => tab.id === currentTab);
|
||||
const isFileManager = currentTabData?.type === "file_manager";
|
||||
const isSplitScreen = allSplitScreenTab.length > 0;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import { ChevronUp, User2, HardDrive, Menu, ChevronRight } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
getCookie,
|
||||
setCookie,
|
||||
isElectron,
|
||||
logoutUser,
|
||||
} from "@/ui/main-axios.ts";
|
||||
import { isElectron, logoutUser } from "@/ui/main-axios.ts";
|
||||
|
||||
import {
|
||||
Sidebar,
|
||||
@@ -57,14 +52,12 @@ interface SSHHost {
|
||||
enableTunnel: boolean;
|
||||
enableFileManager: boolean;
|
||||
defaultPath: string;
|
||||
tunnelConnections: any[];
|
||||
tunnelConnections: unknown[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface SidebarProps {
|
||||
onSelectView: (view: string) => void;
|
||||
getView?: () => string;
|
||||
disabled?: boolean;
|
||||
isAdmin?: boolean;
|
||||
username?: string | null;
|
||||
@@ -87,8 +80,6 @@ async function handleLogout() {
|
||||
}
|
||||
|
||||
export function LeftSidebar({
|
||||
onSelectView,
|
||||
getView,
|
||||
disabled,
|
||||
isAdmin,
|
||||
username,
|
||||
@@ -112,13 +103,19 @@ export function LeftSidebar({
|
||||
setCurrentTab,
|
||||
allSplitScreenTab,
|
||||
updateHostConfig,
|
||||
} = useTabs() as any;
|
||||
} = useTabs() as {
|
||||
tabs: Array<{ id: number; type: string; [key: string]: unknown }>;
|
||||
addTab: (tab: { type: string; [key: string]: unknown }) => number;
|
||||
setCurrentTab: (id: number) => void;
|
||||
allSplitScreenTab: number[];
|
||||
updateHostConfig: (id: number, config: unknown) => void;
|
||||
};
|
||||
const isSplitScreenActive =
|
||||
Array.isArray(allSplitScreenTab) && allSplitScreenTab.length > 0;
|
||||
const sshManagerTab = tabList.find((t) => t.type === "ssh_manager");
|
||||
const openSshManagerTab = () => {
|
||||
if (sshManagerTab || isSplitScreenActive) return;
|
||||
const id = addTab({ type: "ssh_manager" } as any);
|
||||
const id = addTab({ type: "ssh_manager" });
|
||||
setCurrentTab(id);
|
||||
};
|
||||
const adminTab = tabList.find((t) => t.type === "admin");
|
||||
@@ -128,7 +125,7 @@ export function LeftSidebar({
|
||||
setCurrentTab(adminTab.id);
|
||||
return;
|
||||
}
|
||||
const id = addTab({ type: "admin" } as any);
|
||||
const id = addTab({ type: "admin" });
|
||||
setCurrentTab(id);
|
||||
};
|
||||
const userProfileTab = tabList.find((t) => t.type === "user_profile");
|
||||
@@ -138,12 +135,12 @@ export function LeftSidebar({
|
||||
setCurrentTab(userProfileTab.id);
|
||||
return;
|
||||
}
|
||||
const id = addTab({ type: "user_profile" } as any);
|
||||
const id = addTab({ type: "user_profile" });
|
||||
setCurrentTab(id);
|
||||
};
|
||||
|
||||
const [hosts, setHosts] = useState<SSHHost[]>([]);
|
||||
const [hostsLoading, setHostsLoading] = useState(false);
|
||||
const [hostsLoading] = useState(false);
|
||||
const [hostsError, setHostsError] = useState<string | null>(null);
|
||||
const prevHostsRef = React.useRef<SSHHost[]>([]);
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -206,7 +203,7 @@ export function LeftSidebar({
|
||||
});
|
||||
}, 50);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch {
|
||||
setHostsError(t("leftSidebar.failedToLoadHosts"));
|
||||
}
|
||||
}, [updateHostConfig]);
|
||||
@@ -314,14 +311,14 @@ export function LeftSidebar({
|
||||
return;
|
||||
}
|
||||
|
||||
const jwt = getCookie("jwt");
|
||||
try {
|
||||
await deleteAccount(deletePassword);
|
||||
|
||||
handleLogout();
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
setDeleteError(
|
||||
err?.response?.data?.error || t("leftSidebar.failedToDeleteAccount"),
|
||||
(err as { response?: { data?: { error?: string } } })?.response?.data
|
||||
?.error || t("leftSidebar.failedToDeleteAccount"),
|
||||
);
|
||||
setDeleteLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
@@ -19,7 +20,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<TabContextType | undefined>(undefined);
|
||||
@@ -98,7 +108,9 @@ export function TabProvider({ children }: TabProviderProps) {
|
||||
id,
|
||||
title: effectiveTitle,
|
||||
terminalRef:
|
||||
tabData.type === "terminal" ? React.createRef<any>() : undefined,
|
||||
tabData.type === "terminal"
|
||||
? React.createRef<{ disconnect?: () => void }>()
|
||||
: undefined,
|
||||
};
|
||||
setTabs((prev) => [...prev, newTab]);
|
||||
setCurrentTab(id);
|
||||
@@ -140,7 +152,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) {
|
||||
|
||||
@@ -4,12 +4,6 @@ import { Button } from "@/components/ui/button.tsx";
|
||||
import { ChevronDown, ChevronUpIcon, Hammer, FileText } from "lucide-react";
|
||||
import { Tab } from "@/ui/Desktop/Navigation/Tabs/Tab.tsx";
|
||||
import { useTabs } from "@/ui/Desktop/Navigation/Tabs/TabContext.tsx";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { Checkbox } from "@/components/ui/checkbox.tsx";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
@@ -18,6 +12,18 @@ import { TabDropdown } from "@/ui/Desktop/Navigation/Tabs/TabDropdown.tsx";
|
||||
import { getCookie, setCookie } from "@/ui/main-axios.ts";
|
||||
import { SnippetsSidebar } from "@/ui/Desktop/Apps/Terminal/SnippetsSidebar.tsx";
|
||||
|
||||
interface TabData {
|
||||
id: number;
|
||||
type: string;
|
||||
title: string;
|
||||
terminalRef?: {
|
||||
current?: {
|
||||
sendInput?: (data: string) => void;
|
||||
};
|
||||
};
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface TopNavbarProps {
|
||||
isTopbarOpen: boolean;
|
||||
setIsTopbarOpen: (open: boolean) => void;
|
||||
@@ -35,7 +41,14 @@ export function TopNavbar({
|
||||
setSplitScreenTab,
|
||||
removeTab,
|
||||
allSplitScreenTab,
|
||||
} = useTabs() as any;
|
||||
} = useTabs() as {
|
||||
tabs: TabData[];
|
||||
currentTab: number;
|
||||
setCurrentTab: (id: number) => void;
|
||||
setSplitScreenTab: (id: number) => void;
|
||||
removeTab: (id: number) => void;
|
||||
allSplitScreenTab: number[];
|
||||
};
|
||||
const leftPosition = state === "collapsed" ? "26px" : "264px";
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -192,7 +205,7 @@ export function TopNavbar({
|
||||
|
||||
if (commandToSend) {
|
||||
selectedTabIds.forEach((tabId) => {
|
||||
const tab = tabs.find((t: any) => t.id === tabId);
|
||||
const tab = tabs.find((t: TabData) => t.id === tabId);
|
||||
if (tab?.terminalRef?.current?.sendInput) {
|
||||
tab.terminalRef.current.sendInput(commandToSend);
|
||||
}
|
||||
@@ -206,7 +219,7 @@ export function TopNavbar({
|
||||
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
|
||||
const char = e.key;
|
||||
selectedTabIds.forEach((tabId) => {
|
||||
const tab = tabs.find((t: any) => t.id === tabId);
|
||||
const tab = tabs.find((t: TabData) => t.id === tabId);
|
||||
if (tab?.terminalRef?.current?.sendInput) {
|
||||
tab.terminalRef.current.sendInput(char);
|
||||
}
|
||||
@@ -215,7 +228,7 @@ export function TopNavbar({
|
||||
};
|
||||
|
||||
const handleSnippetExecute = (content: string) => {
|
||||
const tab = tabs.find((t: any) => t.id === currentTab);
|
||||
const tab = tabs.find((t: TabData) => t.id === currentTab);
|
||||
if (tab?.terminalRef?.current?.sendInput) {
|
||||
tab.terminalRef.current.sendInput(content + "\n");
|
||||
}
|
||||
@@ -223,13 +236,13 @@ export function TopNavbar({
|
||||
|
||||
const isSplitScreenActive =
|
||||
Array.isArray(allSplitScreenTab) && allSplitScreenTab.length > 0;
|
||||
const currentTabObj = tabs.find((t: any) => t.id === currentTab);
|
||||
const currentTabObj = tabs.find((t: TabData) => t.id === currentTab);
|
||||
const currentTabIsHome = currentTabObj?.type === "home";
|
||||
const currentTabIsSshManager = currentTabObj?.type === "ssh_manager";
|
||||
const currentTabIsAdmin = currentTabObj?.type === "admin";
|
||||
const currentTabIsUserProfile = currentTabObj?.type === "user_profile";
|
||||
|
||||
const terminalTabs = tabs.filter((tab: any) => tab.type === "terminal");
|
||||
const terminalTabs = tabs.filter((tab: TabData) => tab.type === "terminal");
|
||||
|
||||
const updateRightClickCopyPaste = (checked: boolean) => {
|
||||
setCookie("rightClickCopyPaste", checked.toString());
|
||||
@@ -246,7 +259,7 @@ export function TopNavbar({
|
||||
}}
|
||||
>
|
||||
<div className="h-full p-1 pr-2 border-r-2 border-dark-border w-[calc(100%-6rem)] flex items-center overflow-x-auto overflow-y-hidden gap-2 thin-scrollbar">
|
||||
{tabs.map((tab: any) => {
|
||||
{tabs.map((tab: TabData) => {
|
||||
const isActive = tab.id === currentTab;
|
||||
const isSplit =
|
||||
Array.isArray(allSplitScreenTab) &&
|
||||
|
||||
@@ -46,13 +46,17 @@ export function PasswordReset({ userInfo }: PasswordResetProps) {
|
||||
setError(null);
|
||||
setResetLoading(true);
|
||||
try {
|
||||
const result = await initiatePasswordReset(userInfo.username);
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
TabsTrigger,
|
||||
} from "@/components/ui/tabs.tsx";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import { User, Shield, Key, AlertCircle } from "lucide-react";
|
||||
import { User, Shield, AlertCircle } from "lucide-react";
|
||||
import { TOTPSetup } from "@/ui/Desktop/User/TOTPSetup.tsx";
|
||||
import { getUserInfo } from "@/ui/main-axios.ts";
|
||||
import { getVersionInfo } from "@/ui/main-axios.ts";
|
||||
@@ -45,7 +45,7 @@ export function UserProfile({ isTopbarOpen = true }: UserProfileProps) {
|
||||
try {
|
||||
const info = await getVersionInfo();
|
||||
setVersionInfo({ version: info.localVersion });
|
||||
} catch (err) {
|
||||
} catch {
|
||||
const { toast } = await import("sonner");
|
||||
toast.error(t("user.failedToLoadVersionInfo"));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { Status, StatusIndicator } from "@/components/ui/shadcn-io/status";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { ButtonGroup } from "@/components/ui/button-group.tsx";
|
||||
import { Server, Terminal } from "lucide-react";
|
||||
import { Terminal } from "lucide-react";
|
||||
import { getServerStatusById } from "@/ui/main-axios.ts";
|
||||
import { useTabs } from "@/ui/Mobile/Apps/Navigation/Tabs/TabContext.tsx";
|
||||
import type { HostProps } from "../../../../../types/index.js";
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -75,7 +82,7 @@ export function LeftSidebar({
|
||||
}: LeftSidebarProps) {
|
||||
const { t } = useTranslation();
|
||||
const [hosts, setHosts] = useState<SSHHost[]>([]);
|
||||
const [hostsLoading, setHostsLoading] = useState(false);
|
||||
const [hostsLoading] = useState(false);
|
||||
const [hostsError, setHostsError] = useState<string | null>(null);
|
||||
const prevHostsRef = React.useRef<SSHHost[]>([]);
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -90,7 +97,7 @@ export function LeftSidebar({
|
||||
setHosts(newHosts);
|
||||
prevHostsRef.current = newHosts;
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch {
|
||||
setHostsError(t("leftSidebar.failedToLoadHosts"));
|
||||
}
|
||||
}, [t]);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user
The type for
terminalRefin theTabContextTabinterface has been changed toReact.RefObject<HTMLElement>, which is incorrect. This ref is used for an imperative handle on theTerminalcomponent, which exposes methods likefit()andsendInput(). UsingHTMLElementloses type safety and could lead to runtime errors.A
TerminalHandleinterface is defined insrc/backend/ssh/terminal.ts. It would be best to move this interface to a shared types file (like this one) and useReact.RefObject<TerminalHandle>here and in other related components.