track-any-device/core
最新稳定版本:v0.7.0
Composer 安装命令:
composer require track-any-device/core
包简介
Core package for the Track Any Device platform.
README 文档
README
Core package for the Track Any Device (TAD) platform. Provides models, migrations, seeders, middleware, jobs, services, and workflow infrastructure shared across all TAD applications.
Requirements
| Dependency | Version |
|---|---|
| PHP | ^8.3 |
| Laravel | ^13.7 |
| laravel/fortify | ^1.0 |
| laravel/sanctum | ^4.0 |
| stancl/tenancy | ^3.10 |
| track-any-device/sms-gateway | *@dev |
Installation
Add the package to your host application's composer.json:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/track-any-device/package-core"
}
],
"require": {
"track-any-device/core": "dev-main"
}
}
Then install:
composer install
CoreServiceProvider is auto-discovered via the extra.laravel.providers entry in composer.json. The remaining service providers must be registered manually in bootstrap/providers.php (see Service Providers).
Configuration
Environment variables
| Key | Default | Description |
|---|---|---|
APP_SURFACE |
core |
Controls which surfaces load migrations. Set to core (or leave unset) to load all package migrations. Set to any other value to suppress them (e.g. for worker-only containers). |
APP_LOGIN_DOMAIN |
— | Hostname of the dedicated identity server (e.g. login.example.com). Used by AuthorizeTenantAccess to build the OAuth authorize URL. Falls back to APP_DOMAIN when unset. |
APP_DOMAIN |
localhost |
Central / root domain of the application. |
APP_INTERNAL_SECRET |
— | Shared secret for internal service-to-service calls. Validated by ValidateInternalSecret middleware via the X-Internal-Secret header. |
InfluxDB (telemetry)
Signals (device telemetry) are stored in InfluxDB when enabled. Add a config/influxdb.php file to your host application:
return [ 'enabled' => env('INFLUXDB_ENABLED', false), 'url' => env('INFLUXDB_URL', 'http://localhost:8086'), 'token' => env('INFLUXDB_TOKEN', ''), 'bucket' => env('INFLUXDB_BUCKET', 'signals'), 'org' => env('INFLUXDB_ORG', 'tad'), ];
| Key | Default | Description |
|---|---|---|
INFLUXDB_ENABLED |
false |
Set to true to enable time-series signal writes and queries. When false, SignalService no-ops silently. |
INFLUXDB_URL |
http://localhost:8086 |
InfluxDB instance URL |
INFLUXDB_TOKEN |
— | Authentication token |
INFLUXDB_BUCKET |
signals |
Bucket name for signal data |
INFLUXDB_ORG |
tad |
InfluxDB organisation |
Fleet thresholds
Add a config/fleet.php file to configure alarm thresholds:
return [ 'overspeed_threshold' => env('OVERSPEED_THRESHOLD_KMH', 80), 'low_battery_threshold' => env('LOW_BATTERY_THRESHOLD', 20), ];
Workflows
Add a config/workflows.php file to customise webhook retry backoff (useful in tests):
return [ // Seconds to sleep between webhook retry attempts [attempt-1, attempt-2, ...] 'webhook_backoff' => [2, 8], ];
Service Providers
CoreServiceProvider is auto-discovered. Register the rest manually in bootstrap/providers.php:
return [ // Auto-discovered — listed here for clarity TrackAnyDevice\Core\CoreServiceProvider::class, // Must be registered manually TrackAnyDevice\Core\Providers\TenancyServiceProvider::class, TrackAnyDevice\Core\Providers\DeviceServiceProvider::class, TrackAnyDevice\Core\Providers\FortifyServiceProvider::class, ];
CoreServiceProvider
- Loads package migrations when
config('app.surface')iscoreor unset. - Registers Artisan commands.
- Registers the package factory name resolver.
TenancyServiceProvider
Configures stancl/tenancy events, boots the tenancy middleware priority stack, and maps routes/tenant.php from the host application (if it exists) under the web middleware group.
DeviceServiceProvider
Registers device driver bindings keyed by DeviceType slug:
| Slug | Driver |
|---|---|
p901 |
P901Driver |
gf-07 |
GF07Driver |
jt808 |
Jt808Driver |
aot120 |
AOT120Driver |
tad101 |
Registered by track-any-device/tad101 package |
Resolve a driver in application code:
use TrackAnyDevice\Core\Providers\DeviceServiceProvider; $driver = DeviceServiceProvider::driverFor($device->deviceType->slug);
FortifyServiceProvider
Configures Fortify views (Inertia), actions, and rate limiting. Requires the host application to provide:
App\Actions\Fortify\CreateNewUserApp\Actions\Fortify\ResetUserPassword
Migrations & Seeders
Running migrations
php artisan migrate
Migrations are loaded automatically by CoreServiceProvider when APP_SURFACE is core (or unset).
Available seeders
Run the full seed sequence:
php artisan db:seed --class="TrackAnyDevice\Core\Database\Seeders\DatabaseSeeder"
Individual seeders:
| Seeder | Description |
|---|---|
AdminSeeder |
Creates the default admin user |
TenantSeeder |
Seeds example tenant(s) |
DeviceTypeSeeder |
Seeds supported device type records |
DriverSeeder |
Seeds driver records |
SensorSeeder |
Seeds sensor definitions |
GsmNetworkSeeder |
Seeds GSM network records |
CountrySeeder |
Seeds country / dialling-code data |
AlertRuleSeeder |
Seeds default global alert rules |
AssigneeTypeSeeder |
Seeds default assignee type options |
IncidentTaxonomySeeder |
Seeds incident priority and status options |
NavLinkSeeder |
Seeds navigation link defaults |
PolicyVersionSeeder |
Seeds the current policy version |
IndustrySeeder |
Seeds industry classification records |
HomePageSeeder |
Seeds default home page CMS content |
PublicPageSeeder |
Seeds public-facing CMS pages |
BlogSeeder |
Seeds sample blog content |
P901CatalogueSeeder |
Seeds P901 product catalogue data |
WorkflowSeeder |
Seeds example workflows |
SuthraPunjabTenantSeeder |
Seeds the Suthra Punjab demo tenant |
Architecture
Tenancy model
TAD uses stancl/tenancy in a central-database configuration — all data lives in one MySQL database. There are no per-tenant databases.
Tenant isolation is enforced at the query layer:
- Models that belong to one tenant (e.g.
Beat,Assignee,Incident) use theBelongsToTenanttrait, which attachesTenantScopeas a global scope. - Inside a tenant request (domain resolved by tenancy middleware),
TenantScopeautomatically addsWHERE tenant_id = ?to every query on those models. - Outside a tenant context (Filament admin, CLI), the scope is a no-op so admin queries see all rows.
use TrackAnyDevice\Core\Concerns\BelongsToTenant; use TrackAnyDevice\Core\Concerns\UsesCentralConnection; class MyModel extends Model { use BelongsToTenant, UsesCentralConnection; }
UsesCentralConnection pins the model to the connection defined in tenancy.database.central_connection (defaults to mysql). It exists to make a future transition to per-tenant databases non-breaking.
Roles
| Role | isCentralStaff() |
Description |
|---|---|---|
admin |
Yes | Full platform access |
supervisor |
Yes | Central staff with supervisory rights |
staff |
Yes | Central staff |
tenant_user |
No | Member of one or more tenants via tenant_users pivot |
user |
No | End-user / device owner |
Models
| Model | Connection | Notes |
|---|---|---|
User |
Central | Authenticatable; supports Fortify 2FA + Sanctum tokens |
Tenant |
Central | Extends stancl's BaseTenant; uses bigint PK |
Device |
Central | Soft-deletes; tracks status, onboarding, last signal |
DeviceType |
Central | Defines supported hardware variants |
Driver |
Central | Maps a device type to a driver class |
Sensor |
Central | Sensor definitions attached to device types or devices |
Beat |
Central | Geo-fence zone with optional parent hierarchy |
BeatAssignment |
Central | Links a device to a beat with time boundaries |
Assignee |
Central | Person or asset that can be assigned a device |
DeviceAssignment |
Central | Assignment record with condition tracking |
Incident |
Central | Alert / alarm event tied to a device and optional beat |
AlertRule |
Central | Global or tenant-level rules; tenant_id = null means global |
Workflow |
Central | Automation graph triggered by events or time |
WorkflowRun |
Central | Execution record for a single workflow run |
Signal |
InfluxDB | Time-series telemetry; not an Eloquent model |
Services
SignalService
Persists device telemetry to InfluxDB and updates the device's snapshot columns in MySQL.
use TrackAnyDevice\Core\Services\SignalService; $service = app(SignalService::class); // Record a signal $signal = $service->record($signalObject, $device); // Query history $signals = $service->queryHistory( deviceId: $device->id, from: now()->subHours(6), to: now(), limit: 500, eventType: 'location', // optional ); // Latest signals (last 30 days) $signals = $service->latestForDevice($device->id, limit: 100);
When INFLUXDB_ENABLED=false, all read methods return empty collections and write operations are silently skipped. SignalCreatedEvent is still dispatched so observers remain active.
AssignmentService
Manages the lifecycle of device-to-assignee assignments.
use TrackAnyDevice\Core\Services\AssignmentService; $service = app(AssignmentService::class); // Assign $assignment = $service->assign($device, $assignee, $assignedBy); // Transfer to another assignee $assignment = $service->transfer( device: $device, newAssignee: $newAssignee, transferredBy: $user, forceIfCriticalIncidents: false, // throws if unresolved critical incidents ); // Return $assignment = $service->returnDevice($assignment, $returnedBy, conditionIn: 'good');
BeatAssignmentService
Manages assignment of devices to beats (geo-fence zones).
GeoFence
Point-in-polygon and point-in-circle checks for beat violation detection.
use TrackAnyDevice\Core\Services\GeoFence; $geo = app(GeoFence::class); $inside = $geo->isInsideBeat($beat, $latitude, $longitude); // Convert a legacy circle beat to polygon vertices $polygon = $geo->circleToPolygon($lat, $lng, $radiusMetres, points: 64); // Validate a child beat fits within its parent $fits = $geo->childFitsWithinParent($parentBeat, $childCoordinates);
IncidentService
Creates and manages incident records.
OfflineDeviceRecoveryService
Detects devices that have gone silent and dispatches SMS recovery actions. Used by the devices:detect-offline command. Uses exponential backoff (5 → 10 → 20 → 40 → 80 → 160 → 320 → 360 min cap) with a hard limit of 8 attempts.
DeviceCommandService
Queues outbound commands to devices.
Middleware
Register these in your host application's bootstrap/app.php or Http/Kernel.php as needed.
| Middleware | Description |
|---|---|
AuthorizeTenantAccess |
Gate tenant-domain requests; bounces unauthenticated visitors through OAuth SSO. Exempt paths: /register, /sso/callback. |
CheckTenantApproved |
Rejects requests to tenants that are not in approved status. |
EnsureTenantDomain |
Ensures the request is coming from a tenant subdomain (not central). |
EnsureCentralDomain |
Ensures the request is coming from the central domain. |
EnsureLoginDomain |
Ensures the request is coming from the login/identity domain. |
EnsureMyDomain |
Ensures the request host matches APP_DOMAIN. |
EnsureTenantApiScope |
Validates that a Sanctum token has a required ability: ->middleware('tenant.scope:devices.read'). |
EnsurePhoneVerified |
Rejects access when the user's phone number is not verified. |
RequireSmsChallenge |
Requires a one-time SMS OTP challenge after login (session-based). |
InitializeTenancyForRequest |
Thin wrapper around stancl's tenancy initialiser. |
GateTenantRegistration |
Blocks tenant self-registration when registration_enabled = false. |
BeatScopedAccess |
Restricts resource access to the beats a user is assigned to. |
CaptureAuthLocation |
Records the user's browser geolocation on successful authentication. |
ValidateInternalSecret |
Validates the X-Internal-Secret header for service-to-service routes. |
HandleAppearance |
Applies the tenant's color scheme / theme to the response. |
HandleInertiaRequests |
Shares Inertia props (auth user, tenant, flash) on every request. |
ValidateInternalSecret usage
Protect internal-only routes:
// routes/api.php Route::middleware('App\Http\Middleware\ValidateInternalSecret') ->prefix('internal') ->group(function () { Route::post('/signal', SignalIngestController::class); });
The calling service must send:
X-Internal-Secret: <value of APP_INTERNAL_SECRET>
Artisan Commands
| Command | Schedule | Description |
|---|---|---|
devices:detect-offline |
Every 5 min | Detect silent in-service devices and dispatch SMS recovery |
workflows:run-scheduled |
Every minute | Dispatch time-triggered workflows that are due |
sms:poll-inbox |
Every minute | Poll the SMS gateway inbox and store unprocessed messages |
otp:prune |
Daily | Delete expired OTP codes from the database |
beats:normalize-to-polygon |
One-time migration | Convert legacy circle beats to polygon vertex arrays |
Recommended schedule
Add to routes/console.php in the host application:
use Illuminate\Support\Facades\Schedule; Schedule::command('devices:detect-offline')->everyFiveMinutes(); Schedule::command('workflows:run-scheduled')->everyMinute(); Schedule::command('sms:poll-inbox')->everyMinute(); Schedule::command('otp:prune')->daily();
One-time migration
After deployment, run the beat normalisation command to migrate any legacy circle-format beats to polygons:
# Preview (no writes) php artisan beats:normalize-to-polygon --dry-run # Apply php artisan beats:normalize-to-polygon
Workflows
Workflows are automation graphs stored as JSON in the workflows table. Each workflow has a trigger type, a graph of nodes and edges, and optional tenant scoping.
Trigger types
| Type | Description |
|---|---|
incident_opened |
Fires when a new incident is created |
incident_escalated |
Fires when an incident is escalated |
time |
Fires on a cron schedule (driven by workflows:run-scheduled) |
Action types
| Type | Handler | Description |
|---|---|---|
notify |
NotifyUsersAction |
Send notifications to users or assignees |
send_command |
SendDeviceCommandAction |
Queue a command to the incident's device |
escalate_incident |
EscalateIncidentAction |
Change the incident status to escalated |
webhook |
CallWebhookAction |
POST the run context to a tenant-supplied URL (3 retries, 10s timeout) |
wait |
WaitDelayAction |
Pause execution for a specified number of seconds (max 180s) |
Dispatching a workflow from code
use TrackAnyDevice\Core\Workflows\WorkflowDispatcher; use TrackAnyDevice\Core\Enums\WorkflowTriggerType; $dispatcher = app(WorkflowDispatcher::class); $count = $dispatcher->dispatchForIncident($incident, WorkflowTriggerType::IncidentOpened);
Time-triggered workflow config
Store in the trigger_config JSON column:
{
"cron": "*/15 * * * *",
"timezone": "Asia/Karachi"
}
timezone is optional and defaults to config('app.timezone').
Jobs
| Job | Queue | Description |
|---|---|---|
OnboardDeviceJob |
default | Runs the driver's onboarding action for a device (idempotent) |
ProcessAlarmEvents |
default | Creates/resolves incidents from active alarm flags |
CheckBeatViolation |
default | Level-aware beat geo-fence violation checker |
QueueDeviceCommand |
default | Sends a queued command through the device's driver |
RunWorkflowJob |
default | Executes a workflow graph for a given trigger context |
Events
| Event | Payload | Description |
|---|---|---|
SignalCreatedEvent |
$deviceId, SignalObject |
Fired after every signal is recorded |
DeviceOnboardedEvent |
Device |
Fired after OnboardDeviceJob completes onboarding |
DeviceUpdatedEvent |
Device |
Fired when device snapshot columns change |
DeviceLogEvent |
Device, log data |
Fired when a device log entry is created |
Enums
All database-persisted enums are backed PHP enums (string-backed). Key enums:
| Enum | Values |
|---|---|
Role |
admin, supervisor, staff, tenant_user, user |
DeviceStatus |
warehouse, inventory, available, assigned, in_service |
OnboardingStatus |
pending, sim_added, configured, verified |
IncidentStatus |
open, acknowledged, escalated, resolved, closed |
IncidentPriority |
low, medium, high, critical |
AlertRuleEventType |
sos, overspeed, low_battery, power_failure, vibration, beat_violation |
WorkflowTriggerType |
incident_opened, incident_escalated, time |
WorkflowRunStatus |
running, completed, failed |
Factories
Factories are included for testing. The package registers a factory name guesser in CoreServiceProvider so Model::factory() resolves to TrackAnyDevice\Core\Database\Factories\<Model>Factory.
Available factories: User, Tenant, Domain, Device, DeviceType, DeviceCommand, DeviceOrder, DeviceAssignment, Assignee, AssigneeType, Beat, BeatAssignment, BeatTemplate, AlertRule, Incident, SsoToken, Workflow.
License
MIT
统计信息
- 总下载量: 468
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 6
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-23