Commit Graph

3338 Commits

Author SHA1 Message Date
Nabeel Shahzad
c82f877ac8
refactor(addons): remove deprecated tests, update service bindings, and improve addon handling
- Removed outdated AddonsTableTest and related assertions.
- Unified service namespaces: migrated support classes to `Support` and service classes to `Services`.
- Added `name` column to the `addons` table and updated related models, migrations, and seeders.
- Replaced `AddonBootService` references with `AddonRuntimeService` throughout tests and codebase.
- Updated Rector configuration and Mago schema, improving import handling and schema validation.
- Simplified addon discovery logic, enhancing boot cache performance.
- Removed unused methods and adjusted runtime behavior for stricter validation and consistency.
2026-06-07 16:54:20 -05:00
Nabeel Shahzad
24b4a7570a
refactor(addons): update config paths and enhance ManifestParser schema handling
- Renamed `addons.base_path` to `addons.paths.base` in configuration files.
- Added `schema_version` handling to `ManifestParser` to improve compatibility.
- Updated documentation with detailed addon implementation and boot process.
- Removed deprecated and unused configuration entries from `composer.json`.

Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-06-07 14:59:37 -05:00
Arthur Pariente
f6104e1563
feat: add HasNanoIds trait for Nano ID primary keys
Replace the bespoke HashIdTrait with a Laravel-standard HasNanoIds trait
built on HasUniqueStringIds, mirroring HasUuids/HasUlids. Generation and
validation go through new Str::nanoid()/Str::isNanoid() macros (wrapping
hidehalo/nanoid), with the alphabet/length co-located on App\Contracts\Model.

- Apply HasNanoIds to Pirep, Flight, Acars, File; drop manual $keyType and
  the now-redundant #[WithoutIncrementing] attribute
- Deprecate Utils::generateNewId() as a shim over Str::nanoid() and migrate
  callers (FlightImporter, Maintenance, RouteForgeService, FileService,
  VMSAcars)
- Remove HashIdTrait
- Factories generate nanoid ids so test data matches production
- Fix FlightService::getAccessibleFlightIds() casting nanoid flight ids to
  int (0), which broke flight search under rank/typerating restrictions, and
  drop matching (int) casts from the affected test assertions
2026-06-07 21:51:24 +02:00
Nabeel Shahzad
9a9b90a271
refactor(addons): rename PrimeService to AddonRuntimeService and improve functionality
- Renamed `PrimeService` to `AddonRuntimeService` for clarity and alignment with functionality.
- Introduced a new `discoverNewAddons()` method for identifying and handling unregistered addons.
- Updated test cases, services, and commands to use the new service class.
- Improved addon discovery logic and boot cache handling.
- Replaced `registry_id` uniqueness constraint with nullable definition to allow legacy addons.

Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-06-06 17:58:44 -05:00
Nabeel Shahzad
be0c966cf9
Remove the nwidart library 2026-06-05 19:13:51 -05:00
Nabeel Shahzad
5d0a108c4d
Addons self-boot proof of concept 2026-06-05 19:09:51 -05:00
Nabeel Shahzad
23742bb962
Was accidentally removed - addons use it (at least vmsacars does)
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-06-05 15:17:40 -05:00
Nabeel Shahzad
a6a240bdfa
fix(addons): keep ModuleService working post-nwidart
- Add getStudlyName() to ModuleShim (fixes BadMethodCallException at
  ModuleSetupFilament:106 where $module->getStudlyName() is called)
- Remove Artisan::call('module:migrate', ...) from updateModule()
  (fixes CommandNotFoundException on admin enable — nwidart command
  gone; enable/disable lifecycle owned by setActive() + PrimeService)
- Remove unused Artisan import from ModuleService
- Add getStudlyName() assertion to ModuleShimTest
- Add ModuleServiceShimTest proving update/delete flows don't crash

feat(sample): add Filament resource + prove discovery

feat(addons): retire nwidart loader; wire engine

feat(addons): add Module facade shim

feat(addons): add base addon service provider

feat(addons): add FilamentPanelExtender

feat(addons): add runtime AddonLoader

feat(addons): boot-cache schema v2 + filament probe

feat(01-05): add phpvms:addons-prime console command

- Attribute-based: #[AsCommand] + #[Signature] (no $signature property)
- handle(PrimeService $prime): int — DI via handle()
- --force: calls prime->run() (always rebuilds); default: calls primeIfNeeded()
- components->task() for visual feedback, components->info() when cache present
- Returns self::SUCCESS / self::FAILURE with components->error() on exception
- AddonBootTest: command writes cache, Addon count>0, --force is idempotent

feat(01-05): add AddonServiceProvider and register in boot order

- AddonServiceProvider extends Illuminate\Support\ServiceProvider
- register() binds BootCache, ManifestParser, AutoloadGuard, AddonRegistry, PrimeService as singletons
- boot() skips guard+prime in console context (D-17); calls assertRuntimeAutoloadSupported() then primeIfNeeded() at HTTP/Octane boot (D-16, D-09)
- bootstrap/providers.php: AddonServiceProvider slots after ModulesServiceProvider, before AdminPanelProvider
- AddonBootTest: registration-order, provider-loaded, and console-skip (D-17) assertions

feat(01-04): implement PrimeService scan/upsert/cache

- run() scans base_path(modules) and storage_path(app/addons)
- firstOrNew+save upsert preserves operator enabled=false (D-12)
- enabled-only rows written to boot cache after each run (D-13)
- malformed manifests: Log::warning + skip, prime continues (D-15)
- T-04-03 path-traversal guard via realpath containment check
- primeIfNeeded() gates on BootCache::exists() (D-09/D-10)
- storage/app/addons absence treated as zero installed addons

test(01-04): add failing tests for PrimeService

- run() registers bundled modules, is idempotent (D-14)
- preserves operator enabled=false across re-prime (D-12)
- excludes disabled addons from boot cache (D-13)
- skips malformed manifests with Log warning (D-15)
- primeIfNeeded() gates on cache absence (D-09/D-10)
- handles absent storage/app/addons without error

feat(01-04): implement AddonRegistry read API

- enabled(): array → BootCache-sourced hot path (D-10)
- all(): Collection → Addon::query()->get() (no DB::table)
- find(): resolves by registry_id or path, returns null when absent
- Stateless, Octane-safe — no accumulated instance state
- 6 unit tests covering all three behaviors

feat(01-03): implement AutoloadGuard and AutoloadModeException

- AutoloadModeException extends RuntimeException with actionable message
  (names classmap-authoritative mode + composer dump-autoload fix)
- AutoloadGuard.isClassMapAuthoritative(?ClassLoader) checks authoritative mode
- AutoloadGuard.assertRuntimeAutoloadSupported() throws on authoritative loader
- No-arg path resolves via ClassLoader::getRegisteredLoaders() (prefers vendor key)
- Uses Composer\Autoload\ClassLoader — not App\Support\ClassLoader (naming collision avoided)

test(01-03): add failing test for AutoloadGuard

- authoritative loader returns true, non-authoritative returns false
- assertRuntimeAutoloadSupported throws AutoloadModeException for authoritative
- no-throw for non-authoritative loader
- AutoloadModeException message mentions classmap-authoritative and dump-autoload
- no-arg isClassMapAuthoritative() returns bool without error

feat(01-03): implement BootCache atomic boot-manifest read/write

- path() returns base_path('bootstrap/cache/addons.php')
- exists() reflects file presence
- read() requires the PHP cache; returns [] when absent
- write() serializes via var_export (no string interpolation, T-03-01)
- write() uses per-process temp file + rename() for POSIX atomicity (D-14)
- delete() removes file when present

test(01-03): add failing test for BootCache

- exists() returns false/true before/after write
- write() then read() round-trips addon array
- written PHP file passes php -l and require returns array
- no leftover temp files after write
- delete() removes file
- hostile registry_id round-trips via var_export escaping

feat(01-02): implement ManifestParser (lenient module.json + composer.json reader)

- parse() returns null on missing/malformed module.json (D-15, T-02-01)
- All nwidart keys optional with sane defaults (D-01)
- type defaults to 'module' when absent (D-02)
- blank/whitespace registry_id normalised to null (D-03, T-02-04)
- compat stored verbatim, no validation (D-05)
- namespace from autoload.psr-4 first key in composer.json, fallback to Modules\{Name} (D-07)
- version: module.json first, composer.json fallback, null if absent (D-07)
- Stateless class, no mutable instance properties (Octane-safe)
- No filesystem ops on manifest-supplied path/namespace/registryId (T-02-03)

test(01-02): add failing tests for ManifestParser

- Awards: legacy nwidart module, no phpVMS keys, no composer.json
- Sample: composer.json psr-4 dot key, version null
- VMSAcars: psr-4 empty-string key, version from composer.json
- phpVMS keys: type, compat, registry_id, version parsed verbatim
- Malformed JSON returns null, not exception (D-15)
- Missing module.json returns null
- Blank registry_id normalised to null (D-03)

feat(01-02): define ManifestData value-object contract

- Final readonly class in App\Addons namespace
- Constructor-promoted readonly properties: name, alias, type, compat,
  registryId, version, namespace, providers, path, raw
- PHPDoc array shapes for providers (list<string>) and raw (decoded JSON)
- Pure data contract, no Eloquent/Service dependencies
- Identity rules documented per D-03/D-04/MAN-03

feat(01-01): create App\Models\Addon Eloquent model with factory

- extends App\Contracts\Model (integer auto-increment id)
- table addons, fillable for all non-pk columns
- casts(): enabled => boolean, installed_at => datetime
- full $rules for all columns per Invite.php pattern
- full @property PHPDoc for D-06 columns
- HasFactory trait + AddonFactory with sensible defaults

test(01-01): add failing tests for App\Models\Addon

- asserts table name is addons
- asserts enabled cast to bool
- asserts installed_at cast to Carbon
- asserts nullable columns (registry_id, version, installed_at)
- asserts fillable for all non-pk columns

feat(01-01): create addons table migration with DDL and bundled data seed

- addons table with all D-06 columns (registry_id nullable unique, type,
  version, namespace, path, enabled, installed_at, timestamps)
- seeds Awards/Sample/VMSAcars bundled modules with NULL registry_id (D-08)
- namespace derived from composer.json psr-4 first key, fallback nwidart convention (D-07)
- version from module.json then composer.json then null (D-07)
- path sourced only from File::directories() output, not manifest (T-01-03)
- invalid JSON manifests skipped to avoid blocking migrations (D-15/T-01-01)
- idempotency guard: seeds only when table is empty

test(01-01): add failing tests for addons table migration

- asserts addons table with all D-06 columns is created
- asserts 3 bundled modules seeded with NULL registry_id
- asserts Awards namespace and null version
- asserts VMSAcars version 1.1.0
- asserts all bundled rows have type module, enabled true, path in modules/
- asserts down() drops the table
2026-06-05 12:25:15 -05:00
Nabeel S.
0cb67f10a3
feat: enhance installer with auto-skip and auto-run features (#2227)
Closes #2221 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Automated database migrations and seeders during install and update
flows
* Real-time streaming of installation and update logs, plus automatic
redirect after update

* **Improvements**
  * Installer wizard auto-advances to the correct step on load
* Better state handling and cached requirement checks to reduce repeated
work
  * Safer, idempotent execution to avoid duplicate migration/update runs
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-03 15:03:42 -05:00
Nabeel S.
2365c7570c
Merge branch 'main' into streamline-installer 2026-06-03 14:18:05 -05:00
Nabeel S.
823db3c1e2
Add withoutOverlapping() to recurring scheduled cron tasks (#2228)
## Problem

Only the queue-worker schedule entry uses `withoutOverlapping()`. The
recurring cron tasks (`cron-five-minutes` … `cron-monthly`) do not.

If the scheduler is triggered through more than one path — which is
common on phpVMS (the built-in `/api/cron/{id}` web cron **and** a
system `schedule:run` cron), or a duplicate crontab entry — the same
task can run **concurrently**. It also stacks when a task runs longer
than its interval.

In practice this causes intermittent failures around the nightly run
(~01:00): two `CronNightly` handlers firing at once → race conditions,
double processing, occasional DB deadlocks.

## Fix

Add `->withoutOverlapping($ttl)` to the recurring scheduled tasks
(mirrors what the queue worker already does). A task that is already
running is skipped instead of started a second time. The TTL is a safety
release so a crashed task cannot hold the lock indefinitely.

- No change to **what** the tasks do or **when** they run — purely a
concurrency guard.
- Each `Schedule::call(...)` is already `->name(...)`d, so the mutex has
a stable key.
- TTLs are set comfortably above each task's expected runtime (5/15/30
min, hourly 55, nightly/weekly/monthly 120).

## Notes

- Requires a working cache lock store (already required for the existing
queue-worker entry).
- `update-last-run-time` is intentionally left untouched (it must run
every minute), and `queue-worker-cron` already has the guard.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Configured cron job schedules to prevent overlapping execution across
all scheduled intervals (5-minute, 15-minute, 30-minute, hourly,
nightly, weekly, and monthly).

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-03 12:03:52 -05:00
Nabeel S.
1492031f4f
Merge branch 'main' into fix/schedule-without-overlapping 2026-06-03 11:58:18 -05:00
Nabeel S.
c0b804e38d
Merge branch 'main' into streamline-installer 2026-06-03 11:56:52 -05:00
Nabeel Shahzad
92aae040e0
Fix incorrect Vite asset path in route forge view
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-06-03 11:56:23 -05:00
Arthur Pariente
45b9eb387b
refactor(updater): replace backend sleep with frontend redirect delay 2026-06-03 18:34:17 +02:00
Arthur Pariente
f8570e9bbe
refactor(updater): drop non-proc_open fallback branches 2026-06-03 18:34:17 +02:00
Nabeel Shahzad
4612f54551
Build assets path 2026-06-02 23:17:45 -05:00
Nabeel S.
f9e0972561
Fixes/postgres (#2229) 2026-06-02 17:14:51 -05:00
Nabeel S.
4dd5e517c9
Merge branch 'main' into fixes/postgres 2026-06-02 17:12:41 -05:00
Nabeel Shahzad
7d09cac306
Fix assets not building 2026-06-02 17:11:08 -05:00
Nabeel Shahzad
5efd304d36
Test/coordination fixes 2026-06-02 16:58:06 -05:00
Nabeel S.
5e636d8a5b
Merge branch 'main' into streamline-installer 2026-06-02 16:16:47 -05:00
Nabeel Shahzad
ef2aa9d5b1
Fix sample seeder 2026-06-02 16:15:59 -05:00
Nabeel S.
e6b052d109
Merge branch 'main' into streamline-installer 2026-06-02 15:57:11 -05:00
Nabeel S.
d8cff98c05
Postgres compatibility (#2223)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Test/dev environment switched to PostgreSQL with persistence,
init/health checks; added formatting/linting config and a new dev
tooling dependency; updated editor config; removed legacy base YAML
seeds.

* **New Features**
* Centralized settings seeder and idempotent base-data seeder; seeding
flow refactored and PostgreSQL sequence-reset added.

* **Bug Fixes**
* Migrations made database-driver aware and duplicate-flight detection
tightened.

* **Tests**
  * Test bootstrap now seeds settings via the new seeder.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-02 15:51:09 -05:00
Nabeel Shahzad
1ed8ba8a18
More test fixes 2026-06-02 15:40:01 -05:00
Thomas Kant
6b028ce902 Add withoutOverlapping() to recurring scheduled cron tasks
The recurring cron tasks (cron-five-minutes … cron-monthly) had no
overlap protection, unlike the queue worker. When the scheduler is
triggered via more than one path — e.g. the built-in /api/cron web
cron together with a system `schedule:run` cron, or a duplicate
crontab entry — the same task can run concurrently. It also stacks
when a task runs longer than its interval. In practice this causes
intermittent failures around the nightly run (~01:00): two CronNightly
handlers firing at once → race conditions, double processing,
occasional DB deadlocks.

Add `withoutOverlapping($ttl)` so a task already running is skipped
instead of started a second time (mirrors the existing queue-worker
entry). The TTL is a safety release so a crashed task cannot hold the
lock indefinitely. No change to what the tasks do or when they run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 19:40:41 +02:00
Arthur Pariente
47130e3c02
Auto-run the updater on page load instead of requiring the update button 2026-06-02 18:16:39 +02:00
Arthur Pariente
7ba2054498
Auto-skip and auto-run wizard steps in the installer based on system state 2026-06-02 18:15:42 +02:00
Nabeel Shahzad
de88746227
test fixes 2026-06-01 22:41:04 -05:00
Nabeel Shahzad
f5501291f1
postgres/mysql cleanup 2026-06-01 18:54:41 -05:00
Nabeel Shahzad
67c244240a
postgres fixes 2026-06-01 18:38:58 -05:00
Nabeel Shahzad
625082fdae
Build adjustments 2026-05-31 15:47:28 -05:00
Nabeel Shahzad
7eeecd5725
Fix factories and columns that aren't correct 2026-05-31 15:43:19 -05:00
Nabeel Shahzad
1b05dfe757
build fix 2026-05-31 15:02:48 -05:00
Nabeel Shahzad
7d0f97230b
Run migrations and tests against live dbs 2026-05-31 14:37:28 -05:00
Nabeel Shahzad
2c288fe126
make bad setting clearer 2026-05-31 14:24:41 -05:00
Nabeel Shahzad
66f58ea0c2
Don't run yaml seeder 2026-05-31 14:19:16 -05:00
Nabeel Shahzad
a6dbe44014
extra value in compose 2026-05-31 14:16:54 -05:00
Nabeel Shahzad
ee09d78beb
Make the base seeder a little more robust 2026-05-31 14:12:29 -05:00
Nabeel Shahzad
e8c7f7f455
Mago adjustment 2026-05-31 13:50:23 -05:00
Nabeel Shahzad
381bc58f6d
Move the seed yamls into a Seeder classes 2026-05-31 13:47:25 -05:00
Nabeel Shahzad
b4937a4975
Move settings out of yml into its own seeder 2026-05-31 13:37:33 -05:00
Nabeel Shahzad
99faba2626
Update Sail configuration to use PostgreSQL by default, including environment variables and connection settings.
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-05-30 22:45:00 -05:00
Nabeel Shahzad
9a2ed0974c
Migrate expenses table to JSON column for flight_type, update Sail to use PostgreSQL, and refine duplicate handling in flights migration.
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-05-30 22:41:09 -05:00
Nabeel Shahzad
20029cd30d
Eager load ref_model relationships in expense migration and add AUTORUN_ENABLED environment variable to deployment configuration.
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-05-30 17:44:15 -05:00
Nabeel Shahzad
866ecfc15c
Eager load fares relationships across multiple models to improve consistency and reduce redundant queries.
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-05-30 17:02:07 -05:00
Nabeel Shahzad
b56a77e542
Remove simbrief.yml from sample seeder directory in the database
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-05-29 22:47:49 -05:00
Nabeel Shahzad
c36f94ee77
Add post_date field to YamlSeeder attribute list
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-05-29 22:45:35 -05:00
Nabeel Shahzad
b527c3caaf
Add Mailgun configuration and improve datetime parsing in YamlSeeder
Signed-off-by: Nabeel Shahzad <99736+nabeelio@users.noreply.github.com>
2026-05-29 22:39:42 -05:00