# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview FileRise is a self-hosted web file manager / WebDAV server built with PHP 8.3+. It provides drag-and-drop uploads, granular ACL-based permissions, ONLYOFFICE integration, WebDAV support, and OIDC authentication. No external database is required - all data is stored in JSON files. **Tech Stack:** - Backend: PHP 8.3+ (no framework) - Frontend: Vanilla JavaScript, Bootstrap 4.5.2 - WebDAV: sabre/dav - Dependencies: Composer (see composer.json) ## Development Setup ### Running Locally (Docker - Recommended) ```bash docker compose up -d ``` The docker-compose.yml file is configured for development. FileRise will be available at http://localhost:8080. ### Running with PHP Built-in Server 1. Install dependencies: ```bash composer install ``` 2. Create required directories: ```bash mkdir -p uploads users metadata chmod -R 775 uploads users metadata ``` 3. Set environment variables and start: ```bash export TIMEZONE="America/New_York" export TOTAL_UPLOAD_SIZE="10G" export SECURE="false" export PERSISTENT_TOKENS_KEY="dev_key_please_change" php -S localhost:8080 -t public/ ``` ## Architecture ### Directory Structure ``` FileRise/ ├── config/ │ └── config.php # Global configuration, session handling, encryption ├── src/ │ ├── controllers/ # Business logic for each feature area │ │ ├── FileController.php # File operations (download, preview, share) │ │ ├── FolderController.php # Folder operations (create, move, copy, delete) │ │ ├── UserController.php # User management │ │ ├── AuthController.php # Authentication (login, OIDC, TOTP) │ │ ├── AdminController.php # Admin panel operations │ │ ├── AclAdminController.php # ACL management │ │ ├── UploadController.php # File upload handling │ │ ├── MediaController.php # Media preview/streaming │ │ ├── OnlyOfficeController.php # ONLYOFFICE document editing │ │ └── PortalController.php # Client portal (Pro feature) │ ├── models/ # Data access layer │ │ ├── UserModel.php │ │ ├── FolderModel.php │ │ ├── FolderMeta.php │ │ ├── MediaModel.php │ │ └── AdminModel.php │ ├── lib/ # Core libraries │ │ ├── ACL.php # Central ACL enforcement (read, write, upload, share, etc.) │ │ └── FS.php # Filesystem utilities and safety checks │ ├── webdav/ # WebDAV implementation (using sabre/dav) │ │ ├── FileRiseFile.php │ │ ├── FileRiseDirectory.php │ │ └── CurrentUser.php │ ├── cli/ # CLI utilities │ └── openapi/ # OpenAPI spec generation ├── public/ # Web root (served by Apache/Nginx) │ ├── index.html # Main SPA entry point │ ├── api.php # API documentation viewer │ ├── webdav.php # WebDAV endpoint │ ├── api/ # API endpoints (called by frontend) │ │ ├── *.php # Individual API endpoints │ │ └── pro/ # Pro-only API endpoints │ ├── js/ # Frontend JavaScript │ ├── css/ # Stylesheets │ ├── vendor/ # Client-side libraries (Bootstrap, CodeMirror, etc.) │ └── .htaccess # Apache rewrite rules ├── scripts/ │ └── scan_uploads.php # CLI tool to rebuild metadata from filesystem ├── uploads/ # User file storage (created at runtime) ├── users/ # User data, permissions, tokens (created at runtime) └── metadata/ # File metadata, tags, shares, ACLs (created at runtime) ``` ### Key Architectural Patterns #### 1. ACL System (src/lib/ACL.php) The ACL class is the **single source of truth** for all permission checks. It manages folder-level permissions with inheritance: - **Buckets**: owners, read, write, share, read_own, create, upload, edit, rename, copy, move, delete, extract, share_file, share_folder - **Enforcement**: All controllers MUST call ACL methods (e.g., `ACL::canRead()`, `ACL::canWrite()`) before performing operations - **Storage**: Permissions stored in `metadata/folder_acl.json` - **Inheritance**: When a user is granted permissions on a folder, they typically have access to subfolders unless explicitly restricted #### 2. Metadata System FileRise stores metadata in JSON files rather than a database: - **Per-folder metadata**: `metadata/{folder_key}_metadata.json` - Root folder: `root_metadata.json` - Subfolder "invoices/2025": `invoices-2025_metadata.json` (slashes/spaces replaced with hyphens) - **Global metadata**: - `users/users.txt` - User credentials (bcrypt hashed) - `users/userPermissions.json` - Per-user settings (encrypted) - `users/persistent_tokens.json` - "Remember me" tokens (encrypted) - `users/adminConfig.json` - Admin settings (encrypted) - `metadata/folder_acl.json` - All ACL rules - `metadata/folder_owners.json` - Folder ownership tracking #### 3. Encryption Sensitive data is encrypted using AES-256-CBC with the `PERSISTENT_TOKENS_KEY` environment variable: - Functions: `encryptData()` and `decryptData()` in config/config.php - Encrypted files: userPermissions.json, persistent_tokens.json, adminConfig.json, proLicense.json #### 4. Session Management - PHP sessions with configurable lifetime (default: 2 hours) - "Remember me" tokens stored separately with 30-day expiry - Session regeneration on login to prevent fixation attacks - Proxy authentication bypass mode (AUTH_BYPASS) for SSO integration #### 5. WebDAV Integration The WebDAV endpoint (`public/webdav.php`) uses sabre/dav with custom node classes: - `FileRiseFile` and `FileRiseDirectory` in `src/webdav/` - **All WebDAV operations respect ACL rules** via the same ACL class - Authentication via HTTP Basic Auth or proxy headers #### 6. Pro Features FileRise has a Pro version with additional features loaded dynamically: - Pro bundle located in `users/pro/` (configurable via FR_PRO_BUNDLE_DIR) - Bootstrap file: `users/pro/bootstrap_pro.php` - License validation sets FR_PRO_ACTIVE constant - Pro endpoints in `public/api/pro/` ## Common Development Tasks ### Testing ACL Changes When modifying ACL logic: 1. Test with multiple user roles (admin, regular user, restricted user) 2. Verify both UI and WebDAV respect the same rules 3. Check inheritance behavior for nested folders 4. Test edge cases: root folder, trash folder, special characters in paths ### Adding New API Endpoints 1. Create endpoint file in `public/api/` (e.g., `public/api/myFeature.php`) 2. Include config: `require_once __DIR__ . '/../../config/config.php';` 3. Check authentication: `if (empty($_SESSION['authenticated'])) { /* return 401 */ }` 4. Perform ACL checks using `ACL::can*()` methods before operations 5. Return JSON: `header('Content-Type: application/json'); echo json_encode($response);` ### Working with Metadata Reading folder metadata: ```php require_once PROJECT_ROOT . '/src/models/FolderModel.php'; $meta = FolderModel::getFolderMeta($folderKey); // e.g., "root" or "invoices/2025" ``` Writing folder metadata: ```php FolderModel::saveFolderMeta($folderKey, $metaArray); ``` ### Rebuilding Metadata from Filesystem If files are added/removed outside FileRise: ```bash php scripts/scan_uploads.php ``` This rebuilds all `*_metadata.json` files by scanning the uploads directory. ### Running in Docker The Dockerfile and start.sh handle: - Setting PHP configuration (upload limits, timezone) - Running scan_uploads.php if SCAN_ON_START=true - Fixing permissions if CHOWN_ON_START=true - Starting Apache Environment variables are processed in config/config.php (falls back to constants if not set). ## Code Conventions ### File Organization - Controllers handle HTTP requests and orchestrate business logic - Models handle data persistence (JSON file I/O) - ACL class is the **only** place for permission logic - never duplicate ACL checks - FS class provides filesystem utilities and path safety checks ### Security Requirements - **Always validate user input** - use regex patterns from config.php (REGEX_FILE_NAME, REGEX_FOLDER_NAME) - **Always check ACLs** before file/folder operations - **Always use FS::safeReal()** to prevent path traversal via symlinks - **Never trust client-provided paths** - validate and sanitize all paths - **Use CSRF tokens** for state-changing operations (token in $_SESSION['csrf_token']) - **Sanitize output** when rendering user content (especially in previews) ### Error Handling - Return appropriate HTTP status codes (401 Unauthorized, 403 Forbidden, 404 Not Found, 500 Internal Server Error) - Log errors using `error_log()` for debugging - Return user-friendly JSON error messages ### Path Handling - Use DIRECTORY_SEPARATOR for cross-platform compatibility - Always normalize folder keys with `ACL::normalizeFolder()` - Convert between absolute paths and folder keys consistently: - Absolute: `/var/www/uploads/invoices/2025/` - Folder key: `invoices/2025` (relative to uploads, forward slashes) - Root folder key: `root` ## Testing FileRise does not currently have automated tests. When making changes: 1. Test manually in browser UI 2. Test WebDAV operations (if applicable) 3. Test with different user permission levels 4. Test ACL inheritance behavior 5. Check error cases (invalid input, insufficient permissions, missing files) ## CI/CD GitHub Actions workflows (in `.github/workflows/`): - `ci.yml` - Basic CI checks - `release-on-version.yml` - Automated releases when version changes - `sync-changelog.yml` - Changelog synchronization ## Important Notes - **No ORM/framework**: This is vanilla PHP - all database operations are manual JSON file I/O - **Session-based auth**: Not JWT - sessions stored server-side, persistent tokens for "remember me" - **Metadata consistency**: If you modify files directly, run scan_uploads.php to rebuild metadata - **ACL is central**: Never bypass ACL checks - all file operations must go through ACL validation - **Encryption key**: PERSISTENT_TOKENS_KEY must be set in production (default is insecure) - **Pro features**: Some functionality is dynamically loaded from the Pro bundle - check FR_PRO_ACTIVE before calling Pro code ## Performance Considerations - FileRise is designed to scale to **100k+ folders** in the sidebar tree - Metadata files are loaded on-demand (not all at once) - Large directory scans use scandir() with filtering - avoid recursive operations when possible - WebDAV PROPFIND operations should be optimized (limit depth) ## Debugging Enable PHP error reporting in development: ```php ini_set('display_errors', '1'); error_reporting(E_ALL); ``` Check logs: - Apache error log: `/var/log/apache2/error.log` (or similar) - PHP error_log() output: check Docker logs with `docker logs filerise` ## Documentation - Main docs: GitHub Wiki at https://github.com/error311/FileRise/wiki - API docs: Available at `/api.php` when logged in (Redoc interface) - OpenAPI spec: `openapi.json.dist`