Commit Graph
22 Commits
Author SHA1 Message Date
ZacharyZcRandClaude fc6acbb81f FIX: Implement comprehensive autostart tunnel system with credential automation
This commit completely resolves the autostart tunnel functionality issues by:

**Core Autostart System**:
- Fixed internal API to return explicit autostart fields to tunnel service
- Implemented automatic endpoint credential resolution during autostart enable
- Enhanced database synchronization with force save and verification
- Added comprehensive debugging and logging throughout the process

**Tunnel Connection Improvements**:
- Enhanced credential resolution with priority: TunnelConnection → autostart → encrypted
- Fixed SSH command format with proper tunnel markers and exec process naming
- Added connection state protection to prevent premature cleanup during establishment
- Implemented sequential kill strategies for reliable remote process cleanup

**Type System Extensions**:
- Extended TunnelConnection interface with endpoint credential fields
- Added autostart credential fields to SSHHost interface for plaintext storage
- Maintained backward compatibility with existing encrypted credential system

**Key Technical Fixes**:
- Database API now includes /db/host/internal/all endpoint with SystemCrypto auth
- Autostart enable automatically populates endpoint credentials from target hosts
- Tunnel cleanup uses multiple kill strategies with verification and delay timing
- Connection protection prevents cleanup interference during tunnel establishment

Users can now enable fully automated tunneling by simply checking the autostart
checkbox - no manual credential configuration required. The system automatically
resolves and stores plaintext credentials for unattended tunnel operation.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 07:30:21 +08:00
ZacharyZcRandClaude ece6ec0892 FIX: Comprehensive file manager UI/UX improvements and bug fixes
- Fix missing i18n for terminal.terminalWithPath translation key
- Update keyboard shortcuts: remove Ctrl+T conflicts, change refresh to Ctrl+Y, rename shortcut to F6
- Remove click-to-rename functionality to prevent accidental renaming
- Fix drag preview z-index and positioning issues during file operations
- Remove false download trigger when dragging files to original position
- Fix 'Must be handling a user gesture' error in drag-to-desktop functionality
- Remove useless minimize button from file editor and diff viewer windows
- Improve context menu z-index hierarchy for better layering
- Add comprehensive drag state management and visual feedback

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 05:38:30 +08:00
ZacharyZcRandClaude 5f5397b924 FIX: Resolve SSH session timeout and disconnection issues
Fixed SSH sessions being automatically removed after a few minutes of
inactivity, causing connection errors when users return to the interface.

## Problems Identified:

### 1. Aggressive Session Timeout
**Issue**: Sessions were cleaned up after only 10 minutes of inactivity
- Too short for typical user workflows
- No warning or graceful handling when timeout occurs
- Users would get connection errors without explanation

### 2. No Session Keepalive Mechanism
**Issue**: No frontend keepalive to maintain active sessions
- Sessions would timeout even if user was actively viewing files
- No periodic communication to extend session lifetime
- No way to detect session expiration proactively

### 3. Server-side SSH Configuration
**Issue**: While SSH had keepalive settings, they weren't sufficient
- keepaliveInterval: 30000ms (30s)
- keepaliveCountMax: 3
- But no application-level session management

## Technical Solution:

### **Extended Session Timeout**
```typescript
// Increased from 10 minutes to 30 minutes
session.timeout = setTimeout(() => {
  fileLogger.info(`Cleaning up inactive SSH session: ${sessionId}`);
  cleanupSession(sessionId);
}, 30 * 60 * 1000); // 30 minutes
```

### **Backend Keepalive Endpoint**
```typescript
// New endpoint: POST /ssh/file_manager/ssh/keepalive
app.post("/ssh/file_manager/ssh/keepalive", (req, res) => {
  const session = sshSessions[sessionId];
  session.lastActive = Date.now();
  scheduleSessionCleanup(sessionId); // Reset timeout
  res.json({ status: "success", connected: true });
});
```

### **Frontend Automatic Keepalive**
```typescript
// Send keepalive every 5 minutes
keepaliveTimerRef.current = setInterval(async () => {
  if (sshSessionId) {
    await keepSSHAlive(sshSessionId);
  }
}, 5 * 60 * 1000);
```

## Session Management Flow:

**Before (Problematic):**
1. User connects → 10-minute countdown starts
2. User leaves browser open but inactive
3. Session times out after 10 minutes
4. User returns → "SSH session not found" error
5. User forced to reconnect manually

**After (Fixed):**
1. User connects → 30-minute countdown starts
2. Frontend sends keepalive every 5 minutes automatically
3. Each keepalive resets the 30-minute timeout
4. Session stays alive as long as browser tab is open
5. Graceful handling if keepalive fails

## Benefits:
✅ **Extended Session Lifetime**: 30 minutes vs 10 minutes base timeout
✅ **Automatic Session Maintenance**: Keepalive every 5 minutes
✅ **Transparent to User**: No manual intervention required
✅ **Robust Error Handling**: Graceful degradation if keepalive fails
✅ **Resource Efficient**: Only active sessions consume resources
✅ **Better User Experience**: No unexpected disconnections

Sessions now persist for the entire duration users have the file
manager open, eliminating frustrating timeout errors.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 05:03:57 +08:00
ZacharyZcRandClaude ff2bf474ee FIX: Resolve rapid clicking and navigation issues in file manager
Fixed race conditions and loading problems when users click folders
or navigation buttons too quickly.

## Problems Identified:

### 1. Race Conditions in Path Changes
**Issue**: Fast clicking folders/back button caused multiple simultaneous requests
- useEffect triggered on every currentPath change
- No debouncing for path changes (only for manual refresh)
- Multiple loadDirectory() calls executed concurrently
- Later responses could overwrite earlier ones

### 2. Concurrent Request Conflicts
**Issue**: loadDirectory() had basic isLoading check but insufficient protection
- Multiple requests could run if timing was right
- No tracking of which request was current
- Stale responses could update UI incorrectly

### 3. Missing Request Cancellation
**Issue**: No way to cancel outdated requests when user navigates rapidly
- Old requests would complete and show wrong directory
- Confusing UI state when mixed responses arrived

## Technical Solution:

### **Path Change Debouncing**
```typescript
// Added 150ms debounce specifically for path changes
const debouncedLoadDirectory = useCallback((path: string) => {
  if (pathChangeTimerRef.current) {
    clearTimeout(pathChangeTimerRef.current);
  }
  pathChangeTimerRef.current = setTimeout(() => {
    if (path !== lastPathChangeRef.current && sshSessionId) {
      loadDirectory(path);
    }
  }, 150);
}, [sshSessionId, loadDirectory]);
```

### **Request Race Condition Protection**
```typescript
// Track current loading path for proper cancellation
const currentLoadingPathRef = useRef<string>("");

// Enhanced concurrent request prevention
if (isLoading && currentLoadingPathRef.current !== path) {
  console.log("Directory loading already in progress, skipping:", path);
  return;
}
```

### **Stale Response Handling**
```typescript
// Check if response is still relevant before updating UI
if (currentLoadingPathRef.current !== path) {
  console.log("Directory load canceled, newer request in progress:", path);
  return; // Discard stale response
}
```

## Flow Improvements:

**Before (Problematic):**
1. User clicks folder A → currentPath changes → useEffect → loadDirectory(A)
2. User quickly clicks folder B → currentPath changes → useEffect → loadDirectory(B)
3. Both requests run concurrently
4. Response A or B arrives randomly, wrong folder might show

**After (Fixed):**
1. User clicks folder A → currentPath changes → debouncedLoadDirectory(A)
2. User quickly clicks folder B → currentPath changes → cancels A timer → debouncedLoadDirectory(B)
3. Only request B executes after 150ms
4. If A somehow runs, its response is discarded as stale

## User Experience:
✅ Rapid folder navigation works smoothly
✅ Back button rapid clicking handled properly
✅ No more loading wrong directories
✅ Proper loading states maintained
✅ No duplicate API requests
✅ Responsive feel with 150ms debounce (fast enough to feel instant)

The file manager now handles rapid user interactions gracefully without
race conditions or loading the wrong directory content.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 05:00:42 +08:00
ZacharyZcRandClaude ff1f3829bc FIX: Resolve critical window resizing issues in file manager
Fixed window resizing functionality that was completely broken due to
coordinate system confusion and incorrect variable usage.

## Critical Issues Found:

### 1. Variable Type Confusion
**Problem**: windowStart was used for both positions AND dimensions
- handleResizeStart: set windowStart = {x: size.width, y: size.height} (dimensions)
- handleMouseMove: used windowStart as position coordinates (x, y)
- This caused windows to jump to incorrect positions during resize

### 2. Incorrect Delta Calculations
**Problem**: Resize deltas were applied incorrectly
- Left/top resizing used wrong baseline values
- Position updates didn't account for size changes properly
- No proper viewport boundary checking

### 3. Missing State Separation
**Problem**: Conflated drag start positions with resize start dimensions

## Technical Solution:

**Separated State Variables:**
```typescript
const [windowStart, setWindowStart] = useState({ x: 0, y: 0 });     // Position
const [sizeStart, setSizeStart] = useState({ width: 0, height: 0 }); // Dimensions
```

**Fixed Resize Logic:**
- windowStart: tracks initial position during resize
- sizeStart: tracks initial dimensions during resize
- Proper delta calculations for all resize directions
- Correct position updates for left/top edge resizing

**Improved Coordinate Handling:**
- Right/bottom: simple addition to initial size
- Left/top: size change + position compensation
- Proper viewport boundary constraints
- Consistent minimum size enforcement

## Resize Directions Now Work Correctly:
✅ Right edge: expands width rightward
✅ Left edge: expands width leftward + moves position
✅ Bottom edge: expands height downward
✅ Top edge: expands height upward + moves position
✅ All corner combinations work properly
✅ Minimum size constraints respected
✅ Viewport boundaries enforced

**User Experience:**
- No more window "jumping around" during resize
- Smooth, predictable resize behavior
- Proper cursor feedback during resize operations
- Windows stay within viewport bounds

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 04:56:18 +08:00
ZacharyZcRandClaude 2060b75dd3 FIX: Eliminate jarring loading state transition in file manager connection
Fixed the brief jarring flash between SSH connection and file list display:

## Problem
During file manager connection process:
1. SSH connection completes → setIsLoading(false)
2. Brief empty/intermediate state displayed (jarring flash)
3. useEffect triggers → setIsLoading(true) again
4. Directory loads → setIsLoading(false)
5. Files finally displayed

This created a jarring user experience with double loading states.

## Root Cause
- initializeSSHConnection() only handled SSH connection
- File directory loading was handled separately in useEffect
- Gap between connection completion and directory loading caused UI flash

## Solution
**Unified Connection + Directory Loading:**
- Modified initializeSSHConnection() to load initial directory immediately after SSH connection
- Added initialLoadDoneRef to prevent duplicate loading in useEffect
- Loading state now remains true until both connection AND directory are ready

**Technical Changes:**
- SSH connection + initial directory load happen atomically
- useEffect skips initial load, only handles path changes
- No more intermediate states or double loading indicators

## Flow Now:
1. setIsLoading(true) → "Connecting..."
2. SSH connection establishes
3. Initial directory loads immediately
4. setIsLoading(false) → Files displayed seamlessly

**User Experience:**
✅ Smooth single loading state until everything is ready
✅ No jarring flashes or intermediate states
✅ Immediate file display after connection
✅ Maintains proper loading states for path changes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 04:53:28 +08:00
ZacharyZcRandClaude 42d4ba90cd FIX: Add missing i18n translation for admin.encryptionEnabled
Added missing translation key for database security settings:

Problem:
- AdminSettings.tsx used t("admin.encryptionEnabled")
- Translation key was missing from both English and Chinese files
- Caused missing text in database security encryption status display

Solution:
- Added "encryptionEnabled": "Encryption Enabled" to English translations
- Added "encryptionEnabled": "加密已启用" to Chinese translations
- Maintains consistency with existing encryption-related translations

Files updated:
- src/locales/en/translation.json
- src/locales/zh/translation.json

Now the database security section properly displays encryption status
with correct i18n support in both languages.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 04:46:24 +08:00
ZacharyZcRandClaude fce0447bfe FIX: Resolve translation function error in file manager creation components
Fixed "ReferenceError: t is not defined" when creating new files/folders:

Problem:
- CreateIntentGridItem and CreateIntentListItem components used t() function
- But neither component had useTranslation hook imported
- Caused runtime error when trying to create new files or folders

Solution:
- Added const { t } = useTranslation(); to both components
- Fixed hardcoded English text in CreateIntentListItem placeholder
- Now uses proper i18n translation keys for all UI text

Changes:
- CreateIntentGridItem: Added useTranslation hook
- CreateIntentListItem: Added useTranslation hook + fixed placeholder text
- Both components now properly use t('fileManager.folderName') and t('fileManager.fileName')

Now file/folder creation works without console errors and supports i18n.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 04:44:29 +08:00
ZacharyZcRandClaude c6819d3a4b FIX: Resolve lazy encryption migration and data persistence critical issues
Fixed two critical database issues causing user creation errors and data loss:

## Issue 1: Lazy Encryption Migration Error
**Problem**: TypeError: Cannot read properties of undefined (reading 'db')
**Root Cause**: AuthManager called getSqlite() before database initialization
**Solution**: Added databaseReady promise await before accessing SQLite instance

Changes in auth-manager.ts:
- Import and await databaseReady promise before getSqlite() call
- Ensures database is fully initialized before migration attempts
- Prevents "SQLite not initialized" errors during user login

## Issue 2: Data Loss After Backend Restart
**Problem**: All user data wiped after backend restart
**Root Cause**: Database saves were skipped when file encryption disabled
**Solution**: Added fallback to unencrypted SQLite file persistence

Changes in database/db/index.ts:
- Modified saveMemoryDatabaseToFile() to handle encryption disabled scenario
- Added unencrypted SQLite file fallback to prevent data loss
- Added data directory creation to ensure save path exists
- Enhanced logging to track save operations and warnings

## Technical Details:
- saveMemoryDatabaseToFile() now saves data regardless of encryption setting
- Encrypted: saves to .encrypted file (existing behavior)
- Unencrypted: saves to .sqlite file (new fallback)
- Ensures data persistence in all configurations
- Maintains 15-second auto-save and real-time trigger functionality

These fixes ensure:
✅ User creation works without backend errors
✅ Data persists across backend restarts
✅ Lazy encryption migration completes successfully
✅ Graceful handling of encryption disabled scenarios

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 04:40:39 +08:00
ZacharyZcRandClaude a96659f4d2 FIX: Resolve symlink double-click behavior in file manager
Root cause: Duplicate handleFileOpen function definitions caused symlinks
to be treated as regular files instead of navigating to their targets.

Problem:
- Line 575: Correct implementation with symlink handling
- Line 1401: Incorrect duplicate that overrode the correct function
- Double-clicking symlinks opened them as files instead of following links

Solution:
- Removed duplicate handleFileOpen function (lines 1401-1436)
- Preserved correct implementation with symlink navigation logic
- Added recordRecentFile call for consistency

Now symlinks properly:
- Navigate to target directories when they point to folders
- Open target files when they point to files
- Use identifySSHSymlink backend API for resolution

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 04:37:13 +08:00
ZacharyZcRandClaude 3c1aeef47e FIX: Resolve SSH terminal connection port mismatch
Fixed WebSocket connection issue where SSH terminals couldn't connect
despite correct credentials. Root cause was port mismatch - terminals
were trying to connect to port 8081 while SSH service runs on 8082.

Changes:
- Desktop Terminal: Updated WebSocket URL to use port 8082
- Mobile Terminal: Updated WebSocket URL to use port 8082
- File Manager continues using port 8081 for HTTP API (unchanged)

This ensures all SSH terminal connections route to the correct service port.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 04:30:21 +08:00
ZacharyZcRandClaude 46f842afce feat: Implement dual-stage database migration with lazy field encryption
Phase 1: Database file migration (startup)
- Add DatabaseMigration class for safe unencrypted → encrypted DB migration
- Disable foreign key constraints during migration to prevent constraint failures
- Create timestamped backups and verification checks
- Rename original files instead of deletion for safety

Phase 2: Lazy field encryption (user login)
- Add LazyFieldEncryption utility for plaintext field detection
- Implement gradual migration of sensitive fields using user KEK
- Update DataCrypto to handle mixed plaintext/encrypted data
- Integrate lazy encryption into AuthManager login flow

Key improvements:
- Non-destructive migration with comprehensive backup strategy
- Automatic detection and handling of plaintext vs encrypted fields
- User-transparent migration during normal login process
- Complete migration logging and admin API endpoints
- Foreign key constraint handling during database structure migration

Resolves data decryption errors during Docker updates by providing
seamless transition from plaintext to encrypted storage.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 03:50:38 +08:00
ZacharyZcRandClaude 35a8b2fe4d fix: Complete i18n translation keys for export/import functionality
Add missing Chinese translations for new SQLite export/import features:
- passwordRequired: Password requirement validation
- confirmExport: Export confirmation dialog
- exportDescription: SQLite export functionality description
- importDescription: Incremental import process description

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 03:27:17 +08:00
ZacharyZcRandClaude 0506536893 FEAT: Implement SQLite-based data export/import with incremental merge
Replace JSON-based backup system with SQLite export/import functionality:

**Export Features:**
- Generate SQLite database files with complete user data
- Export all tables: SSH hosts, credentials, file manager data, settings, alerts
- Include OIDC configuration and system settings (admin only)
- Password authentication required for data decryption
- Direct browser download instead of file path display

**Import Features:**
- Incremental import with duplicate detection and skipping
- Smart conflict resolution by key combinations:
  - SSH hosts: ip + port + username
  - Credentials: name + username
  - File manager: path + name
- Re-encrypt imported data to current user's keys
- Admin-only settings import (including OIDC config)
- Detailed import statistics with category breakdown

**Removed:**
- Database backup functionality (redundant with export)
- JSON export format
- File path-based workflows

**Security:**
- Password verification for all operations
- SQLite file format validation
- Proper error handling and logging
- Admin permission checks for settings

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 03:09:53 +08:00
ZacharyZcRandClaude b655b2fe0c OPTIMIZE: Simplify Docker multi-stage build to reduce space usage
- Merge production-deps and native-builder stages to eliminate duplication
- Remove redundant intermediate layers that were consuming Docker space
- Add aggressive cleanup (rm -rf ~/.npm /tmp/* /var/cache/apk/*)
- Reduce overall image size and build-time space requirements

Fixes:
- ENOSPC errors during COPY operations from multiple build stages
- Excessive Docker layer accumulation from duplicate dependency installs
- Reduced disk space usage during multi-stage builds

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 02:28:00 +08:00
ZacharyZcRandClaude 017b56af27 FIX: Correct chmod syntax in Dockerfile
- Fix chmod command syntax to properly set permissions for multiple directories
- Use && to chain chmod commands instead of space-separated arguments
- Ensure /app/config, /app/ssl, and /app/data have correct 755 permissions

Fixes syntax error that would cause Docker build failures.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 02:26:48 +08:00
ZacharyZcRandClaude 4df88da680 ADD: .dockerignore to fix Docker build space issues
- Add comprehensive .dockerignore to exclude unnecessary files from Docker context
- Exclude .git directory to prevent large Git objects from being copied
- Exclude node_modules, logs, temp files, and other build artifacts
- Reduce Docker image size and build time significantly

Fixes:
- ENOSPC error during Docker build due to large .git directory
- Excessive Docker image size from unnecessary files
- Build context transfer time and resource usage

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 02:25:42 +08:00
ZacharyZcRandClaude 187658d9c9 FIX: Docker startup ENOSPC error - add missing SSL directory
- Pre-create /app/ssl directory in Dockerfile to prevent runtime creation failures
- Set proper permissions for /app/ssl, /app/config, and /app/data directories
- Ensure all required directories exist before application startup

Fixes:
- ENOSPC error when creating SSL directory at runtime
- Permission issues with auto-generated .env file writing
- Container restart loops due to initialization failures

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 02:22:59 +08:00
ZacharyZcRandClaude 27da06d72f DOCKER: Add INTERNAL_AUTH_TOKEN support and improve auto-generation
- Add INTERNAL_AUTH_TOKEN to docker-compose.yml environment variables
- Create comprehensive .env.example with deployment guidance
- Document zero-config deployment for single instances
- Clarify multi-instance deployment requirements
- Ensure auto-generated keys persist in Docker volumes (/app/config)

Security improvements:
- Complete Docker support for new internal auth token mechanism
- Maintains automatic key generation while ensuring persistence
- No manual configuration required for standard deployments

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-24 02:17:08 +08:00
ZacharyZcR fdc8198c3d Merge branch 'main' of github.com:ZacharyZcR/Termix 2025-09-24 00:13:08 +08:00
ZacharyZcRandClaude 084bfd9500 fix: 修复数据库解密Silent Failure导致数据丢失
- 移除静默忽略解密错误的逻辑,始终快速失败
- 添加详细的SystemCrypto初始化和解密过程日志
- 修复CommonJS require语法错误
- 确保数据库解密失败时不会创建空数据库

问题根源:异步初始化竞争条件 + Silent Failure掩盖真实错误
修复后:解密失败会明确报错,防止数据丢失

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-23 23:37:57 +08:00
ZacharyZcRandClaude 009f258996 FIX: Resolve Docker build and deployment critical issues
- Upgrade Node.js to 24 for dependency compatibility (better-sqlite3, vite)
- Add openssl to Alpine image for SSL certificate generation
- Fix Docker file permissions for /app/config directory (node user access)
- Update npm syntax: --only=production → --omit=dev (modern npm)
- Implement persistent configuration storage via Docker volumes
- Modify security checks to warn instead of exit for auto-generated keys
- Remove incorrect root Dockerfile/docker-compose.yml files
- Enable proper SSL/TLS certificate auto-generation in containers

All Docker deployment issues resolved. Application now starts successfully
with persistent configuration and auto-generated security keys.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-23 06:52:08 +08:00