Handle Symlink in the File manager. #227

Merged
jedi04 merged 1 commits from feat/symlink into dev-1.7.0 2025-09-15 02:30:17 +00:00
jedi04 commented 2025-09-12 20:23:54 +00:00 (Migrated from github.com)

Fix handling of symlink when displayed in the File manager (#214 )

It identifies the type of target of symlink which is either file or directory and then accordingly moves ahead.

Summary by CodeRabbit

  • New Features
    • SSH File Manager now resolves symlinks: clicking a link opens its target (navigates to directories or opens files).
    • Symlinks are visually distinguished with a dedicated icon and are labeled “Link” in rename dialogs.
    • Added clear error toasts when resolving symlinks or reconnecting SSH sessions fails, with automatic reconnect attempts where possible.
Fix handling of symlink when displayed in the File manager (#214 ) It identifies the type of target of symlink which is either file or directory and then accordingly moves ahead. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * New Features * SSH File Manager now resolves symlinks: clicking a link opens its target (navigates to directories or opens files). * Symlinks are visually distinguished with a dedicated icon and are labeled “Link” in rename dialogs. * Added clear error toasts when resolving symlinks or reconnecting SSH sessions fails, with automatic reconnect attempts where possible. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
coderabbitai[bot] commented 2025-09-12 20:24:00 +00:00 (Migrated from github.com)

Walkthrough

Adds a new backend GET endpoint to identify symlinks over SSH, a UI API wrapper to call it, and UI logic to handle symlink clicks in the File Manager sidebar, resolving targets and navigating/opening accordingly. The backend route appears duplicated in the file.

Changes

Cohort / File(s) Summary
Backend: SSH symlink identification endpoint
src/backend/ssh/file-manager.ts
Adds GET endpoint at /ssh/file_manager/ssh/identifySymlink that validates sessionId and path, executes stat and readlink via the active SSH session, and returns { path, target, type }. Handles errors and updates session activity. Note: route added twice (duplicate definition).
UI: File Manager symlink handling
src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx
Introduces handleSymlinkClick(item) for SSH symlink resolution. Differentiates items of type "link", uses a FileSymlink icon, resolves via API, navigates to directories or opens files, and expands toasts/labels to include "Link". Manages SSH session status and reconnection.
API client wrapper
src/ui/main-axios.ts
Adds identifySSHSymlink(sessionId, path) that calls /ssh/identifySymlink via fileManagerApi and returns `{ path, target, type: "directory"

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant FM as FileManagerLeftSidebar (UI)
  participant API as main-axios identifySSHSymlink()
  participant BE as Backend route /ssh/.../identifySymlink
  participant SSH as Active SSH Session

  U->>FM: Click symlink item
  FM->>FM: Ensure SSH session (status/reconnect)
  alt Session OK
    FM->>API: identifySSHSymlink(sessionId, symlinkPath)
    API->>BE: GET /ssh/identifySymlink?sessionId&path
    BE->>SSH: Exec: stat/readlink on path
    alt Exec success
      SSH-->>BE: fileType, target
      BE-->>API: { path, target, type }
      API-->>FM: { path, target, type }
      alt type == directory
        FM->>FM: Navigate to target directory
      else type == file
        FM->>FM: Open target file
      end
    else Exec error / non-zero exit
      BE-->>API: 500 error
      API-->>FM: Error
      FM->>U: Toast: failed to resolve symlink
    end
  else Session not OK
    FM->>FM: Attempt reconnect
    alt Reconnect fails
      FM->>U: Toast: failed to connect/reconnect SSH
    end
  end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks (2 passed, 1 warning)

Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Passed checks (2 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check Passed The current title "Handle Symlink in the File manager." is concise and directly reflects the PR objective of fixing symlink handling in the File Manager; it matches the changeset which adds symlink resolution in backend, UI, and API layers and avoids extraneous details. It clearly communicates the primary change so a teammate scanning history will understand the intent.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

- Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
- Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

Finishing touches
  • 📝 Docstrings were successfully generated. (🔄 Check again to generate docstrings again)
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- walkthrough_start --> ## Walkthrough Adds a new backend GET endpoint to identify symlinks over SSH, a UI API wrapper to call it, and UI logic to handle symlink clicks in the File Manager sidebar, resolving targets and navigating/opening accordingly. The backend route appears duplicated in the file. ## Changes | Cohort / File(s) | Summary | | --- | --- | | **Backend: SSH symlink identification endpoint**<br>`src/backend/ssh/file-manager.ts` | Adds GET endpoint at `/ssh/file_manager/ssh/identifySymlink` that validates `sessionId` and `path`, executes `stat` and `readlink` via the active SSH session, and returns `{ path, target, type }`. Handles errors and updates session activity. Note: route added twice (duplicate definition). | | **UI: File Manager symlink handling**<br>`src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx` | Introduces `handleSymlinkClick(item)` for SSH symlink resolution. Differentiates items of type "link", uses a FileSymlink icon, resolves via API, navigates to directories or opens files, and expands toasts/labels to include "Link". Manages SSH session status and reconnection. | | **API client wrapper**<br>`src/ui/main-axios.ts` | Adds `identifySSHSymlink(sessionId, path)` that calls `/ssh/identifySymlink` via `fileManagerApi` and returns `{ path, target, type: "directory" | "file" }`, with error handling through `handleApiError`. | ## Sequence Diagram(s) ```mermaid sequenceDiagram autonumber participant U as User participant FM as FileManagerLeftSidebar (UI) participant API as main-axios identifySSHSymlink() participant BE as Backend route /ssh/.../identifySymlink participant SSH as Active SSH Session U->>FM: Click symlink item FM->>FM: Ensure SSH session (status/reconnect) alt Session OK FM->>API: identifySSHSymlink(sessionId, symlinkPath) API->>BE: GET /ssh/identifySymlink?sessionId&path BE->>SSH: Exec: stat/readlink on path alt Exec success SSH-->>BE: fileType, target BE-->>API: { path, target, type } API-->>FM: { path, target, type } alt type == directory FM->>FM: Navigate to target directory else type == file FM->>FM: Open target file end else Exec error / non-zero exit BE-->>API: 500 error API-->>FM: Error FM->>U: Toast: failed to resolve symlink end else Session not OK FM->>FM: Attempt reconnect alt Reconnect fails FM->>U: Toast: failed to connect/reconnect SSH end end ``` ## Estimated code review effort 🎯 3 (Moderate) | ⏱️ ~25 minutes <!-- pre_merge_checks_walkthrough_start --> ## Pre-merge checks (2 passed, 1 warning) <details> <summary>❌ Failed checks (1 warning)</summary> | Check name | Status | Explanation | Resolution | | :----------------: | :--------- | :----------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- | | Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run `@coderabbitai generate docstrings` to improve docstring coverage. | </details> <details> <summary>✅ Passed checks (2 passed)</summary> | Check name | Status | Explanation | | :---------------: | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. | | Title Check | ✅ Passed | The current title "Handle Symlink in the File manager." is concise and directly reflects the PR objective of fixing symlink handling in the File Manager; it matches the changeset which adds symlink resolution in backend, UI, and API layers and avoids extraneous details. It clearly communicates the primary change so a teammate scanning history will understand the intent. | </details> <!-- pre_merge_checks_walkthrough_end --> <!-- walkthrough_end --> <!-- announcements_start --> > [!TIP] > <details> > <summary>👮 Agentic pre-merge checks are now available in preview!</summary> > > Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs. > > - Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more. > - Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks. > > Please see the [documentation](https://docs.coderabbit.ai/pr-reviews/pre-merge-checks) for more information. > > Example: > > ```yaml > reviews: > pre_merge_checks: > custom_checks: > - name: "Undocumented Breaking Changes" > mode: "warning" > instructions: | > Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). > ``` > > Please share your feedback with us on this [Discord post](https://discord.com/channels/1134356397673414807/1414771631775158383). > > </details> <!-- announcements_end --> <!-- internal state start --> <!-- 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 --> <!-- internal state end --> <!-- finishing_touch_checkbox_start --> <details> <summary>✨ Finishing touches</summary> - [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Docstrings were successfully generated. (🔄 Check again to generate docstrings again) <details> <summary>🧪 Generate unit tests</summary> - [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create PR with unit tests - [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Post copyable unit tests in a comment </details> </details> <!-- finishing_touch_checkbox_end --> <!-- tips_start --> --- <sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub> <!-- tips_end -->
coderabbitai[bot] (Migrated from github.com) reviewed 2025-09-12 20:30:37 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (1)

335-357: Renaming a symlink uses the wrong oldPath (includes " -> target").

item.path for links contains “name -> target”, so mv will fail. Strip the suffix before calling rename.

Apply:

   const handleRename = async (item: any, newName: string) => {
     if (!sshSessionId || !newName.trim() || newName === item.name) {
       setRenamingItem(null);
       return;
     }
 
     try {
-      await renameSSHItem(sshSessionId, item.path, newName.trim());
+      const oldPath =
+        item.type === "link" && item.path.includes(" -> ")
+          ? item.path.split(" -> ")[0]
+          : item.path;
+      await renameSSHItem(sshSessionId, oldPath, newName.trim());
🧹 Nitpick comments (2)
src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (2)

380-446: Symlink resolution flow is solid; minor UX tweak for file open.

When opening a symlinked file, pass the display name without “ -> …” to avoid odd tab titles.

Apply:

-        onOpenFile({
-          name: item.name,
+        const displayName = item.name.includes(" -> ")
+          ? item.name.split(" -> ")[0]
+          : item.name;
+        onOpenFile({
+          name: displayName,
           path: symlinkInfo.target, // Use the target path, not the symlink path
           isSSH: item.isSSH,
           sshSessionId: currentSessionId,
         });

593-649: Consider allowing pinning links and folders (if product wants it).

Currently only files show the pin button. If users often pin directories or useful links, expose the same control for item.type === "directory" or "link".

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4dc310c93 and d5471d68a3.

📒 Files selected for processing (3)
  • src/backend/ssh/file-manager.ts (1 hunks)
  • src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (8 hunks)
  • src/ui/main-axios.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx}: Review TypeScript and React code for Termix server management platform. Key considerations:

Architecture & Patterns:

  • Follow the established multi-port backend architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085)
  • Use proper separation between Desktop and Mobile UI components
  • Maintain consistent state management patterns with React hooks and context
  • Follow the established tab-based navigation system

Database & Backend:

  • Use Drizzle ORM with SQLite for database operations
  • Implement proper JWT authentication middleware patterns
  • Follow the established API error handling patterns in main-axios.ts
  • Use proper logging with the structured logger system (apiLogger, authLogger, sshLogger, etc.)
  • Maintain proper input validation and sanitization

UI/UX Guidelines:

  • Use Shadcn/UI components with Tailwind CSS for consistent styling
  • Follow the established theme system with dark/light mode support
  • Use proper responsive design patterns for Desktop/Mobile views
  • Implement proper loading states and error handling
  • Use the established confirmation patterns with useConfirmation hook
  • Use CSS variables and classes from index.css instead of hardcoding colors
  • Follow the established color token system (--primary, --secondary, --background, etc.)
  • Use proper Tailwind CSS classes instead of inline styles
  • Implement proper focus states and accessibility indicators

SSH & Security:

  • Implement proper SSH connection management with session handling
  • Use secure credential storage and management patterns
  • Follow the established authentication flow (password, key, credential-based)
  • Implement proper file operation security and validation

Code Quality:

  • Use proper TypeScript types from the centralized types/index.ts
  • Follow the established API patterns in main-axios.ts
  • Implement proper error boundaries and fallback UI
  • Use proper React patterns (hooks, context, refs)
  • Maintain consistent naming conventions...

Files:

  • src/ui/main-axios.ts
  • src/backend/ssh/file-manager.ts
  • src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx
**/main-axios.ts

⚙️ CodeRabbit configuration file

**/main-axios.ts: Review main-axios.ts API client configuration for Termix server management platform. Key considerations:

API Client Architecture:

  • Maintain the multi-port API architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085)
  • Use proper service-specific API instances (sshHostApi, tunnelApi, fileManagerApi, statsApi, authApi)
  • Implement proper API instance creation with createApiInstance function
  • Use proper base URL configuration for different environments (dev, production, Electron)

Error Handling:

  • Use the centralized handleApiError function for consistent error handling
  • Implement proper error classification (auth, network, validation, server errors)
  • Use proper error logging with service-specific loggers
  • Implement proper error response formatting and user-friendly messages

Request/Response Interceptors:

  • Implement proper JWT token handling in request interceptors
  • Use proper request timing and performance logging
  • Implement proper response logging and error tracking
  • Use proper authentication token refresh and cleanup

API Function Organization:

  • Group API functions by service (SSH Host Management, Tunnel Management, File Manager, etc.)
  • Use proper TypeScript types for all API functions
  • Implement proper parameter validation and sanitization
  • Use proper return type definitions and error handling

Authentication:

  • Implement proper JWT token management and refresh
  • Use proper cookie handling for web and Electron environments
  • Implement proper authentication state management
  • Use proper token expiration and cleanup

Logging:

  • Use proper structured logging with context and metadata
  • Implement proper request/response logging with performance metrics
  • Use proper error logging with appropriate log levels
  • Implement proper service-specific logger selection

Performance:

  • Implement proper request timeout and retry logic
  • Use proper connection pooling and resource management
  • Implement pr...

Files:

  • src/ui/main-axios.ts
**/backend/**/*.{ts,js}

⚙️ CodeRabbit configuration file

**/backend/**/*.{ts,js}: Review backend code for Termix server management platform. Key considerations:

Backend Architecture:

  • Follow the multi-port microservice architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085)
  • Use Express.js with proper middleware patterns
  • Implement proper CORS and security headers
  • Use proper request/response logging with structured logging

Database Operations:

  • Use Drizzle ORM with proper schema definitions
  • Implement proper database migrations and schema updates
  • Use proper transaction handling for critical operations
  • Follow the established database connection patterns

Authentication & Security:

  • Implement proper JWT token validation and refresh
  • Use bcryptjs for password hashing with proper salt rounds
  • Implement proper input validation and sanitization
  • Use proper CORS configuration for security
  • Implement proper rate limiting and security headers

SSH Operations:

  • Use ssh2 library with proper connection management
  • Implement proper SSH key handling and validation
  • Use proper session management and cleanup
  • Implement proper error handling for SSH operations
  • Use proper file operation security and validation

API Design:

  • Follow RESTful API patterns with proper HTTP status codes
  • Implement proper error response formatting
  • Use proper request/response validation
  • Implement proper API versioning and backward compatibility
  • All API routes should be defined in main-axios.ts, not scattered across components
  • Use the established multi-port API architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085)
  • Follow the established error handling patterns with handleApiError function
  • Use proper structured logging with service-specific loggers (apiLogger, authLogger, sshLogger, etc.)

Logging & Monitoring:

  • Use the structured logging system with proper context
  • Implement proper error tracking and reporting
  • Use proper performance monitoring and metrics
  • Impleme...

Files:

  • src/backend/ssh/file-manager.ts
🧬 Code graph analysis (1)
src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (1)
src/ui/main-axios.ts (2)
  • getSSHStatus (945-956)
  • identifySSHSymlink (972-984)
🔇 Additional comments (6)
src/ui/main-axios.ts (1)

972-984: Symlink API wrapper looks correct and consistent with the backend path.

Base URL + route resolves to /ssh/file_manager/ssh/identifySymlink. Error handling flows through handleApiError. Typing matches backend response.

src/backend/ssh/file-manager.ts (1)

351-353: Note: Linux-specific commands.

stat -L -c "%F" and readlink -f aren’t portable to macOS/BSD. If non-Linux hosts are supported, fall back to POSIX alternatives or use sftp.stat().

Would you like me to put together a small compatibility shim that detects OS and picks the right command, or an sftp.stat()-based variant?

src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (4)

11-11: Icon import for symlinks — good addition for clarity.


33-34: API import aligns with main-axios export.


529-531: Symlink icon rendering — looks good and consistent.

Also applies to: 583-585


571-579: Click handling for links correctly routes to symlink resolver.

**Actionable comments posted: 2** > [!CAUTION] > Some comments are outside the diff and can’t be posted inline due to platform limitations. > > > > <details> > <summary>⚠️ Outside diff range comments (1)</summary><blockquote> > > <details> > <summary>src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (1)</summary><blockquote> > > `335-357`: **Renaming a symlink uses the wrong oldPath (includes " -> target").** > > item.path for links contains “name -> target”, so mv will fail. Strip the suffix before calling rename. > > Apply: > > ```diff > const handleRename = async (item: any, newName: string) => { > if (!sshSessionId || !newName.trim() || newName === item.name) { > setRenamingItem(null); > return; > } > > try { > - await renameSSHItem(sshSessionId, item.path, newName.trim()); > + const oldPath = > + item.type === "link" && item.path.includes(" -> ") > + ? item.path.split(" -> ")[0] > + : item.path; > + await renameSSHItem(sshSessionId, oldPath, newName.trim()); > ``` > > </blockquote></details> > > </blockquote></details> <details> <summary>🧹 Nitpick comments (2)</summary><blockquote> <details> <summary>src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (2)</summary><blockquote> `380-446`: **Symlink resolution flow is solid; minor UX tweak for file open.** When opening a symlinked file, pass the display name without “ -> …” to avoid odd tab titles. Apply: ```diff - onOpenFile({ - name: item.name, + const displayName = item.name.includes(" -> ") + ? item.name.split(" -> ")[0] + : item.name; + onOpenFile({ + name: displayName, path: symlinkInfo.target, // Use the target path, not the symlink path isSSH: item.isSSH, sshSessionId: currentSessionId, }); ``` --- `593-649`: **Consider allowing pinning links and folders (if product wants it).** Currently only files show the pin button. If users often pin directories or useful links, expose the same control for item.type === "directory" or "link". </blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used**: Path: .coderabbit.yaml **Review profile**: CHILL **Plan**: Pro <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between d4dc310c93016c231df8f34165c619bfb1b3f723 and d5471d68a30cb3101d4b8ee7a230f2c9e1ff7119. </details> <details> <summary>📒 Files selected for processing (3)</summary> * `src/backend/ssh/file-manager.ts` (1 hunks) * `src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx` (8 hunks) * `src/ui/main-axios.ts` (1 hunks) </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>📓 Path-based instructions (3)</summary> <details> <summary>**/*.{ts,tsx}</summary> **⚙️ CodeRabbit configuration file** > `**/*.{ts,tsx}`: Review TypeScript and React code for Termix server management platform. Key considerations: > > **Architecture & Patterns:** > - Follow the established multi-port backend architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085) > - Use proper separation between Desktop and Mobile UI components > - Maintain consistent state management patterns with React hooks and context > - Follow the established tab-based navigation system > > **Database & Backend:** > - Use Drizzle ORM with SQLite for database operations > - Implement proper JWT authentication middleware patterns > - Follow the established API error handling patterns in main-axios.ts > - Use proper logging with the structured logger system (apiLogger, authLogger, sshLogger, etc.) > - Maintain proper input validation and sanitization > > **UI/UX Guidelines:** > - Use Shadcn/UI components with Tailwind CSS for consistent styling > - Follow the established theme system with dark/light mode support > - Use proper responsive design patterns for Desktop/Mobile views > - Implement proper loading states and error handling > - Use the established confirmation patterns with useConfirmation hook > - Use CSS variables and classes from index.css instead of hardcoding colors > - Follow the established color token system (--primary, --secondary, --background, etc.) > - Use proper Tailwind CSS classes instead of inline styles > - Implement proper focus states and accessibility indicators > > **SSH & Security:** > - Implement proper SSH connection management with session handling > - Use secure credential storage and management patterns > - Follow the established authentication flow (password, key, credential-based) > - Implement proper file operation security and validation > > **Code Quality:** > - Use proper TypeScript types from the centralized types/index.ts > - Follow the established API patterns in main-axios.ts > - Implement proper error boundaries and fallback UI > - Use proper React patterns (hooks, context, refs) > - Maintain consistent naming conventions... Files: - `src/ui/main-axios.ts` - `src/backend/ssh/file-manager.ts` - `src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx` </details> <details> <summary>**/main-axios.ts</summary> **⚙️ CodeRabbit configuration file** > `**/main-axios.ts`: Review main-axios.ts API client configuration for Termix server management platform. Key considerations: > > **API Client Architecture:** > - Maintain the multi-port API architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085) > - Use proper service-specific API instances (sshHostApi, tunnelApi, fileManagerApi, statsApi, authApi) > - Implement proper API instance creation with createApiInstance function > - Use proper base URL configuration for different environments (dev, production, Electron) > > **Error Handling:** > - Use the centralized handleApiError function for consistent error handling > - Implement proper error classification (auth, network, validation, server errors) > - Use proper error logging with service-specific loggers > - Implement proper error response formatting and user-friendly messages > > **Request/Response Interceptors:** > - Implement proper JWT token handling in request interceptors > - Use proper request timing and performance logging > - Implement proper response logging and error tracking > - Use proper authentication token refresh and cleanup > > **API Function Organization:** > - Group API functions by service (SSH Host Management, Tunnel Management, File Manager, etc.) > - Use proper TypeScript types for all API functions > - Implement proper parameter validation and sanitization > - Use proper return type definitions and error handling > > **Authentication:** > - Implement proper JWT token management and refresh > - Use proper cookie handling for web and Electron environments > - Implement proper authentication state management > - Use proper token expiration and cleanup > > **Logging:** > - Use proper structured logging with context and metadata > - Implement proper request/response logging with performance metrics > - Use proper error logging with appropriate log levels > - Implement proper service-specific logger selection > > **Performance:** > - Implement proper request timeout and retry logic > - Use proper connection pooling and resource management > - Implement pr... Files: - `src/ui/main-axios.ts` </details> <details> <summary>**/backend/**/*.{ts,js}</summary> **⚙️ CodeRabbit configuration file** > `**/backend/**/*.{ts,js}`: Review backend code for Termix server management platform. Key considerations: > > **Backend Architecture:** > - Follow the multi-port microservice architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085) > - Use Express.js with proper middleware patterns > - Implement proper CORS and security headers > - Use proper request/response logging with structured logging > > **Database Operations:** > - Use Drizzle ORM with proper schema definitions > - Implement proper database migrations and schema updates > - Use proper transaction handling for critical operations > - Follow the established database connection patterns > > **Authentication & Security:** > - Implement proper JWT token validation and refresh > - Use bcryptjs for password hashing with proper salt rounds > - Implement proper input validation and sanitization > - Use proper CORS configuration for security > - Implement proper rate limiting and security headers > > **SSH Operations:** > - Use ssh2 library with proper connection management > - Implement proper SSH key handling and validation > - Use proper session management and cleanup > - Implement proper error handling for SSH operations > - Use proper file operation security and validation > > **API Design:** > - Follow RESTful API patterns with proper HTTP status codes > - Implement proper error response formatting > - Use proper request/response validation > - Implement proper API versioning and backward compatibility > - All API routes should be defined in main-axios.ts, not scattered across components > - Use the established multi-port API architecture (SSH: 8081, Tunnel: 8083, File Manager: 8084, Stats: 8085) > - Follow the established error handling patterns with handleApiError function > - Use proper structured logging with service-specific loggers (apiLogger, authLogger, sshLogger, etc.) > > **Logging & Monitoring:** > - Use the structured logging system with proper context > - Implement proper error tracking and reporting > - Use proper performance monitoring and metrics > - Impleme... Files: - `src/backend/ssh/file-manager.ts` </details> </details><details> <summary>🧬 Code graph analysis (1)</summary> <details> <summary>src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (1)</summary><blockquote> <details> <summary>src/ui/main-axios.ts (2)</summary> * `getSSHStatus` (945-956) * `identifySSHSymlink` (972-984) </details> </blockquote></details> </details> </details> <details> <summary>🔇 Additional comments (6)</summary><blockquote> <details> <summary>src/ui/main-axios.ts (1)</summary><blockquote> `972-984`: **Symlink API wrapper looks correct and consistent with the backend path.** Base URL + route resolves to /ssh/file_manager/ssh/identifySymlink. Error handling flows through handleApiError. Typing matches backend response. </blockquote></details> <details> <summary>src/backend/ssh/file-manager.ts (1)</summary><blockquote> `351-353`: **Note: Linux-specific commands.** stat -L -c "%F" and readlink -f aren’t portable to macOS/BSD. If non-Linux hosts are supported, fall back to POSIX alternatives or use sftp.stat(). Would you like me to put together a small compatibility shim that detects OS and picks the right command, or an sftp.stat()-based variant? </blockquote></details> <details> <summary>src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (4)</summary><blockquote> `11-11`: **Icon import for symlinks — good addition for clarity.** --- `33-34`: **API import aligns with main-axios export.** --- `529-531`: **Symlink icon rendering — looks good and consistent.** Also applies to: 583-585 --- `571-579`: **Click handling for links correctly routes to symlink resolver.** </blockquote></details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@@ -332,0 +374,4 @@
`SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
);
return res.status(500).json({ error: `Command failed: ${errorData}` });
}
coderabbitai[bot] (Migrated from github.com) commented 2025-09-12 20:30:37 +00:00

🛠️ Refactor suggestion

Return a specific status for broken links.

If stderr contains “No such file or directory”, respond with 404 to let the UI show a clearer toast for broken symlinks instead of a generic 500.

Apply:

-      if (code !== 0) {
+      if (code !== 0) {
         fileLogger.error(
           `SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
         );
-        return res.status(500).json({ error: `Command failed: ${errorData}` });
+        const notFound = /No such file or directory/i.test(errorData);
+        return res.status(notFound ? 404 : 500).json({
+          error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`,
+        });
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    stream.on("close", (code) => {
      if (code !== 0) {
        fileLogger.error(
          `SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
        );
        const notFound = /No such file or directory/i.test(errorData);
        return res.status(notFound ? 404 : 500).json({
          error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`,
        });
      }
🤖 Prompt for AI Agents
In src/backend/ssh/file-manager.ts around lines 371-377, the handler currently
maps any non-zero SSH command exit to a 500; change it to detect broken symlink
errors by inspecting errorData (stderr) for "No such file or directory"
(case-insensitive) and return res.status(404). Keep the fileLogger.error call
but include the detected error text and indicate it's a missing target;
otherwise preserve the existing 500 behavior and message for other errors.
_🛠️ Refactor suggestion_ **Return a specific status for broken links.** If stderr contains “No such file or directory”, respond with 404 to let the UI show a clearer toast for broken symlinks instead of a generic 500. Apply: ```diff - if (code !== 0) { + if (code !== 0) { fileLogger.error( `SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`, ); - return res.status(500).json({ error: `Command failed: ${errorData}` }); + const notFound = /No such file or directory/i.test(errorData); + return res.status(notFound ? 404 : 500).json({ + error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`, + }); } ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion stream.on("close", (code) => { if (code !== 0) { fileLogger.error( `SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`, ); const notFound = /No such file or directory/i.test(errorData); return res.status(notFound ? 404 : 500).json({ error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`, }); } ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In src/backend/ssh/file-manager.ts around lines 371-377, the handler currently maps any non-zero SSH command exit to a 500; change it to detect broken symlink errors by inspecting errorData (stderr) for "No such file or directory" (case-insensitive) and return res.status(404). Keep the fileLogger.error call but include the detected error text and indicate it's a missing target; otherwise preserve the existing 500 behavior and message for other errors. ``` </details> <!-- fingerprinting:phantom:medusa:chinchilla --> <!-- This is an auto-generated comment by CodeRabbit -->
@@ -332,0 +393,4 @@
});
});
});
coderabbitai[bot] (Migrated from github.com) commented 2025-09-12 20:30:37 +00:00

⚠️ Potential issue

Guard against missing/invalid path and harden output parsing.

decodeURIComponent is called before validating presence; undefined becomes the string "undefined" and the command runs against that path. Also, parsing assumes exactly two non-empty lines. Tighten both.

Apply:

-app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => {
-  const sessionId = req.query.sessionId as string;
-  const sshConn = sshSessions[sessionId];
-  const linkPath = decodeURIComponent(req.query.path as string);
+app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => {
+  const sessionId = req.query.sessionId as string;
+  const sshConn = sshSessions[sessionId];
+  const rawPath = req.query.path;
+  if (typeof rawPath !== "string" || rawPath.trim() === "") {
+    return res.status(400).json({ error: "Link path is required" });
+  }
+  const linkPath = decodeURIComponent(rawPath);
@@
-  sshConn.client.exec(command, (err, stream) => {
+  sshConn.client.exec(command, (err, stream) => {
@@
-    stream.on("close", (code) => {
+    stream.on("close", (code) => {
       if (code !== 0) {
@@
-      const [fileType, target] = data.trim().split("\n");
-      
+      const lines = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
+      if (lines.length < 2) {
+        fileLogger.error(`SSH identifySymlink command returned unexpected output: ${JSON.stringify(lines)}`);
+        return res.status(500).json({ error: "Unexpected command output while resolving link" });
+      }
+      const [fileType, target] = lines;
+
       res.json({
         path: linkPath,
         target: target,
         type: fileType.toLowerCase().includes("directory") ? "directory" : "file"
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => {
  const sessionId = req.query.sessionId as string;
  const sshConn = sshSessions[sessionId];
  const rawPath = req.query.path;
  if (typeof rawPath !== "string" || rawPath.trim() === "") {
    return res.status(400).json({ error: "Link path is required" });
  }
  const linkPath = decodeURIComponent(rawPath);

  if (!sessionId) {
    return res.status(400).json({ error: "Session ID is required" });
  }

  if (!sshConn?.isConnected) {
    return res.status(400).json({ error: "SSH connection not established" });
  }

  if (!linkPath) {
    return res.status(400).json({ error: "Link path is required" });
  }

  sshConn.lastActive = Date.now();

  const escapedPath = linkPath.replace(/'/g, "'\"'\"'");
  const command = `stat -L -c "%F" '${escapedPath}' && readlink -f '${escapedPath}'`;

  sshConn.client.exec(command, (err, stream) => {
    if (err) {
      fileLogger.error("SSH identifySymlink error:", err);
      return res.status(500).json({ error: err.message });
    }

    let data = "";
    let errorData = "";

    stream.on("data", (chunk: Buffer) => {
      data += chunk.toString();
    });

    stream.stderr.on("data", (chunk: Buffer) => {
      errorData += chunk.toString();
    });

    stream.on("close", (code) => {
      if (code !== 0) {
        fileLogger.error(
          `SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
        );
        return res.status(500).json({ error: `Command failed: ${errorData}` });
      }

      const lines = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
      if (lines.length < 2) {
        fileLogger.error(`SSH identifySymlink command returned unexpected output: ${JSON.stringify(lines)}`);
        return res.status(500).json({ error: "Unexpected command output while resolving link" });
      }
      const [fileType, target] = lines;
      
      res.json({
        path: linkPath,
        target: target,
        type: fileType.toLowerCase().includes("directory") ? "directory" : "file"
      });
    });

    stream.on("error", (streamErr) => {
      fileLogger.error("SSH identifySymlink stream error:", streamErr);
      if (!res.headersSent) {
        res.status(500).json({ error: `Stream error: ${streamErr.message}` });
      }
    });
  });
});
🤖 Prompt for AI Agents
In src/backend/ssh/file-manager.ts around lines 332-396, decodeURIComponent is
called before validating the raw query path and the output parsing assumes
exactly two non-empty lines; fix by first reading and validating req.query.path
(return 400 if missing/empty), then safely decode it inside a try/catch (return
400 if decoding fails), continue to escape the decoded path for the shell, and
after command completes robustly parse output by trimming and splitting into
lines, treating missing lines gracefully (e.g. set target = lines[1] || "" and
fileType = lines[0] || "" and if fileType is empty return 500 or an informative
error), and ensure responses are only sent once (check res.headersSent) when
handling stream errors or command failures.
_⚠️ Potential issue_ **Guard against missing/invalid path and harden output parsing.** decodeURIComponent is called before validating presence; undefined becomes the string "undefined" and the command runs against that path. Also, parsing assumes exactly two non-empty lines. Tighten both. Apply: ```diff -app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => { - const sessionId = req.query.sessionId as string; - const sshConn = sshSessions[sessionId]; - const linkPath = decodeURIComponent(req.query.path as string); +app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => { + const sessionId = req.query.sessionId as string; + const sshConn = sshSessions[sessionId]; + const rawPath = req.query.path; + if (typeof rawPath !== "string" || rawPath.trim() === "") { + return res.status(400).json({ error: "Link path is required" }); + } + const linkPath = decodeURIComponent(rawPath); @@ - sshConn.client.exec(command, (err, stream) => { + sshConn.client.exec(command, (err, stream) => { @@ - stream.on("close", (code) => { + stream.on("close", (code) => { if (code !== 0) { @@ - const [fileType, target] = data.trim().split("\n"); - + const lines = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean); + if (lines.length < 2) { + fileLogger.error(`SSH identifySymlink command returned unexpected output: ${JSON.stringify(lines)}`); + return res.status(500).json({ error: "Unexpected command output while resolving link" }); + } + const [fileType, target] = lines; + res.json({ path: linkPath, target: target, type: fileType.toLowerCase().includes("directory") ? "directory" : "file" }); ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => { const sessionId = req.query.sessionId as string; const sshConn = sshSessions[sessionId]; const rawPath = req.query.path; if (typeof rawPath !== "string" || rawPath.trim() === "") { return res.status(400).json({ error: "Link path is required" }); } const linkPath = decodeURIComponent(rawPath); if (!sessionId) { return res.status(400).json({ error: "Session ID is required" }); } if (!sshConn?.isConnected) { return res.status(400).json({ error: "SSH connection not established" }); } if (!linkPath) { return res.status(400).json({ error: "Link path is required" }); } sshConn.lastActive = Date.now(); const escapedPath = linkPath.replace(/'/g, "'\"'\"'"); const command = `stat -L -c "%F" '${escapedPath}' && readlink -f '${escapedPath}'`; sshConn.client.exec(command, (err, stream) => { if (err) { fileLogger.error("SSH identifySymlink error:", err); return res.status(500).json({ error: err.message }); } let data = ""; let errorData = ""; stream.on("data", (chunk: Buffer) => { data += chunk.toString(); }); stream.stderr.on("data", (chunk: Buffer) => { errorData += chunk.toString(); }); stream.on("close", (code) => { if (code !== 0) { fileLogger.error( `SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`, ); return res.status(500).json({ error: `Command failed: ${errorData}` }); } const lines = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean); if (lines.length < 2) { fileLogger.error(`SSH identifySymlink command returned unexpected output: ${JSON.stringify(lines)}`); return res.status(500).json({ error: "Unexpected command output while resolving link" }); } const [fileType, target] = lines; res.json({ path: linkPath, target: target, type: fileType.toLowerCase().includes("directory") ? "directory" : "file" }); }); stream.on("error", (streamErr) => { fileLogger.error("SSH identifySymlink stream error:", streamErr); if (!res.headersSent) { res.status(500).json({ error: `Stream error: ${streamErr.message}` }); } }); }); }); ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In src/backend/ssh/file-manager.ts around lines 332-396, decodeURIComponent is called before validating the raw query path and the output parsing assumes exactly two non-empty lines; fix by first reading and validating req.query.path (return 400 if missing/empty), then safely decode it inside a try/catch (return 400 if decoding fails), continue to escape the decoded path for the shell, and after command completes robustly parse output by trimming and splitting into lines, treating missing lines gracefully (e.g. set target = lines[1] || "" and fileType = lines[0] || "" and if fileType is empty return 500 or an informative error), and ensure responses are only sent once (check res.headersSent) when handling stream errors or command failures. ``` </details> <!-- fingerprinting:phantom:medusa:chinchilla --> <!-- This is an auto-generated comment by CodeRabbit -->
LukeGus commented 2025-09-12 20:39:10 +00:00 (Migrated from github.com)

I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).

I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).
jedi04 commented 2025-09-12 20:58:43 +00:00 (Migrated from github.com)

I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).

Surely thank you. I was working in the dev branch but then saw you merged it so raised against main.

> I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them). Surely thank you. I was working in the dev branch but then saw you merged it so raised against main.
coderabbitai[bot] commented 2025-09-14 23:08:13 +00:00 (Migrated from github.com)

Caution

An unexpected error occurred while opening a pull request: Reference update failed - https://docs.github.com/rest/git/refs#create-a-reference

> [!CAUTION] > An unexpected error occurred while opening a pull request: Reference update failed - https://docs.github.com/rest/git/refs#create-a-reference
Sign in to join this conversation.