Route changed to /api/v1/{lot_number}. LotResource now includes
packing details, units_produced, quantity_g and recipe version.
CORS config added for public API access. README documents the
endpoint, example response and subdomain setup instructions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
269570c7d6
commit
e14baca7e0
5 files changed
+107
-3
No files matched your search
@@ -95,6 +95,80 @@ Rezepte sind versioniert und einem Produkt zugeordnet:
|
||||
|
||||
---
|
||||
|
||||
## LOT-API
|
||||
|
||||
Öffentliche JSON-API zur Rückverfolgung von Chargen. Kein Auth erforderlich.
|
||||
|
||||
### Endpunkt
|
||||
|
||||
```
|
||||
GET https://api.sms.spices.blue/api/v1/{lot_number}
|
||||
```
|
||||
|
||||
### Beispiel-Antwort
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"lot_number": "LOT-2024-001",
|
||||
"produced_at": "2024-10-01",
|
||||
"expires_at": "2026-10-01",
|
||||
"units_produced": 50,
|
||||
"quantity_g": 5000,
|
||||
"product": {
|
||||
"name": "Curry Klassik",
|
||||
"description": "..."
|
||||
},
|
||||
"packing": {
|
||||
"name": "Standbodenbeutel",
|
||||
"size_g": 100,
|
||||
"sku": "CUR-100",
|
||||
"gtin": "4012345678901"
|
||||
},
|
||||
"recipe": {
|
||||
"version": 3,
|
||||
"created_at": "2024-09-15"
|
||||
},
|
||||
"spices": [
|
||||
{
|
||||
"name": "Kurkuma",
|
||||
"percentage": 60.0,
|
||||
"origin": {
|
||||
"country": "Indien",
|
||||
"farmer": "Singh Farm",
|
||||
"supplier": "Gewürzhandel GmbH",
|
||||
"notes": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Fehler (nicht gefunden):
|
||||
```json
|
||||
{ "error": "LOT-Nummer nicht gefunden." }
|
||||
```
|
||||
|
||||
### Subdomain-Setup auf dem Server
|
||||
|
||||
Der Container läuft intern auf `127.0.0.1:5000`. Der Host-Proxy (Nginx/Caddy) muss `api.sms.spices.blue` auf denselben Port zeigen — Beispiel für Nginx:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
server_name api.sms.spices.blue;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Danach SSL via `certbot --nginx -d api.sms.spices.blue` erneuern.
|
||||
|
||||
---
|
||||
|
||||
## Cronjob
|
||||
|
||||
Auf dem Server einrichten, damit der Laravel-Scheduler läuft:
|
||||
@@ -198,4 +272,5 @@ Table batch_origins {
|
||||
- [x] Shopify-Import (Produkte, Varianten, Lagerbestand, Bilder, Beschreibung, Preise)
|
||||
- [x] Continuous Deployment via Gitea Actions + Docker
|
||||
- [x] Frontend (Sidebar, Navigation, Barcode-Preview, Bild-Grid)
|
||||
- [x] LOT-API (JSON, öffentlich, Subdomain api.sms.spices.blue)
|
||||
- [ ] Dashboard (Füllstände, Lager, Sales-Zeitspanne)
|
||||
@@ -13,6 +13,7 @@ class LotController extends Controller
|
||||
{
|
||||
$batch = Batch::with([
|
||||
'product',
|
||||
'size.packing',
|
||||
'recipe.spices',
|
||||
'origins.spice',
|
||||
'origins.supplier',
|
||||
|
||||
@@ -13,12 +13,21 @@ class LotResource extends JsonResource
|
||||
'lot_number' => $this->lot_number,
|
||||
'produced_at' => $this->produced_at?->toDateString(),
|
||||
'expires_at' => $this->expires_at?->toDateString(),
|
||||
'units_produced' => $this->units_produced,
|
||||
'quantity_g' => (float) $this->quantity_g,
|
||||
'product' => [
|
||||
'name' => $this->product->title,
|
||||
'description' => $this->product->description,
|
||||
],
|
||||
'packing' => $this->size ? [
|
||||
'name' => $this->size->packing->name,
|
||||
'size_g' => (float) $this->size->packing->size,
|
||||
'sku' => $this->size->sku,
|
||||
'gtin' => $this->size->gtin,
|
||||
] : null,
|
||||
'recipe' => [
|
||||
'name' => $this->recipe->name,
|
||||
'version' => $this->recipe->version,
|
||||
'created_at' => $this->recipe->created_at->toDateString(),
|
||||
],
|
||||
'spices' => $this->recipe->spices->map(function ($spice) {
|
||||
$origin = $this->origins->firstWhere('spice_id', $spice->id);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'paths' => ['api/*'],
|
||||
|
||||
'allowed_methods' => ['GET'],
|
||||
|
||||
'allowed_origins' => ['*'],
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
'allowed_headers' => ['Content-Type', 'Accept'],
|
||||
|
||||
'exposed_headers' => [],
|
||||
|
||||
'max_age' => 86400,
|
||||
|
||||
'supports_credentials' => false,
|
||||
];
|
||||
+2
-2
@@ -5,8 +5,8 @@ use App\Http\Controllers\ShopifyWebhookController;
|
||||
use App\Http\Middleware\VerifyShopifyWebhook;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/lot/{lot_number}', [LotController::class, 'show'])
|
||||
->middleware('throttle:30,1')
|
||||
Route::get('/v1/{lot_number}', [LotController::class, 'show'])
|
||||
->middleware('throttle:60,1')
|
||||
->name('api.lot');
|
||||
|
||||
Route::post('/webhooks/shopify/orders-paid', [ShopifyWebhookController::class, 'ordersPaid'])
|
||||
|
||||
Reference in new issue
Block a user