unraid / unraid/api

[Feature Bounty] File Manager

Open
#1,599 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
113
Forks
22
Avg merge
10h 40m
Merged PRs (30d)
13

Description

Modern File Manager API Feature Request

Is your feature request related to a problem?

Users need a modern, performant file management API that can power web interfaces, mobile apps, and automation scripts. The current system requires direct SSH access or WebGUI interaction, making it impossible to build modern file management experiences.

Describe the solution you'd like

Integration of an existing, mature file management service that the Unraid API can host and authenticate, rather than building from scratch. The API should:

  1. Full file system operations via API:

    • Browse directories with pagination and sorting
    • Create, read, update, delete files and folders
    • Copy, move, rename with conflict resolution
    • Batch operations with progress tracking
    • Archive creation/extraction (zip, tar, etc.)
    • File permissions management
  2. Advanced file features:

    • Full-text search across file names and content
    • File metadata extraction (EXIF, ID3, etc.)
    • Thumbnail generation for images/videos
    • File preview for common formats
    • Real-time file system monitoring via WebSockets
    • File sharing with temporary links
  3. Modern UX capabilities:

    • Virtual file system for unified view of shares
    • Favorites and recent files
    • Trash/recycle bin functionality
    • File versioning and snapshots
    • Clipboard operations (cut/copy/paste)
  4. Integration features:

    • Direct integration with Docker container volumes
    • VM disk image management
    • Plugin for VS Code Server or similar IDEs
    • WebDAV server for remote access
    • S3-compatible API for backup tools

Describe alternatives you've considered

  1. Krusader Docker container - Requires VNC/noVNC, not API-based
  2. Midnight Commander - Terminal only, no API
  3. Direct SMB/NFS access - No unified API, platform-dependent
  4. FileBrowser - Separate service, not integrated with Unraid API
  5. Custom scripts - Fragmented, no standard interface

Additional context

Recommended File Management Services

Instead of building from scratch, integrate one of these proven file managers:

Option 1: FileBrowser (Recommended)
# FileBrowser - Modern web file manager with API
- URL: https://github.com/filebrowser/filebrowser
- Language: Go with REST API
- Features:
  - Complete file operations (CRUD, copy, move, archive)
  - Built-in authentication (can be disabled for SSO)
  - Real-time file updates via WebSocket
  - Search and filtering
  - File sharing with temporary links
  - Image thumbnails and previews
  - Multiple storage backends
  - Extensible via plugins
  - Mobile-friendly UI
  
# Integration approach:
- Run FileBrowser as a subprocess or sidecar
- Proxy API calls through NestJS
- Use shared cookie authentication
- Override auth with Unraid API tokens
Integration Architecture with NestJS

Host the file manager service as a sidecar with shared authentication:

// Integration structure
// Location: api/src/unraid-api/modules/filemanager/

filemanager/
├── filemanager.module.ts          # Integration module
├── filemanager.controller.ts      # HTTP proxy controller
├── filemanager.service.ts         # Service orchestration
├── filemanager.config.ts          # Configuration
├── auth/
│   ├── cookie-auth.guard.ts      # Shared cookie authentication
│   ├── token-bridge.service.ts   # Bridge Unraid tokens to file manager
│   └── sso.strategy.ts           # SSO strategy for file manager
├── proxy/
│   ├── api-proxy.middleware.ts   # Proxy API calls to file manager
│   ├── websocket-proxy.ts        # Proxy WebSocket connections
│   └── response-transformer.ts   # Transform responses if needed
└── __tests__/
    └── integration.spec.ts
Integration Service Example
// filemanager.service.ts - Orchestrate external file manager
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { spawn, ChildProcess } from 'child_process';
import { createProxyMiddleware } from 'http-proxy-middleware';
import * as cookieParser from 'cookie-parser';

@Injectable()
export class FileManagerService implements OnModuleInit, OnModuleDestroy {
  private fileManagerProcess: ChildProcess;
  private fileManagerPort: number = 8080;
  private fileManagerUrl: string;
  
  constructor(
    private readonly configService: ConfigService,
    private readonly authService: AuthService,
  ) {
    this.fileManagerUrl = `http://localhost:${this.fileManagerPort}`;
  }

  async onModuleInit() {
    // Start FileBrowser as subprocess
    await this.startFileBrowser();
    
    // Wait for service to be ready
    await this.waitForService();
    
    // Configure SSO/authentication
    await this.configureAuthentication();
  }

  async onModuleDestroy() {
    // Gracefully stop file manager
    if (this.fileManagerProcess) {
      this.fileManagerProcess.kill('SIGTERM');
    }
  }

  private async startFileBrowser() {
    const config = {
      port: this.fileManagerPort,
      root: '/mnt/user',
      database: '/boot/config/plugins/unraid-api/filebrowser.db',
      'auth.method': 'proxy', // Use proxy auth headers
      'auth.header': 'X-Unraid-User',
    };

    // Start FileBrowser with configuration
    this.fileManagerProcess = spawn('filebrowser', [
      '--config', JSON.stringify(config),
      '--noauth', // Disable built-in auth, use our proxy
    ]);

    this.fileManagerProcess.stdout.on('data', (data) => {
      this.logger.debug(`FileBrowser: ${data}`);
    });

    this.fileManagerProcess.stderr.on('data', (data) => {
      this.logger.error(`FileBrowser Error: ${data}`);
    });
  }

  // Create proxy middleware for API calls
  createApiProxy() {
    return createProxyMiddleware({
      target: this.fileManagerUrl,
      changeOrigin: true,
      ws: true, // Enable WebSocket proxy
      
      // Add authentication headers
      onProxyReq: (proxyReq, req, res) => {
        // Extract user from session/cookie
        const user = req.user || this.authService.getUserFromCookie(req.cookies);
        
        if (user) {
          proxyReq.setHeader('X-Unraid-User', user.username);
          proxyReq.setHeader('X-Unraid-Roles', user.roles.join(','));
        }
      },
      
      // Transform responses if needed
      onProxyRes: (proxyRes, req, res) => {
        // Add CORS headers if needed
        proxyRes.headers['Access-Control-Allow-Credentials'] = 'true';
      },
    });
  }
}
Controller for Proxy
// filemanager.controller.ts
import { Controller, All, Req, Res, UseGuards } from '@nestjs/common';
import { Request, Response } from 'express';
import { CookieAuthGuard } from './auth/cookie-auth.guard';

@Controller('filemanager')
@UseGuards(CookieAuthGuard) // Ensure user is authenticated
export class FileManagerController {
  constructor(private readonly fileManagerService: FileManagerService) {}

  @All('*')
  async proxyRequest(@Req() req: Request, @Res() res: Response) {
    // Proxy all requests to file manager service
    const proxy = this.fileManagerService.createApiProxy();
    proxy(req, res, (err) => {
      if (err) {
        res.status(500).json({ error: 'Proxy error', details: err.message });
      }
    });
  }
}
Configuration

Create a separate configuration file following the existing pattern:

// Location: api/dev/configs/filemanager.json
// This follows the same pattern as connect.json, cloud.json, etc.
{
  "service": "filebrowser",  // Which file manager to use
  "port": 8080,             // Port for the file manager service
  "binary": "/usr/local/emhttp/plugins/unraid-api/filemanager/filebrowser",
  "database": "/boot/config/plugins/unraid-api/filebrowser.db",
  "auth": {
    "method": "proxy",      // Use proxy authentication
    "header": "X-Unraid-User"
  },
  "roots": [
    {
      "name": "User Shares",
      "path": "/mnt/user",
      "writable": true
    },
    {
      "name": "Cache",
      "path": "/mnt/cache", 
      "writable": true
    },
    {
      "name": "Disks",
      "path": "/mnt/disk*",
      "writable": true
    },
    {
      "name": "Boot",
      "path": "/boot",
      "writable": false
    }
  ]
}

// Environment variable configuration
// Location: .env or environment
FILEMANAGER_ENABLED=true
FILEMANAGER_SERVICE=filebrowser
FILEMANAGER_PORT=8080

Environment (if relevant)

Unraid OS Version: 6.11+ (any version with API support)

Pre-submission Checklist

  • I have searched existing issues to ensure this feature hasn't already been requested
  • This is not an Unraid Connect related feature
  • I have provided clear examples and implementation details for the feature

Bounty Development Guidelines

For developers interested in implementing this feature:

  1. DO NOT build a file manager from scratch - Use FileBrowser, Filestash, or similar
  2. Run file manager as a sidecar service - Start/stop with NestJS lifecycle
  3. Implement authentication bridge - Share cookies between Unraid API and file manager
  4. Use HTTP proxy pattern - Proxy all requests through NestJS for auth/control
  5. Bundle the file manager binary - Download during build-txz.ts like other tools
  6. Create WebGUI page - Follow LogViewer.vue pattern for iframe integration
  7. Add to Tools menu - Include navigation entry with proper permissions
  8. Start with FileBrowser - Most mature option with best API
  9. Disable file manager's built-in auth - Use Unraid API authentication only
  10. Proxy WebSocket connections - Ensure real-time updates work
  11. Map Unraid paths correctly - /mnt/user, /mnt/disk*, /mnt/cache
  12. Test permission inheritance - Ensure Unraid permissions are respected
  13. Style iframe properly - Full height, no borders, responsive layout
  14. Document the integration - How to configure, extend, and troubleshoot
Integration Strategy

The implementation should:

For FileBrowser Integration:
  • Download FileBrowser binary during plugin build
  • Start as subprocess with --noauth flag
  • Use proxy auth headers (X-Unraid-User, X-Unraid-Roles)
  • Proxy all /filemanager/* routes through NestJS
  • Store database in /boot/config/plugins/unraid-api/
For Authentication:
  • Use existing Unraid API session cookies
  • Bridge authentication to file manager via headers
  • Implement cookie-auth.guard.ts for validation
  • Map Unraid roles to file manager permissions
Performance Considerations
  • Use streams for large file operations
  • Implement request debouncing for file system watchers
  • Cache directory listings with TTL
  • Use worker threads for CPU-intensive operations (thumbnails, indexing)
  • Implement progressive loading for large directories
  • Use database for search index if dealing with millions of files
Security Requirements
  • Path validation: Prevent directory traversal attacks
  • Permission checking: Respect Unraid user permissions
  • Rate limiting: Prevent DoS through excessive operations
  • File type validation: Prevent execution of malicious files
  • Sandboxing: Isolate file operations from system
  • Audit logging: Track all file operations
Testing Requirements
  • Unit tests for all services
  • Integration tests for GraphQL operations
  • Performance tests with large directories (10,000+ files)
  • Security tests for path traversal and permission bypass
  • Compatibility tests with different file systems (XFS, BTRFS, ZFS)
  • Stress tests for concurrent operations
WebGUI Integration

Create a WebGUI page for the file browser following the existing log viewer pattern:

// Location: web/pages/FileManager.vue
// Follow the pattern from web/pages/LogViewer.vue

<template>
  <PageLayout :title="t('file_manager.title')" contentClass="!p-0">
    <iframe
      ref="fileManagerFrame"
      :src="fileManagerUrl"
      class="w-full h-full border-0"
      @load="onFrameLoad"
    />
  </PageLayout>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import { useApiStore } from '@/stores/api';

const apiStore = useApiStore();

// Use the proxied file manager URL
const fileManagerUrl = computed(() => {
  const baseUrl = apiStore.baseUrl || '';
  return `${baseUrl}/filemanager`;
});

const fileManagerFrame = ref<HTMLIFrameElement>();

const onFrameLoad = () => {
  // Pass authentication token to iframe if needed
  if (fileManagerFrame.value?.contentWindow) {
    // The proxy should handle auth, but we can pass additional data if needed
    fileManagerFrame.value.contentWindow.postMessage({
      type: 'auth',
      token: apiStore.token,
    }, '*');
  }
};
</script>

Add navigation entry:

// Location: web/composables/use-navigation.ts
// Add to the Tools section

{
  label: 'File Manager',
  icon: 'folder-open',
  to: '/file-manager',
  permission: 'share.read',
}
Deliverables
  1. FileManager integration module for NestJS
  2. Authentication bridge implementation
  3. HTTP/WebSocket proxy configuration
  4. FileBrowser binary download in build-txz.ts
  5. Service lifecycle management (start/stop)
  6. Configuration for Unraid paths
  7. WebGUI page (FileManager.vue) following LogViewer pattern
  8. Navigation integration in Tools menu
  9. Documentation for integration and usage
  10. Tests for auth bridge and proxy
  11. Example nginx configuration for production

Why Not Build From Scratch?

Building a file manager is a massive undertaking that requires:

  • Handling edge cases for thousands of file system scenarios
  • Security hardening against path traversal, symlink attacks, etc.
  • Performance optimization for large directories
  • Cross-platform compatibility issues
  • Extensive testing across file systems

FileBrowser alone has:

  • 7+ years of development
  • 20,000+ GitHub stars
  • 100+ contributors
  • Battle-tested in production

By integrating an existing solution:

  • We get a mature, feature-complete file manager immediately
  • Security updates and bug fixes from the upstream project
  • Community plugins and extensions
  • Well-documented API
  • Active development and support

The integration approach gives us:

  • Full control over authentication and authorization
  • Ability to customize the UI through the file manager's theming
  • Easy upgrades by updating the binary
  • Fallback to direct file manager access if needed
  • Lower maintenance burden for the Unraid API team

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the existing LogViewer.vue pattern and the configuration conventions in api/dev/configs, then inspect the proposed api/src/unraid-api/modules/filemanager/ entry points. Scope the FileBrowser sidecar, authentication bridge, proxy routes, WebGUI page, and build packaging before implementing. Done requires documented integration, permission and path-safety coverage, and the listed unit and integration tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, typescript
Domain
backend-api-design, devops, security, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.