Skip to content

Technical — Raga License Env

A Laravel + Filament admin dashboard for managing clients and licenses for the RAGA platform. Besides the admin panel, this service exposes a single API endpoint (Basic Auth) used by other services for license validation.

Repository

KeyValue
Git Remotehttps://git.tlab.co.id/tarantula/tarantula-v2/dashboard/raga-license-env.git
Active Branchmain
bash
git clone https://git.tlab.co.id/tarantula/tarantula-v2/dashboard/raga-license-env.git
cd raga-license-env

Tech Stack

LayerTechnology
FrameworkLaravel 12 (PHP 8.2)
Admin PanelFilament 3.3
RBAC Panelbezhansalleh/filament-shield
Profile & 2FAjeffgreco13/filament-breezy
Activity Logrmsramos/activitylog + saade/filament-laravel-log (viewer)
Data Exportpxlrbt/filament-excel
Map Pickerdotswan/filament-map-picker
Auto-Generated REST APItomatophp/filament-api (generic API from Filament resources)
DatabasePostgreSQL
Secret ManagementInfisical CLI (infisical run --watch)

Folder Structure

raga-license-env/
├── app/
│   ├── Models/
│   │   ├── Client.php              # Client/company data
│   │   ├── License.php              # Licenses owned by a client
│   │   ├── InfisicalClient.php      # Per-client Infisical credentials
│   │   └── User.php                 # Admin panel login account (Filament)
│   ├── Filament/
│   │   ├── Resources/
│   │   │   ├── ClientResource/
│   │   │   ├── LicenseResource/
│   │   │   ├── InfisicalClientResource/
│   │   │   └── UserResource/
│   │   ├── Pages/
│   │   └── Widgets/
│   ├── Services/
│   │   └── CustomFilamentAPIServices.php  # filament-api customization
│   ├── Policies/                    # Filament Shield authorization policies
│   ├── Http/Middleware/
│   │   └── BlockFileUpload.php      # Blocks Livewire upload/preview-file endpoints (global middleware)
│   ├── Listeners/
│   │   └── SendTelegramLoginNotification.php  # Sends a Telegram notification on every successful panel login
│   ├── Console/Commands/
│   │   └── SyncEnvToInfisical.php   # `sync:infisical` command — pushes local `.env` contents to Infisical
│   └── Helpers/
│       └── helper.php               # login_infisical(), update_secret_value(), load_fitur(), trigger_pipeline()
├── routes/
│   ├── api.php                      # GET /api/licenses/{id_client}, GET /api/health
│   └── web.php                      # POST /save-infisical (form action for the Infisical Environment page)
├── database/migrations/
├── docker/
│   ├── Dockerfile
│   ├── nginx/
│   └── php.ini
├── docker-compose.dev.yml
├── docker-compose.dev.yml
├── script.sh                        # Used by docker-compose.dev.yml: Infisical login -> `php artisan serve`
└── infisical.sh                     # Similar generic wrapper, not used by docker-compose.dev.yml (see Docker section)

Environment Variables

Application & Database

Standard Laravel base config (APP_KEY, APP_URL, etc.) plus two database connections in config/database.php: pgsql (active, DB_HOST/DB_PORT/DB_DATABASE/DB_USERNAME/DB_PASSWORD) and mariadb (DB_HOST_2/DB_PORT_2/DB_DATABASE_2/DB_USERNAME_2/DB_PASSWORD_2) — this second connection appears to be leftover Laravel boilerplate; it isn't referenced anywhere in the application code.

Infisical (Bootstrap)

VariableDescription
INFISICAL_ENVTarget environment (dev/staging/prod)
INFISICAL_PATHSecrets path in Infisical
INFISICAL_API_URLInfisical server URL
INFISICAL_CLIENT_IDUniversal Auth Client ID
INFISICAL_CLIENT_SECRETUniversal Auth Client Secret
INFISICAL_PROJECT_IDInfisical project ID

Important note: this repo's .env.example contains APP_KEY and Infisical credential values (INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_PROJECT_ID) formatted like real, working values rather than empty placeholders as seen in other services' .env.example files. Since Infisical is the primary secret store for the entire platform, these credentials — if genuine — should be rotated promptly and the example file replaced with plain placeholders.

Additional Integrations (not in .env.example)

The following variables are read via env()/config('services.telegram.*') in the code but are not listed in .env.example:

VariableDescription
TELEGRAM_TOKENTelegram bot token for login notifications (config/services.php). If empty, the login notification is silently skipped
TELEGRAM_CHAT_IDTarget chat/group ID for the login notification
TELEGRAM_TOPIC_IDTelegram topic/thread ID (optional, only used if the group uses topics)
GIT_TOKENGitLab personal/project access token — used by create_trigger() to create a pipeline trigger token
GIT_PROJECT_IDGitLab project ID (git.tlab.co.id) whose pipeline is triggered from the Infisical Environment page for clients of type frontend

Domain Model

ModelRelationDescription
ClienthasMany License, hasMany InfisicalClientCompany/customer data (name, company_name, email, phone, address, is_active, max_user)
LicensebelongsTo ClientLicense (license_name, serial_number, qty, installed_in, binded_in, status, activated_date, expired_date, module)
InfisicalClientbelongsTo ClientPer-client Infisical credential mapping (type, infisical_project_id, infisical_client_id, infisical_client_secret, infisical_client_env, path, branch_source)
UserFilament admin panel login account (also used for Basic Auth on the license API endpoint)

All three main models (Client, License, InfisicalClient) use Spatie\Activitylog\Traits\LogsActivity — every field listed in getActivitylogOptions() is recorded to the activity log table on change.

Security note: InfisicalClient::getActivitylogOptions() includes the infisical_client_secret field in logOnly(), and that field's Filament form input (InfisicalClientResource) is a plain TextInput without ->password() (not masked). As a result, each client's Infisical secret is recorded in plaintext to the activity log table every time the record is created/updated, in addition to being stored in plaintext in its own column. This field should be excluded from logOnly() and the form input given ->password() or hidden after saving. A similar (though lower-impact, since it's already hashed) issue exists on User::getActivitylogOptions(), which includes the password column.

Middleware & Event Listener

ComponentDescription
BlockFileUpload (middleware, registered globally in bootstrap/app.php)Blocks requests to livewire/upload-file and livewire/preview-file/* with an HTTP 403 — effectively disabling every Filament file-upload component (FileUpload) across the panel, regardless of resource
SendTelegramLoginNotification (listener, wired up in AppServiceProvider::boot() for the Illuminate\Auth\Events\Login event)On every successful admin panel login, sends a Telegram message (TELEGRAM_TOKEN/TELEGRAM_CHAT_ID) with the user's name, email, IP, time (WIB), and user agent. A failed send (e.g. Telegram is down) is only logged and does not interrupt the login flow

Admin Panel (Filament Resources)

ResourceFunction
ClientResourceCRUD for client/company data
LicenseResourceCRUD for licenses per client
InfisicalClientResourceCRUD for per-client Infisical credential mappings
UserResourceCRUD for admin accounts (protected by Filament Shield RBAC)

Authorization across resources is handled via bezhansalleh/filament-shield (Spatie Permission-based roles & permissions), separate from the User Service RBAC system used across RAGA in general — this dashboard has its own access system for TLab's internal team.

Dashboard Widgets

The panel's home page (/) shows 4 stat/analytics widgets:

WidgetContent
DashboardOverviewStat cards for active Client, active License, and active Infisical Client counts
UserOverviewStat card for the total number of admin panel users
RecentLicensesWidgetTable of the 5 most recently activated_date licenses, with client name and expiry date
TopClientsWithMostLicensesChartChart of the top 5 clients by license count

Infisical Environment Page

The "Environment" table action on InfisicalClientResource opens /infisical-env?id={id} (new tab) — a custom page (App\Filament\Pages\InfisicalEnv, outside the standard CRUD resources) that:

  1. Fetches all of that client's Infisical secrets directly from the Infisical API (load_fitur()GET {INFISICAL_API_URL}/v3/secrets/raw, authenticated with that InfisicalClient record's credentials) and displays them in a form.
  2. On form submit (POST /save-infisical, defined in routes/web.php), each key-value pair is updated via update_secret_value() (PATCH {INFISICAL_API_URL}/v3/secrets/raw/{key}).
  3. If InfisicalClient.type === 'frontend', after the secrets are saved it automatically calls trigger_pipeline() — creating a pipeline trigger token and triggering a GitLab pipeline (git.tlab.co.id) on that client's branch_source, using GIT_TOKEN/GIT_PROJECT_ID.

This page does not appear in the navigation menu ($shouldRegisterNavigation = false) — it's only reachable via the table action on InfisicalClientResource.

Endpoints

MethodPathAuthDescription
GET/api/licenses/{id_client}HTTP Basic Auth (auth.basic)Returns a client's data along with all of its licenses
GET/api/healthSimple health check

GET /api/licenses/{id_client}

Authenticated via Laravel's built-in auth.basic middleware, which checks credentials against the same users table used to log into the admin panel — so the LICENSE_API_EMAIL/LICENSE_API_PASSWORD configured in User Service and API Tarantula are actually a Filament admin account's credentials, not a separate service account.

200 Response:

json
{
  "success": true,
  "data": {
    "id": "client-uuid",
    "name": "...",
    "company_name": "...",
    "licenses": [
      { "id": "license-uuid", "license_name": "...", "status": true, "expired_date": "..." }
    ]
  }
}

404 Response:

json
{ "success": false, "message": "License not found" }

Docker

docker-compose.dev.yml

ContainerImagePortDescription
raga-licensedocker/Dockerfile9000Laravel app (PHP-FPM), runs composer install then script.sh on start
raga-postgrespostgres:16.1-alpine5001→5432Main database (filament)

Startup (script.sh)

The app container's command in docker-compose.dev.yml runs composer install && script.sh. Same pattern as opa-config and other Node/Python services: script.sh logs in to Infisical via Universal Auth, then runs php artisan serve --host=0.0.0.0 --port=9000 through infisical run --watch so secrets are injected as environment variables.

The repo also has an infisical.sh at its root — the same Infisical login pattern, but generic (runs "$@", whatever command is passed) instead of hardcoding php artisan serve. This script is not called by docker-compose.dev.yml; it's likely used as a separate entrypoint for production deployment (the docker/Dockerfile image already installs supervisor and there's an nginx-fpm config at docker/nginx/conf/default.conf, suggesting a different setup for production than for dev).

Build & Run

bash
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate

# Development
php artisan serve

# Docker Compose (dev)
docker compose -f docker-compose.dev.yml up --build

# Sync local .env to Infisical (dev/staging/prod at once)
php artisan sync:infisical