- server/api/schemas.py: New shared Pydantic models for all API routes
(ServerCreate/Update/Push/Check, SyncFiles/Commands/Config/Sftp/Browse,
ScriptCreate/Update/Execute, ScheduleCreate/Update, PlatformCreate/Update,
NodeCreate/Update, PaginatedResponse)
- servers.py: Replace raw dict with Pydantic models, add pagination
(page/per_page params, response now includes items/total/pages)
- sync_v2.py: Replace raw dict with SyncFiles/Commands/Config/Sftp/Browse
- scripts.py: Replace raw dict with ScriptCreate/Update/Execute
- assets.py: Replace raw dict with PlatformCreate/Update, NodeCreate/Update
- web/app/api.js: Shared JS auth module with JWT auto-refresh (2min
before expiry), 401 retry with refresh token, doLogout with
sendBeacon to backend, backward-compatible ah() alias
- web/app/servers.html: Use api.js, handle paginated response format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- login.php: Fix JWT response format (success/access_token vs status=ok),
store JWT tokens in session, support TOTP 202 flow
- api_client.php: Dual auth (JWT Bearer + X-API-Key fallback), add
auto-refresh on 401, fix API endpoints (checkServerHealth → array,
getServerLogs, getAuditLogs, getDashboardStats field mapping)
- api_proxy.php: Fix check_all to use /api/servers/check (not per-server),
fix get_logs to support global endpoint, restart_multisync → restart_nexus
- install.php: Add 4 missing tables (platforms, nodes, ssh_sessions,
command_logs), fix table creation order for FK dependencies, add JWT
columns to admins, add new indexes, add platform_id/node_id/protocols/
extra_attrs/connectivity to servers
- logout.php: Call /api/auth/logout to invalidate JWT refresh token
- index.php: Use JWT Bearer auth for API calls
- server_service.py: Implement real check_all_servers with concurrent
httpx Agent health checks (was stub)
- servers.py: Add GET /api/servers/logs global sync logs endpoint
- Branding: MultiSync → Nexus across all PHP files (sidebar, titles,
TOTP issuer, version strings)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. schedule_runner.py: cron-based schedule execution every 60s
- Parses 5-field cron expressions (*, */N, ranges, lists)
- Triggers SyncEngineV2.sync_files for due schedules
- Prevents re-trigger within same minute
2. retry_runner.py: automatic retry of failed pushes every 5min
- Exponential backoff (60s base, doubles per retry, max 64x)
- Respects max_retries from PushRetryJob model
3. GET /api/servers/stats: dashboard aggregation endpoint
- Returns total/online/offline counts + category breakdown
- Reads real-time status from Redis, falls back to MySQL
- Registered before /{id} to avoid path collision
4. main.py: register schedule_runner + retry_runner as background tasks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. infrastructure/__init__.py: remove nonexistent get_ssh_pool import,
use lazy __getattr__ to avoid triggering MySQL engine at import time
2. auth_service.py + auth_jwt.py: JWT sub field must be string
(PyJWT 2.12+ enforces RFC 7519), decode with int() conversion
3. session.py: lazy engine init — engine created on first access
instead of module import time, allows testing without MySQL
4. Tests: fix Column default assertions (SQLAlchemy defaults only
apply on DB INSERT), fix Request.url mock for Starlette
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All skill files group labels cleaned (no duplicates, no garbled text).
CMS developer renamed to frontend developer in AG group.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Temporary: includes real credentials for deployment testing.
Will be removed from .gitignore when switching to formal repo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SECRETS.md is a local-only file for storing database passwords,
API keys, and server credentials. It must never be committed to Git.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CI (every push):
- pytest with coverage (>=50% gate)
- ruff lint check
- mypy type check (non-blocking)
CD (main branch only, after CI passes):
- SSH deploy to production server
- git pull + pip install + supervisorctl restart
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>