Files
server-manager/AGENTS.md

68 lines
5.2 KiB
Markdown

# ServerManager — AGENTS.md
Repo-specific guidance for OpenCode sessions. Verified against code.
## Stack
- PHP 8.3+, custom MVC (not Laravel), MySQL/MariaDB, phpseclib 3.0, phpdotenv
- Autoload: PSR-4 `ServerManager\``src/`; `src/Helpers/functions.php` auto-loaded
- Android companion app at `android/` (Jetpack Compose, Material3, Retrofit, Kotlinx Serialization)
## Entry point
`public/index.php``App::boot()``App::run()``Router::dispatch()`
## Commands
- `composer install` — install deps (no dev deps in `composer.json`)
- `php bin/generate-key.php` — generate master key at `/etc/servermanager/master.key`
- `sudo bash install.sh` — full Ubuntu 22.04+/24.04+ install (needs sudo)
- `mysql servermanager < database/migrations/001_initial_schema.sql` — db setup
- `export ANDROID_HOME=/opt/android-sdk && cd android && ./gradlew assembleDebug` — build APK → `public/sysadmin.apk`
## Non-obvious conventions
- Route params use `:id` syntax (`/servers/:id`), not `{id}`. Patterns: `:id``(\d+)`, `:slug``([a-zA-Z0-9\-]+)`
- Router supports method spoofing via `_method` POST field
- Session name is `SM_SESSION`; sessions stored server-side in `sessions` table
- View engine: custom PHP templates (`extract()` + `include`), not Twig/Blade
- SSH credentials encrypted with AES-256-CBC; master key at `/etc/servermanager/master.key`
- `vendor/` is properly gitignored now (`/vendor/` in `.gitignore`, no leading space)
- Android build outputs ignored: `android/.gradle/`, `android/build/`, `android/app/build/`, `android/local.properties`
- Bump `versionCode` and `versionName` in `android/app/build.gradle.kts` on every code change to the Android app
## Backend gotchas
- `View::json()` has `: never` return type — calls `exit` immediately. Do not put code after it expecting to run.
- `AuditService::log(string $action, string $entityType, ?int $entityId, ?array $details = null, ?array $metadata = null)` — requires **3** positional args minimum. Calling with 2 args causes Error 500.
- `RoleMiddleware` defaults to `'admin'` required level. Hierarchy: `super_admin` (3) > `admin` (2) > `operator` (1).
- Role helper functions in views: `is_super_admin()`, `is_admin()`, `is_operator()`
- CSRF: middleware checks `$_POST['_csrf_token']` then `$_SERVER['HTTP_X_CSRF_TOKEN']`. Token expiry 7200s default.
- API routes have **no middleware** (auth is manual via `$this->authenticateRequest()` in controller). Web routes use `AuthMiddleware` / `RoleMiddleware` / `CSRFMiddleware`.
- Admin group routes (`/admin/*`) already include `AuthMiddleware` + `RoleMiddleware`; individual routes add `CSRFMiddleware`.
- Rate limiting on `POST /login` only (via `RateLimitMiddleware`)
- Every credential access must be logged at `warning` level via `AuditService`
- Notification read receipts tracked in `notification_reads` table: `(notification_id, user_id)` unique pair, with `read_at` timestamp
## Permission validation on server create/edit
- When creating or editing a server, required SSH permissions can be specified and are validated via SSH before saving.
- `PermissionValidator::validate(array $serverConfig, array $permissions)` connects to the remote server and runs check commands for each permission.
- Permission types: `sudo` (checks passwordless sudo), `command` (checks `command -v`), `file_read` (`test -r`), `file_write` (`test -w`), `custom` (arbitrary command, exit code 0 = pass).
- `ServerPermission` model handles CRUD for the `server_permissions` table.
- Both web (`ServerController::store/update`) and API (`ApiController::serverAdd/serverUpdate`) flows include permission validation.
- On validation failure, the web flow preserves form input via `$_SESSION['_form_input']` and redirects back with error details.
## Database
- Migrations run manually: `mysql servermanager < database/migrations/NNN_name.sql`
- 13 migrations: `001_initial_schema`, `002_server_access`, `003_teams`, `004_soft_deletes`, `005_app_config_and_notifications`, `006_agent_tokens`, `007_server_permission_checks`, `008_notification_reads`, `009_remove_notification_obsolete_columns`, `010_user_fcm_tokens`, `011_cleanup_unused_tables`, `012_server_thresholds`, `013_user_notification_prefs`
- Users table has `role` ENUM: `super_admin`, `admin`, `operator`
- `api_token` column on users for Bearer token auth
- `server_permissions` table stores required SSH permissions per server (FK to `servers.id` CASCADE)
## PR creation workflow
- If the current branch **exists on remote** (`git ls-remote --heads origin <branch>`), create a new PR from it via API: `POST /api/v1/repos/devlab/server-manager/pulls` with `head=<branch>`, `base=master`.
- If the current branch **does not exist on remote**, create a new one from master first: `git checkout -b <name> master`, push it, then create the PR.
- The API token for `git.devlab.lat` is embedded in the remote URL (`agent:<token>@git.devlab.lat`). Use `-u "agent:<token>"` for basic auth. The token is accessible via `git remote get-url origin`.
## Testing / CI
- **No tests, no CI, no linter, no formatter, no typechecker** exist in this repo
## Notable missing files
- `.env.example` does NOT exist (referenced in `composer.json` post-install-cmd but missing from repo)
- No Makefile, Dockerfile, or CI workflows