From d94c4bed536ee511193de5d24ef941ccdcbc96f4 Mon Sep 17 00:00:00 2001 From: Divarion-D Date: Tue, 30 Jun 2026 21:23:02 +0300 Subject: [PATCH] docs: add CLAUDE.md project guide --- CLAUDE.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..bae62f55 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,71 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +XC_VM is an open-source, Xtream-Codes-style IPTV management panel (PHP 8.1+, AGPL-3.0). It is a modular monolith that has been migrated to Composer PSR-4. The application lives under `src/` and is deployed verbatim to `/home/xc_vm/` (so `src/` is the deploy root and `MAIN_HOME` maps to it). + +## Layout essentials + +- **`src/` is the application root.** `composer.json`, `vendor/`, `bootstrap.php`, `console.php` all live in `src/`, not the repo root. PSR-4: `XcVm\` → `src/` (e.g. `XcVm\Core\Database\DatabaseHandler` = `src/Core/Database/DatabaseHandler.php`). +- The repo root holds build/release tooling: `Makefile`, `tools/`, `tests/`, `install/`, `lb_configs/`, `.php-cs-fixer.dist.php`. + +## Commands + +Everything is driven from the **repo root via the `Makefile`**. Static-analysis/style tools are `require-dev` packages and are NOT in the committed `vendor/`; install them first. + +```bash +make dev-tools # composer install in src/ — adds PHPStan + PHP-CS-Fixer to src/vendor (do this first) +make phpstan # static analysis (phpstan.dist.neon, --memory-limit=2G) +make cs # code-style check (dry-run, fails on diff) +make cs-fix # apply style fixes in place +make gates # fast PSR-4 regression gates (see below) +make dev-clean # prune src/vendor back to production-only (composer install --no-dev) + +# Tests — PHPUnit 10.5, config in tests/phpunit.xml.dist (suite "Unit", bootstrap tests/bootstrap.php) +php tools/.bin/phpunit.phar -c tests/phpunit.xml.dist +php tools/.bin/phpunit.phar -c tests/phpunit.xml.dist --filter SomeTestName # single test + +php -l path/to/File.php # quick syntax check (used constantly; no DB needed) +``` + +`make gates` runs three CI blockers: `check-procedural-use` (every procedural/view file must `use`-import the classes it references at the top of the file — PHP `use` is positional), `verify-lb-archive` (the load-balancer build must contain no privileged code), and `check-vendor-prod-only` (the committed `vendor/` must stay production-only). + +### Build / release (Makefile) + +- `make main` — build the full panel archive (`dist/xc_vm.tar.gz` + `XC_VM.zip` installer). +- `make lb` — build the **load-balancer** archive: the same tree with admin/reseller/player code, installers, and privileged commands stripped out (see `LB_DIRS_TO_REMOVE` / `LB_FILES_TO_REMOVE`). **Modules are MAIN-only** and excluded from LB. +- `make new` — wipe `dist/`. Builds copy only git-tracked files and run `verify_no_lfs_pointers`. + +## Critical constraints + +- **`vendor/` is committed and PRODUCTION-ONLY.** Never run `composer install` on a deploy path. To change autoload, run `composer dump-autoload` from `src/`. After changing deps, re-commit a `composer install --no-dev` vendor (and `composer.lock`). +- **Git LFS:** `src/bin/install/database.sql`, the bundled `ffmpeg`/`ffprobe`, `redis-server`, `yt-dlp`, fonts/videos, etc. are LFS objects. Editing an LFS file is transparent (the clean filter re-stages it as an LFS object on `git add`; `git push` uploads it). A checkout without LFS materialised ships 130-byte pointer stubs — the build's `verify_no_lfs_pointers` guards against this. +- **Never `git push`.** Commit freely (Conventional Commits, English messages, grouped logically), but pushing to remote is the user's call — do not push unless explicitly told to. +- PHP runs with **`short_open_tag=1`** (view templates use `query(...)`. + +**Module system (`src/Modules//`, core in `src/Core/Module/`).** Each module ships a `module.json` manifest (name, version, `dependencies`, `optional_dependencies`, `requires_core`) and a `Module extends BaseModule`. `ModuleLoader` discovers modules, topologically sorts by dependency (with cycle detection), and boots them; `ModuleManager` handles install/update/uninstall and writes state to `config/modules.php`. +- **Modules own their DB schema** via file migrations: `Modules//migrations/.up.sql` / `.down.sql`, executed by `ModuleMigrator` (install → `up(null→version)`, update → `up(from→to)`, uninstall → `down`). The recorded `installed_version` is the watermark; there is no per-module tracking table. Do NOT add module tables to `src/bin/install/database.sql`. `ModuleManager::syncBundledModules()` (called from `StatusCommand`/`console.php status`) auto-installs not-yet-installed bundled modules in dependency order. +- **Core must not touch module-owned tables directly** (they may be dropped on uninstall). Instead, core dispatches an event (e.g. `StreamsDeletedEvent`, `BouquetDeletedEvent`) and the owning module subscribes via `#[ListensTo]` to clean up its own data. + +**Main vs Load Balancer.** The MAIN server runs the panel + MySQL/MariaDB. LB nodes only restream; they get a stripped codebase (the `make lb` archive) and are provisioned over SSH by `Cli/Commands/ServerInstallCommand` + `LbInstallFlow`, which also pull distribution binaries (PHP/nginx/ffmpeg) from a separate GitHub *binaries* release repo. Streaming code runs in both; admin/reseller/player and privileged CLI commands are MAIN-only. + +**`xcvm_core` C extension.** A bundled Zend extension (loaded via `bin/php/lib/php.ini`, alongside ioncube/opcache/maxminddb) provides marketplace module installation, module decryption, and `install_id`/environment fingerprinting. PHP code reaches it via the global `\XC_VM::*` API. + +## Conventions & gotchas + +- Outbound HTTPS from PHP-FPM must use cURL — `file_get_contents()` over https does not work in this environment. +- The TMDb client is the legacy global `\TMDB` class (`Modules/tmdb/lib/TmdbClient.php`, not PSR-4). Build it through `XcVm\Module\Tmdb\TmdbApiService::createClient($apiKey, $language)`, which loads the library itself — do not `require_once` the lib path manually. +- Config: `config.ini` (DB creds, `server_id`), the `settings` DB table via `SettingsManager`, and `config/modules.php` for module enable/disable/version state.