Admin Service
The Admin Service is the platform control plane for Vecton — tenant lifecycle, billing, deployments, monitoring, backups, and configuration. It does not host tenant business data; the tenant services (CRM, webshop, warehouse, etc.) own that.
Architecture
┌────────────────────────────────────┐
│ Admin Frontend (Vue 3 + Vuetify) │
│ https://admin.vecton.hu │
└─────────────────┬───────────────────┘
│
▼
┌────────────────────────────────────┐
│ Admin Backend (Laravel 12) │
│ /api/admin/* │
└────┬───────┬──────┬──────┬──────┬───┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐
│ PG │ │Redis│ │RabM│ │ S3 │ │OTel│
└────┘ └────┘ └────┘ └────┘ └────┘
┌──────────────────┐
│ Identity API │ ← OAuth, users, abilities
└──────────────────┘
┌──────────────────┐
│ DevOps API │ ← K8s/ArgoCD/Harbor orchestration
└──────────────────┘
┌──────────────────┐
│ Tenant services │ ← CRM/webshop/warehouse proxy
└──────────────────┘
Tech Stack
| Component | Technology |
|---|---|
| Backend | Laravel 12 + PHP 8.4 |
| Server | FrankenPHP (Octane) |
| Frontend | Vue 3.5 + TypeScript + Vuetify 3 + Pinia |
| Database | PostgreSQL 16 |
| Cache/Sessions | Redis 7 |
| Queue | RabbitMQ via php-amqplib |
| Object Storage | Rook-Ceph S3 (private + public buckets) |
| OpenAPI | Dedoc Scramble auto-generation |
| Frontend SDK | @hey-api/openapi-ts generated typed client |
| Tracing | OpenTelemetry → OTLP → Tempo/Loki/Grafana |
| Auth | OAuth 2.0 token issued by Identity service |
Source layout
Backend (main/main-backend/)
| Path | Purpose |
|---|---|
app/Http/Controllers/Tenant/ | Tenant lifecycle, scaling, versions, storage, email, SMS, health (split from a former 1076-line monolith) |
app/Http/Controllers/Admin/ | LogViewer, Impersonation, SLA, UsageMetering, TenantActivity, TenantEmail (snapshot-based) |
app/Http/Controllers/ (root) | Auth, Dashboard, Billing, Feature, Backup, Monitoring, Infrastructure, Registry, Deployment, ArgoCD, DatabaseAccess, Maintenance, Notification, Contact, Users, AuditLog, ServiceDefaultVersion, VersionTracking, PlatformService, Status |
app/Services/ | DevOpsApiService, IdentityService, IdentityAdminService, TenantSystemApiService, AuditService, FeatureService, ScalingConfigService, BackupConfigService, EventPublisher, RabbitMQService, AlertDispatchService, EndpointCheckerService, NotificationService, TenantProvisioningService, VersionTrackingService |
app/Providers/AppServiceProvider.php | Registers admin.tenants.email.* Gate abilities |
app/Http/Middleware/VerifyOAuthToken.php | Validates Bearer tokens against Identity (60s cache) |
routes/api.php | All admin routes; mounted under /api/admin/ |
config/scramble.php | OpenAPI scope configuration (api_path = api/admin) |
Frontend (main/main-frontend/)
| Path | Purpose |
|---|---|
src/lib/axios.ts | Single shared axios instance with auth/refresh interceptor |
src/api/index.ts | Bridges the generated SDK to the shared axios |
src/api/generated/ | Auto-generated typed SDK from swagger/admin.json |
src/types/api.ts | Hand-curated domain types (Tenant, EmailAccount, ScalingConfig…) |
src/stores/ | Pinia stores (one per domain area) |
src/pages/ | File-based routes (unplugin-vue-router) |
src/views/tenants/ | Tenant detail per-tab components |
src/navigation/vertical/index.ts | Sidebar grouping: Platform / Configuration / Administration |
Authentication & Authorization
- OAuth flow: the frontend redirects to Identity (
/oauth/authorize), gets a code, the backend exchanges it for a Bearer token (AuthController::callback). - Per-request validation: every
/api/admin/*request hitsVerifyOAuthToken, which callsIdentity::getUserFromTokenand caches the user 60 s by token hash. - Authorization layers:
is_admin: truefrom Identity → access granted to every admin endpoint.- Granular abilities defined in
AppServiceProvider::ADMIN_ABILITIESand checked viacan:admin.tenants.email.viewroute middleware. - User-permission entries support glob wildcards:
admin.tenants.email.*matchesadmin.tenants.email.view.
- Audit log: mutating endpoints call
AuditService::log()after success; entries persist inaudit_logs(indexed ontarget,user_email,action,created_at).
Design notes:
- The OAuth
?token=query-string fallback was removed (audit #03). - Production exception responses no longer leak
getMessage()details unlessAPP_DEBUG=trueor the throwable is an HTTP exception (audit #06). IpWhitelist,Subscriptionmutations, andBackup::restoreToNeware now wrapped in transactions (audit #11, #15).
Functional areas
| Area | Routes | Notes |
|---|---|---|
| Tenants | /api/admin/tenants/* | CRUD, suspend/activate, K8s status, services list |
| Provisioning | /api/admin/tenants/{id}/{provisioning-status,retry-provisioning,sync-config} | Async job + log stream |
| Versions | /api/admin/tenants/{id}/versions | Image tag updates per service |
| Scaling | /api/admin/tenants/{id}/scaling-config | Per-service scale-to-zero, replicas, CPU/mem |
| Storage | /api/admin/tenants/{id}/storage-usage | Bucket quotas, usage |
| Health | /api/admin/tenants/{id}/service-health | Per-tenant service health probes |
| Email (CRM proxy) | /api/admin/tenants/{id}/email/* | Accounts, assignments, usage, bonus, provision |
| Email (snapshot admin) | /api/admin/tenants/{id}/email/{overview,messages,quarantine,…} | Platform-wide diagnostics, gated by admin.tenants.email.* abilities |
| SMS | /api/admin/tenants/{id}/sms/* | Twilio/SeeMe accounts |
| Billing | /api/admin/{plans,subscriptions,payments} | Plan CRUD, subscription state, payment recording |
| Features | /api/admin/{feature-groups,feature-definitions}/* | Feature flag/limit catalog and per-tenant overrides |
| Monitoring | /api/admin/monitoring/{dashboard,endpoints,alert-channels,metrics} | Endpoint health + Prometheus metrics |
| Backups | /api/admin/backups/* | List, trigger, restore, 3-tier config (defaults / plan / tenant) |
| Database access | /api/admin/database-access/{ip-whitelist,platform/users,tenants/{id}/users} | Postgres user lifecycle + IP whitelist |
| Maintenance | /api/admin/maintenance-windows | Scheduled platform downtime announcements |
| Audit logs | /api/admin/audit-logs | Searchable mutation history |
| Service versions (defaults) | /api/admin/service-default-versions | Default image tags for new tenants |
| Version tracking | /api/admin/versions/* | Per-tenant version drift dashboard |
| Infrastructure | /api/admin/infrastructure/{overview,nodes,postgresql,rabbitmq,ceph} | Cluster overview |
| Registry | /api/admin/registry/* | Harbor projects/repos/tags |
| ArgoCD | /api/admin/argocd/* | Read-only application list |
| Notifications | /api/admin/notifications/* | Admin-targeted notification feed (IDOR-scoped) |
| Impersonation | /api/admin/{users/{id}/impersonate,impersonation/{stop,history}} | Impersonate a tenant user |
| Platform services | /api/admin/platform-services | Image / PDF / SMTP / Scan health (parallel Http::pool) |
| Tenant logs | /api/admin/tenants/{id}/logs | Loki query proxy with service whitelist |
OpenAPI workflow
The backend generates an OpenAPI 3.0 spec from PHPDoc + route inspection via Scramble:
# main-backend
php artisan scramble:export # writes docs/api.json
cp docs/api.json ../main-frontend/swagger/admin.json
The frontend regenerates a typed Axios SDK:
# main-frontend
npm run api:export # curls the running backend's /docs/api.json
npm run api:generate # runs @hey-api/openapi-ts → src/api/generated/
Use the SDK in stores via:
import { sdk } from '@/api'
const { data } = await sdk.tenantsIndex({ query: { perPage: 10 } })
The generated client shares src/lib/axios.ts, so authentication, refresh, and 422 toast handling apply uniformly.
To enrich the response types, annotate Laravel controllers with PHPDoc @response blocks; Scramble picks them up on the next export.
Configuration
Key environment variables (see main-backend/.env.example for the full list):
| Variable | Purpose | Example |
|---|---|---|
APP_URL | Backend URL | https://admin.vecton.hu |
APP_DEBUG | Detailed exception messages — never true in production | false |
IDENTITY_URL | Identity service base URL | http://localhost:8000 |
OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET | OAuth client credentials issued by Identity | — |
DEVOPS_API_URL / DEVOPS_API_TOKEN | DevOps orchestration API | — |
TENANT_SYSTEM_API_TOKEN | Token for proxying calls to tenant services | — |
TENANT_{WEBSHOP,WAREHOUSE,CRM,ANALYTICS,WEBHOOK}_URL | Local-dev fallback URLs | http://localhost:801{0..5} |
RABBITMQ_{HOST,PORT,USER,PASSWORD,VHOST} | RabbitMQ connection | — |
LOKI_URL | Loki endpoint for tenant log viewer | http://grafana-loki.monitoring.svc.cluster.local:3100 |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP collector | http://otel-collector:4318 |
Development
# From vecton/main/main-backend
composer install
php artisan migrate --seed
php artisan serve --port=8002 # or `composer dev` for full stack
# From vecton/main/main-frontend
npm install
npm run dev # starts Vite on :5174
Useful npm scripts:
npm run typecheck # vue-tsc --noEmit
npm run lint # eslint --fix
npm run api:export # refresh swagger/admin.json from running backend
npm run api:generate # regenerate typed SDK
Useful artisan commands:
php artisan scramble:export # regenerate OpenAPI spec
php artisan migrate --seed
php artisan test # PHPUnit feature + unit tests
Testing
The backend has 350+ tests covering controllers, services, and middleware. Notable:
tests/Feature/Middleware/VerifyOAuthTokenTest.php— OAuth header parsing, identity rejection, query-token-rejection.tests/Feature/Authorization/AdminGatesTest.php—admin.tenants.email.*ability resolution including wildcards.tests/Unit/Models/TenantNamespaceTest.php— Tenantname → namespacemutator.tests/Feature/NotificationControllerTest.php— IDOR scope.tests/Feature/Tenant/ScalingConfigTest.php— Scaling-config response shape.
Run the suite:
php artisan test
The base Tests\TestCase bypasses VerifyOAuthToken for most tests; the middleware itself is exercised by VerifyOAuthTokenTest which re-enables it explicitly.
Production checklist
Before deploying a new release:
-
APP_DEBUG=falsein the production env - All five
TENANT_*_URLenvs andDEVOPS_API_URLresolve from inside the cluster - Migrations applied (
php artisan migrate --force) - OpenAPI spec regenerated and frontend rebuilt
-
.env.productioncontains the rightVITE_OAUTH_CLIENT_IDandVITE_IDENTITY_URL - OTel collector is reachable; otherwise set
OTEL_PHP_AUTOLOAD_ENABLED=false - Audit logs index migration applied
- Feature definitions seeded for the new release's
infra.*slugs