@fleetbase/solid-engine 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/ACL_SOLUTION.md +72 -0
  2. package/CSS_SCOPE_ISSUE.md +140 -0
  3. package/HOTFIX_SYNTAX_ERROR.md +100 -0
  4. package/LICENSE.md +651 -21
  5. package/MANUAL_ACL_SETUP.md +135 -0
  6. package/README.md +74 -27
  7. package/REFACTORING_SUMMARY.md +330 -0
  8. package/VERIFICATION_CHECKLIST.md +82 -0
  9. package/addon/components/modals/create-solid-folder.hbs +29 -0
  10. package/addon/components/modals/import-solid-resources.hbs +85 -0
  11. package/addon/controllers/data/content.js +17 -0
  12. package/addon/controllers/data/index.js +219 -0
  13. package/addon/controllers/home.js +84 -0
  14. package/addon/engine.js +1 -24
  15. package/addon/extension.js +26 -0
  16. package/addon/routes/data/content.js +11 -0
  17. package/addon/routes/data/index.js +17 -0
  18. package/addon/routes.js +2 -7
  19. package/addon/styles/solid-engine.css +1 -2
  20. package/addon/templates/account.hbs +3 -3
  21. package/addon/templates/application.hbs +2 -12
  22. package/addon/templates/data/content.hbs +48 -0
  23. package/addon/templates/{pods/explorer.hbs → data/index.hbs} +6 -5
  24. package/addon/templates/home.hbs +168 -10
  25. package/app/components/modals/{backup-pod.js → create-solid-folder.js} +1 -1
  26. package/app/components/modals/{resync-pod.js → import-solid-resources.js} +1 -1
  27. package/app/components/modals/{create-pod.js → setup-css-credentials.js} +1 -1
  28. package/composer.json +5 -11
  29. package/extension.json +2 -2
  30. package/index.js +0 -11
  31. package/package.json +9 -9
  32. package/server/migrations/2024_12_21_add_css_credentials_to_solid_identities_table.php +32 -0
  33. package/server/src/Client/OpenIDConnectClient.php +686 -15
  34. package/server/src/Client/SolidClient.php +104 -8
  35. package/server/src/Http/Controllers/DataController.php +261 -0
  36. package/server/src/Http/Controllers/OIDCController.php +42 -8
  37. package/server/src/Http/Controllers/SolidController.php +179 -85
  38. package/server/src/Models/SolidIdentity.php +13 -3
  39. package/server/src/Services/AclService.php +146 -0
  40. package/server/src/Services/PodService.php +863 -0
  41. package/server/src/Services/ResourceSyncService.php +336 -0
  42. package/server/src/Services/VehicleSyncService.php +289 -0
  43. package/server/src/Support/Utils.php +10 -0
  44. package/server/src/routes.php +25 -1
  45. package/addon/components/modals/backup-pod.hbs +0 -3
  46. package/addon/components/modals/create-pod.hbs +0 -5
  47. package/addon/components/modals/resync-pod.hbs +0 -3
  48. package/addon/controllers/pods/explorer/content.js +0 -12
  49. package/addon/controllers/pods/explorer.js +0 -149
  50. package/addon/controllers/pods/index/pod.js +0 -12
  51. package/addon/controllers/pods/index.js +0 -137
  52. package/addon/routes/pods/explorer/content.js +0 -10
  53. package/addon/routes/pods/explorer.js +0 -44
  54. package/addon/routes/pods/index/pod.js +0 -3
  55. package/addon/routes/pods/index.js +0 -21
  56. package/addon/templates/pods/explorer/content.hbs +0 -19
  57. package/addon/templates/pods/index/pod.hbs +0 -11
  58. package/addon/templates/pods/index.hbs +0 -19
  59. package/server/src/LegacyClient/Identity/IdentityProvider.php +0 -174
  60. package/server/src/LegacyClient/Identity/Profile.php +0 -18
  61. package/server/src/LegacyClient/OIDCClient.php +0 -350
  62. package/server/src/LegacyClient/Profile/WebID.php +0 -26
  63. package/server/src/LegacyClient/SolidClient.php +0 -271
@@ -0,0 +1,336 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Solid\Services;
4
+
5
+ use Fleetbase\Solid\Models\SolidIdentity;
6
+ use Fleetbase\Solid\Client\SolidClient;
7
+ use Illuminate\Support\Facades\Log;
8
+
9
+ class ResourceSyncService
10
+ {
11
+ /**
12
+ * Import resources into a pod.
13
+ *
14
+ * @param SolidIdentity $identity
15
+ * @param string $podUrl
16
+ * @param array $resourceTypes
17
+ * @return array
18
+ */
19
+ public function importResources(SolidIdentity $identity, string $podUrl, array $resourceTypes): array
20
+ {
21
+ $imported = [];
22
+ $errors = [];
23
+ $totalCount = 0;
24
+
25
+ foreach ($resourceTypes as $resourceType) {
26
+ try {
27
+ Log::info('[IMPORTING RESOURCE TYPE]', ['type' => $resourceType, 'pod_url' => $podUrl]);
28
+
29
+ $result = $this->importResourceType($identity, $podUrl, $resourceType);
30
+ $imported[$resourceType] = $result['count'];
31
+ $totalCount += $result['count'];
32
+
33
+ Log::info('[RESOURCE TYPE IMPORTED]', [
34
+ 'type' => $resourceType,
35
+ 'count' => $result['count'],
36
+ ]);
37
+ } catch (\Throwable $e) {
38
+ Log::error('[RESOURCE IMPORT ERROR]', [
39
+ 'type' => $resourceType,
40
+ 'error' => $e->getMessage(),
41
+ ]);
42
+ $errors[$resourceType] = $e->getMessage();
43
+ }
44
+ }
45
+
46
+ return [
47
+ 'imported' => $imported,
48
+ 'errors' => $errors,
49
+ 'total_count' => $totalCount,
50
+ ];
51
+ }
52
+
53
+ /**
54
+ * Import a specific resource type.
55
+ *
56
+ * @param SolidIdentity $identity
57
+ * @param string $podUrl
58
+ * @param string $resourceType
59
+ * @return array
60
+ */
61
+ protected function importResourceType(SolidIdentity $identity, string $podUrl, string $resourceType): array
62
+ {
63
+ // Get resources from Fleetops
64
+ $resources = $this->getFleetopsResources($resourceType);
65
+
66
+ if (empty($resources)) {
67
+ return ['count' => 0];
68
+ }
69
+
70
+ // Create container for this resource type
71
+ $containerUrl = rtrim($podUrl, '/') . '/' . $resourceType . '/';
72
+ $this->createContainer($identity, $containerUrl);
73
+
74
+ // Import each resource
75
+ $count = 0;
76
+ foreach ($resources as $resource) {
77
+ try {
78
+ $turtle = $this->convertToRDF($resourceType, $resource);
79
+ $resourceUrl = $containerUrl . $resource->public_id . '.ttl';
80
+
81
+ $this->storeResource($identity, $resourceUrl, $turtle);
82
+ $count++;
83
+ } catch (\Throwable $e) {
84
+ Log::warning('[RESOURCE IMPORT FAILED]', [
85
+ 'type' => $resourceType,
86
+ 'id' => $resource->public_id ?? 'unknown',
87
+ 'error' => $e->getMessage(),
88
+ ]);
89
+ }
90
+ }
91
+
92
+ return ['count' => $count];
93
+ }
94
+
95
+ /**
96
+ * Get Fleetops resources by type.
97
+ *
98
+ * @param string $resourceType
99
+ * @return \Illuminate\Support\Collection
100
+ */
101
+ protected function getFleetopsResources(string $resourceType)
102
+ {
103
+ $modelMap = [
104
+ 'vehicles' => \Fleetbase\FleetOps\Models\Vehicle::class,
105
+ 'drivers' => \Fleetbase\FleetOps\Models\Driver::class,
106
+ 'contacts' => \Fleetbase\FleetOps\Models\Contact::class,
107
+ 'orders' => \Fleetbase\FleetOps\Models\Order::class,
108
+ ];
109
+
110
+ if (!isset($modelMap[$resourceType])) {
111
+ throw new \Exception("Unknown resource type: {$resourceType}");
112
+ }
113
+
114
+ $modelClass = $modelMap[$resourceType];
115
+
116
+ // Get current company's resources
117
+ $companyId = session('company');
118
+
119
+ Log::info('[FETCHING FLEETOPS RESOURCES]', [
120
+ 'resource_type' => $resourceType,
121
+ 'model_class' => $modelClass,
122
+ 'company_id' => $companyId,
123
+ ]);
124
+
125
+ if (!$companyId) {
126
+ Log::warning('[NO COMPANY ID]', ['session' => session()->all()]);
127
+ // Try to get from auth user
128
+ $user = auth()->user();
129
+ if ($user && isset($user->company_uuid)) {
130
+ $companyId = $user->company_uuid;
131
+ Log::info('[USING USER COMPANY]', ['company_id' => $companyId]);
132
+ }
133
+ }
134
+
135
+ if (!$companyId) {
136
+ Log::error('[CANNOT DETERMINE COMPANY]');
137
+ return collect([]);
138
+ }
139
+
140
+ $query = $modelClass::where('company_uuid', $companyId)
141
+ ->limit(100); // Limit for now to avoid overwhelming the pod
142
+
143
+ $count = $query->count();
144
+ Log::info('[RESOURCE QUERY]', [
145
+ 'resource_type' => $resourceType,
146
+ 'count' => $count,
147
+ 'sql' => $query->toSql(),
148
+ 'bindings' => $query->getBindings(),
149
+ ]);
150
+
151
+ return $query->get();
152
+ }
153
+
154
+ /**
155
+ * Convert a resource to RDF/Turtle format.
156
+ *
157
+ * @param string $resourceType
158
+ * @param mixed $resource
159
+ * @return string
160
+ */
161
+ protected function convertToRDF(string $resourceType, $resource): string
162
+ {
163
+ $baseUri = "http://fleetbase.io/ns/{$resourceType}/";
164
+ $resourceUri = $baseUri . $resource->public_id;
165
+
166
+ $turtle = "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
167
+ $turtle .= "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
168
+ $turtle .= "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n";
169
+ $turtle .= "@prefix fb: <http://fleetbase.io/ns/> .\n";
170
+ $turtle .= "@prefix fb{$resourceType}: <{$baseUri}> .\n\n";
171
+
172
+ $turtle .= "<{$resourceUri}>\n";
173
+ $turtle .= " a fb:{$this->getResourceClass($resourceType)} ;\n";
174
+ $turtle .= " fb:id \"{$resource->public_id}\" ;\n";
175
+
176
+ // Add resource-specific properties
177
+ $properties = $this->getResourceProperties($resourceType, $resource);
178
+ foreach ($properties as $property => $value) {
179
+ if ($value !== null && $value !== '') {
180
+ $turtle .= " fb:{$property} " . $this->formatRDFValue($value) . " ;\n";
181
+ }
182
+ }
183
+
184
+ // Remove trailing semicolon and add period
185
+ $turtle = rtrim($turtle, " ;\n") . " .\n";
186
+
187
+ return $turtle;
188
+ }
189
+
190
+ /**
191
+ * Get RDF class name for resource type.
192
+ *
193
+ * @param string $resourceType
194
+ * @return string
195
+ */
196
+ protected function getResourceClass(string $resourceType): string
197
+ {
198
+ return ucfirst(rtrim($resourceType, 's'));
199
+ }
200
+
201
+ /**
202
+ * Get properties for a resource type.
203
+ *
204
+ * @param string $resourceType
205
+ * @param mixed $resource
206
+ * @return array
207
+ */
208
+ protected function getResourceProperties(string $resourceType, $resource): array
209
+ {
210
+ switch ($resourceType) {
211
+ case 'vehicles':
212
+ return [
213
+ 'name' => $resource->name,
214
+ 'make' => $resource->make,
215
+ 'model' => $resource->model,
216
+ 'year' => $resource->year,
217
+ 'vin' => $resource->vin,
218
+ 'plate_number' => $resource->plate_number,
219
+ 'status' => $resource->status,
220
+ 'created_at' => $resource->created_at?->toIso8601String(),
221
+ 'updated_at' => $resource->updated_at?->toIso8601String(),
222
+ ];
223
+
224
+ case 'drivers':
225
+ return [
226
+ 'name' => $resource->name,
227
+ 'email' => $resource->email,
228
+ 'phone' => $resource->phone,
229
+ 'license_number' => $resource->drivers_license_number,
230
+ 'status' => $resource->status,
231
+ 'created_at' => $resource->created_at?->toIso8601String(),
232
+ 'updated_at' => $resource->updated_at?->toIso8601String(),
233
+ ];
234
+
235
+ case 'contacts':
236
+ return [
237
+ 'name' => $resource->name,
238
+ 'email' => $resource->email,
239
+ 'phone' => $resource->phone,
240
+ 'type' => $resource->type,
241
+ 'created_at' => $resource->created_at?->toIso8601String(),
242
+ 'updated_at' => $resource->updated_at?->toIso8601String(),
243
+ ];
244
+
245
+ case 'orders':
246
+ return [
247
+ 'tracking_number' => $resource->public_id,
248
+ 'status' => $resource->status,
249
+ 'type' => $resource->type,
250
+ 'scheduled_at' => $resource->scheduled_at?->toIso8601String(),
251
+ 'created_at' => $resource->created_at?->toIso8601String(),
252
+ 'updated_at' => $resource->updated_at?->toIso8601String(),
253
+ ];
254
+
255
+ default:
256
+ return [];
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Format a value for RDF.
262
+ *
263
+ * @param mixed $value
264
+ * @return string
265
+ */
266
+ protected function formatRDFValue($value): string
267
+ {
268
+ if (is_numeric($value)) {
269
+ return "\"{$value}\"^^xsd:integer";
270
+ }
271
+
272
+ if (is_bool($value)) {
273
+ return $value ? '"true"^^xsd:boolean' : '"false"^^xsd:boolean';
274
+ }
275
+
276
+ // Escape quotes and special characters
277
+ $escaped = str_replace(['"', '\\'], ['\\"', '\\\\'], (string)$value);
278
+ return "\"{$escaped}\"";
279
+ }
280
+
281
+ /**
282
+ * Create a container in the pod.
283
+ *
284
+ * @param SolidIdentity $identity
285
+ * @param string $containerUrl
286
+ * @return void
287
+ */
288
+ protected function createContainer(SolidIdentity $identity, string $containerUrl): void
289
+ {
290
+ try {
291
+ $response = $identity->request('put', $containerUrl, '', [
292
+ 'headers' => [
293
+ 'Content-Type' => 'text/turtle',
294
+ 'Link' => '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"',
295
+ ],
296
+ ]);
297
+
298
+ Log::info('[CONTAINER CREATED]', [
299
+ 'url' => $containerUrl,
300
+ 'status' => $response->status(),
301
+ ]);
302
+ } catch (\Throwable $e) {
303
+ // Container might already exist, that's okay
304
+ Log::debug('[CONTAINER CREATION SKIPPED]', [
305
+ 'url' => $containerUrl,
306
+ 'reason' => $e->getMessage(),
307
+ ]);
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Store a resource in the pod.
313
+ *
314
+ * @param SolidIdentity $identity
315
+ * @param string $resourceUrl
316
+ * @param string $turtle
317
+ * @return void
318
+ */
319
+ protected function storeResource(SolidIdentity $identity, string $resourceUrl, string $turtle): void
320
+ {
321
+ $response = $identity->request('put', $resourceUrl, $turtle, [
322
+ 'headers' => [
323
+ 'Content-Type' => 'text/turtle',
324
+ ],
325
+ ]);
326
+
327
+ if (!$response->successful()) {
328
+ throw new \Exception("Failed to store resource: {$response->body()}");
329
+ }
330
+
331
+ Log::debug('[RESOURCE STORED]', [
332
+ 'url' => $resourceUrl,
333
+ 'status' => $response->status(),
334
+ ]);
335
+ }
336
+ }
@@ -0,0 +1,289 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Solid\Services;
4
+
5
+ use Fleetbase\FleetOps\Models\Vehicle;
6
+ use Fleetbase\Solid\Models\SolidIdentity;
7
+ use Illuminate\Support\Facades\Log;
8
+ use Illuminate\Support\Str;
9
+
10
+ class VehicleSyncService
11
+ {
12
+ /**
13
+ * Get vehicles available for sync.
14
+ */
15
+ public function getAvailableVehicles(): array
16
+ {
17
+ try {
18
+ $vehicles = Vehicle::with(['driver', 'vendor', 'category'])
19
+ ->where('company_uuid', session('company'))
20
+ ->get();
21
+
22
+ return $vehicles->map(function ($vehicle) {
23
+ return [
24
+ 'id' => $vehicle->uuid,
25
+ 'display_name' => $this->getVehicleDisplayName($vehicle),
26
+ 'make' => $vehicle->make,
27
+ 'model' => $vehicle->model,
28
+ 'year' => $vehicle->year,
29
+ 'plate_number' => $vehicle->plate_number,
30
+ 'vin' => $vehicle->vin,
31
+ 'status' => $vehicle->status,
32
+ 'driver_name' => $vehicle->driver?->name,
33
+ 'vendor_name' => $vehicle->vendor?->name,
34
+ 'last_seen' => $vehicle->updated_at?->diffForHumans(),
35
+ 'sync_status' => 'not_synced', // TODO: Track actual sync status
36
+ ];
37
+ })->toArray();
38
+ } catch (\Throwable $e) {
39
+ Log::error('[GET AVAILABLE VEHICLES ERROR]', [
40
+ 'error' => $e->getMessage(),
41
+ ]);
42
+ throw $e;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Sync selected vehicles to a pod.
48
+ */
49
+ public function syncVehiclesToPod(SolidIdentity $identity, string $podUrl, array $vehicleIds): array
50
+ {
51
+ $syncedCount = 0;
52
+ $failedCount = 0;
53
+ $details = [];
54
+
55
+ try {
56
+ $vehicles = Vehicle::with(['driver', 'vendor', 'category'])
57
+ ->whereIn('uuid', $vehicleIds)
58
+ ->where('company_uuid', session('company'))
59
+ ->get();
60
+
61
+ foreach ($vehicles as $vehicle) {
62
+ try {
63
+ $this->syncSingleVehicle($identity, $podUrl, $vehicle);
64
+ $syncedCount++;
65
+ $details[] = [
66
+ 'vehicle_id' => $vehicle->uuid,
67
+ 'status' => 'success',
68
+ 'message' => 'Synced successfully',
69
+ ];
70
+ } catch (\Throwable $e) {
71
+ $failedCount++;
72
+ $details[] = [
73
+ 'vehicle_id' => $vehicle->uuid,
74
+ 'status' => 'failed',
75
+ 'message' => $e->getMessage(),
76
+ ];
77
+
78
+ Log::error('[VEHICLE SYNC FAILED]', [
79
+ 'vehicle_id' => $vehicle->uuid,
80
+ 'error' => $e->getMessage(),
81
+ ]);
82
+ }
83
+ }
84
+
85
+ Log::info('[VEHICLE SYNC COMPLETED]', [
86
+ 'pod_url' => $podUrl,
87
+ 'synced_count' => $syncedCount,
88
+ 'failed_count' => $failedCount,
89
+ ]);
90
+
91
+ return [
92
+ 'synced_count' => $syncedCount,
93
+ 'failed_count' => $failedCount,
94
+ 'details' => $details,
95
+ ];
96
+ } catch (\Throwable $e) {
97
+ Log::error('[SYNC VEHICLES TO POD ERROR]', [
98
+ 'pod_url' => $podUrl,
99
+ 'vehicle_ids' => $vehicleIds,
100
+ 'error' => $e->getMessage(),
101
+ ]);
102
+ throw $e;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Sync a single vehicle to the pod.
108
+ */
109
+ private function syncSingleVehicle(SolidIdentity $identity, string $podUrl, Vehicle $vehicle): void
110
+ {
111
+ // Generate RDF/Turtle content for the vehicle
112
+ $rdfContent = $this->generateVehicleRDF($vehicle);
113
+
114
+ // Create filename
115
+ $filename = $this->generateVehicleFilename($vehicle);
116
+ $resourceUrl = rtrim($podUrl, '/') . '/' . $filename;
117
+
118
+ // Store the vehicle data in the pod
119
+ $response = $identity->request('put', $resourceUrl, $rdfContent, [
120
+ 'headers' => [
121
+ 'Content-Type' => 'text/turtle',
122
+ ],
123
+ ]);
124
+
125
+ if (!$response->successful()) {
126
+ throw new \Exception('Failed to store vehicle data: ' . $response->body());
127
+ }
128
+
129
+ Log::info('[VEHICLE SYNCED]', [
130
+ 'vehicle_id' => $vehicle->uuid,
131
+ 'resource_url' => $resourceUrl,
132
+ 'status' => $response->status(),
133
+ ]);
134
+ }
135
+
136
+ /**
137
+ * Generate RDF/Turtle content for a vehicle.
138
+ */
139
+ private function generateVehicleRDF(Vehicle $vehicle): string
140
+ {
141
+ $turtle = "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
142
+ $turtle .= "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
143
+ $turtle .= "@prefix dc: <http://purl.org/dc/terms/> .\n";
144
+ $turtle .= "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
145
+ $turtle .= "@prefix vehicle: <http://fleetbase.io/ontology/vehicle#> .\n";
146
+ $turtle .= "@prefix fleet: <http://fleetbase.io/ontology/fleet#> .\n\n";
147
+
148
+ $vehicleUri = "<#vehicle-{$vehicle->uuid}>";
149
+
150
+ $turtle .= "$vehicleUri a vehicle:Vehicle ;\n";
151
+ $turtle .= " dc:identifier \"{$vehicle->uuid}\" ;\n";
152
+
153
+ if ($vehicle->make) {
154
+ $turtle .= " vehicle:make \"{$vehicle->make}\" ;\n";
155
+ }
156
+
157
+ if ($vehicle->model) {
158
+ $turtle .= " vehicle:model \"{$vehicle->model}\" ;\n";
159
+ }
160
+
161
+ if ($vehicle->year) {
162
+ $turtle .= " vehicle:year \"{$vehicle->year}\" ;\n";
163
+ }
164
+
165
+ if ($vehicle->trim) {
166
+ $turtle .= " vehicle:trim \"{$vehicle->trim}\" ;\n";
167
+ }
168
+
169
+ if ($vehicle->type) {
170
+ $turtle .= " vehicle:type \"{$vehicle->type}\" ;\n";
171
+ }
172
+
173
+ if ($vehicle->plate_number) {
174
+ $turtle .= " vehicle:plateNumber \"{$vehicle->plate_number}\" ;\n";
175
+ }
176
+
177
+ if ($vehicle->vin) {
178
+ $turtle .= " vehicle:vin \"{$vehicle->vin}\" ;\n";
179
+ }
180
+
181
+ if ($vehicle->status) {
182
+ $turtle .= " fleet:status \"{$vehicle->status}\" ;\n";
183
+ }
184
+
185
+ if ($vehicle->online !== null) {
186
+ $onlineStatus = $vehicle->online ? 'true' : 'false';
187
+ $turtle .= " fleet:online \"$onlineStatus\"^^<http://www.w3.org/2001/XMLSchema#boolean> ;\n";
188
+ }
189
+
190
+ // Add location if available
191
+ if ($vehicle->location) {
192
+ $turtle .= " fleet:location [\n";
193
+ $turtle .= " a fleet:Location ;\n";
194
+
195
+ if (isset($vehicle->location['coordinates'])) {
196
+ $coords = $vehicle->location['coordinates'];
197
+ if (isset($coords[0]) && isset($coords[1])) {
198
+ $turtle .= " fleet:longitude \"{$coords[0]}\"^^<http://www.w3.org/2001/XMLSchema#decimal> ;\n";
199
+ $turtle .= " fleet:latitude \"{$coords[1]}\"^^<http://www.w3.org/2001/XMLSchema#decimal> ;\n";
200
+ }
201
+ }
202
+
203
+ $turtle .= " ] ;\n";
204
+ }
205
+
206
+ // Add driver relationship
207
+ if ($vehicle->driver) {
208
+ $turtle .= " fleet:assignedDriver [\n";
209
+ $turtle .= " a foaf:Person ;\n";
210
+ $turtle .= " foaf:name \"{$vehicle->driver->name}\" ;\n";
211
+ $turtle .= " dc:identifier \"{$vehicle->driver->uuid}\" ;\n";
212
+ $turtle .= " ] ;\n";
213
+ }
214
+
215
+ // Add vendor relationship
216
+ if ($vehicle->vendor) {
217
+ $turtle .= " fleet:vendor [\n";
218
+ $turtle .= " a fleet:Vendor ;\n";
219
+ $turtle .= " foaf:name \"{$vehicle->vendor->name}\" ;\n";
220
+ $turtle .= " dc:identifier \"{$vehicle->vendor->uuid}\" ;\n";
221
+ $turtle .= " ] ;\n";
222
+ }
223
+
224
+ // Add metadata
225
+ $turtle .= " dc:created \"{$vehicle->created_at->toISOString()}\" ;\n";
226
+ $turtle .= " dc:modified \"{$vehicle->updated_at->toISOString()}\" ;\n";
227
+ $turtle .= ' fleet:syncedAt "' . now()->toISOString() . "\" ;\n";
228
+ $turtle .= " fleet:syncedFrom \"fleetbase\" .\n";
229
+
230
+ return $turtle;
231
+ }
232
+
233
+ /**
234
+ * Generate filename for vehicle resource.
235
+ */
236
+ private function generateVehicleFilename(Vehicle $vehicle): string
237
+ {
238
+ $identifier = $vehicle->plate_number ?: $vehicle->vin ?: $vehicle->uuid;
239
+ $slug = Str::slug($identifier);
240
+
241
+ return "vehicle-{$slug}.ttl";
242
+ }
243
+
244
+ /**
245
+ * Get vehicle display name.
246
+ */
247
+ private function getVehicleDisplayName(Vehicle $vehicle): string
248
+ {
249
+ $parts = array_filter([
250
+ $vehicle->year,
251
+ $vehicle->make,
252
+ $vehicle->model,
253
+ $vehicle->trim,
254
+ ]);
255
+
256
+ $name = implode(' ', $parts);
257
+
258
+ if ($vehicle->plate_number) {
259
+ $name .= " ({$vehicle->plate_number})";
260
+ }
261
+
262
+ return $name ?: "Vehicle {$vehicle->uuid}";
263
+ }
264
+
265
+ /**
266
+ * Get sync status for a pod.
267
+ */
268
+ public function getSyncStatus(SolidIdentity $identity, string $podId): array
269
+ {
270
+ try {
271
+ // This would typically check a sync status table or cache
272
+ // For now, return a basic status
273
+ return [
274
+ 'pod_id' => $podId,
275
+ 'last_sync' => null,
276
+ 'total_vehicles' => 0,
277
+ 'synced_vehicles' => 0,
278
+ 'failed_vehicles' => 0,
279
+ 'status' => 'ready',
280
+ ];
281
+ } catch (\Throwable $e) {
282
+ Log::error('[GET SYNC STATUS ERROR]', [
283
+ 'pod_id' => $podId,
284
+ 'error' => $e->getMessage(),
285
+ ]);
286
+ throw $e;
287
+ }
288
+ }
289
+ }
@@ -44,4 +44,14 @@ class Utils extends FleetbaseUtils
44
44
 
45
45
  return null;
46
46
  }
47
+
48
+ /**
49
+ * Get the Solid server URL from configuration.
50
+ *
51
+ * @return string
52
+ */
53
+ public static function getSolidServerUrl(): string
54
+ {
55
+ return config('solid.server.url', 'http://localhost:3000');
56
+ }
47
57
  }
@@ -28,11 +28,35 @@ Route::prefix(config('solid.api.routing.prefix', 'solid'))->namespace('Fleetbase
28
28
  $router->group(
29
29
  ['prefix' => 'v1'],
30
30
  function ($router) {
31
- $router->get('pods', 'SolidController@getPods');
32
31
  $router->get('authenticate/{identifier}', 'SolidController@authenticate');
33
32
  $router->group(['middleware' => ['fleetbase.protected']], function ($router) {
33
+ // Authentication status and management
34
34
  $router->get('account', 'SolidController@getAccountIndex');
35
35
  $router->get('request-authentication', 'SolidController@requestAuthentication');
36
+ $router->get('authentication-status', 'SolidController@getAuthenticationStatus');
37
+ $router->post('logout', 'SolidController@logout');
38
+
39
+ // Account and profile
40
+ $router->get('account', 'SolidController@getAccountIndex');
41
+ $router->get('profile', 'SolidController@getProfileData');
42
+
43
+ // Data management routes (single-pod architecture)
44
+ $router->get('data', 'DataController@index');
45
+ $router->get('data/folder/{slug}', 'DataController@showFolder');
46
+ $router->post('data/folder', 'DataController@createFolder');
47
+ $router->delete('data/{type}/{slug}', 'DataController@deleteItem');
48
+ $router->post('data/import', 'DataController@importResources');
49
+
50
+
51
+
52
+ // Resource sync endpoints
53
+ $router->get('sync-status', 'SolidController@getSyncStatus');
54
+ $router->post('sync-vehicles', 'SolidController@syncVehicles');
55
+ $router->post('sync-drivers', 'SolidController@syncDrivers');
56
+ $router->post('sync-orders', 'SolidController@syncOrders');
57
+ $router->post('sync-all', 'SolidController@syncAll');
58
+
59
+ // Server configuration
36
60
  $router->get('server-config', 'SolidController@getServerConfig');
37
61
  $router->post('server-config', 'SolidController@saveServerConfig');
38
62
  });
@@ -1,3 +0,0 @@
1
- <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
- <div class="modal-body-container"></div>
3
- </Modal::Default>
@@ -1,5 +0,0 @@
1
- <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
- <div class="modal-body-container">
3
- <InputGroup @name="Pod Name" @value={{@options.pod.name}} @helpText="Input a name for your new Pod" />
4
- </div>
5
- </Modal::Default>