feat: Add API documentation and permissions configuration

This commit is contained in:
2026-02-22 23:16:37 +01:00
parent b67542d159
commit b312dc56e3
3 changed files with 704 additions and 47 deletions

View File

@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(python3:*)",
"Bash(py:*)",
"Bash(npm ls:*)",
"WebFetch(domain:github.com)"
]
}
}

View File

@@ -7,38 +7,57 @@
"size": 1508, "size": 1508,
"lmtime": 1771460522111, "lmtime": 1771460522111,
"modified": false "modified": false
},
"migrate.php": {
"type": "-",
"size": 1357,
"lmtime": 1771692294431,
"modified": false
} }
}, },
"bootstrap": { "bootstrap": {
"app.php": { "app.php": {
"type": "-", "type": "-",
"size": 869, "size": 877,
"lmtime": 1771459558621, "lmtime": 1771692620052,
"modified": false "modified": false
} }
}, },
"composer.json": { "composer.json": {
"type": "-", "type": "-",
"size": 330, "size": 368,
"lmtime": 1771460587937, "lmtime": 1771691973809,
"modified": false "modified": false
}, },
"config": { "config": {
"app.php": { "app.php": {
"type": "-", "type": "-",
"size": 622, "size": 690,
"lmtime": 1771460431365, "lmtime": 1771692227139,
"modified": false "modified": false
}, },
"auth.php": { "auth.php": {
"type": "-", "type": "-",
"size": 289, "size": 289,
"lmtime": 1771459566674, "lmtime": 1771691433920,
"modified": false
},
"database.php": {
"type": "-",
"size": 425,
"lmtime": 1771691899980,
"modified": false "modified": false
} }
}, },
"database": { "database": {
"migrations": {}, "migrations": {
"20260221_000001_create_users_table.sql": {
"type": "-",
"size": 333,
"lmtime": 1771691952235,
"modified": false
}
},
"seeders": {} "seeders": {}
}, },
"DOCS": { "DOCS": {
@@ -48,6 +67,12 @@
"lmtime": 1771460831573, "lmtime": 1771460831573,
"modified": false "modified": false
}, },
"FRONTEND_STANDARDS.md": {
"type": "-",
"size": 766,
"lmtime": 1771691123295,
"modified": false
},
"PLAN_PROJEKTU.md": { "PLAN_PROJEKTU.md": {
"type": "-", "type": "-",
"size": 5181, "size": 5181,
@@ -59,12 +84,30 @@
"size": 132, "size": 132,
"lmtime": 1771460631339, "lmtime": 1771460631339,
"modified": false "modified": false
},
"MIGRATIONS.md": {
"type": "-",
"size": 599,
"lmtime": 1771692333726,
"modified": false
} }
}, },
".env": {
"type": "-",
"size": 325,
"lmtime": 1771692631760,
"modified": false
},
".env.example": { ".env.example": {
"type": "-", "type": "-",
"size": 222, "size": 229,
"lmtime": 1771459546785, "lmtime": 1771692627769,
"modified": false
},
".gitignore": {
"type": "-",
"size": 82,
"lmtime": 1771460725903,
"modified": false "modified": false
}, },
".htaccess": { ".htaccess": {
@@ -1235,8 +1278,8 @@
}, },
"package.json": { "package.json": {
"type": "-", "type": "-",
"size": 671, "size": 1005,
"lmtime": 1771460684115, "lmtime": 1771691140832,
"modified": false "modified": false
}, },
"package-lock.json": { "package-lock.json": {
@@ -1250,8 +1293,8 @@
"css": { "css": {
"app.css": { "app.css": {
"type": "-", "type": "-",
"size": 2295, "size": 5370,
"lmtime": 1771460692659, "lmtime": 1771692521342,
"modified": false "modified": false
}, },
"app.css.map": { "app.css.map": {
@@ -1262,8 +1305,8 @@
}, },
"login.css": { "login.css": {
"type": "-", "type": "-",
"size": 2987, "size": 4504,
"lmtime": 1771460693108, "lmtime": 1771692521824,
"modified": false "modified": false
}, },
"login.css.map": { "login.css.map": {
@@ -1271,10 +1314,27 @@
"size": 958, "size": 958,
"lmtime": 1771460670209, "lmtime": 1771460670209,
"modified": false "modified": false
},
"modules": {
"jquery-alerts.css": {
"type": "-",
"size": 768,
"lmtime": 1771691151859,
"modified": false
}
} }
}, },
"img": {}, "img": {},
"js": {} "js": {
"modules": {
"jquery-alerts.js": {
"type": "-",
"size": 1573,
"lmtime": 1771691107503,
"modified": false
}
}
}
}, },
"index.php": { "index.php": {
"type": "-", "type": "-",
@@ -1299,17 +1359,39 @@
"lang": { "lang": {
"pl.php": { "pl.php": {
"type": "-", "type": "-",
"size": 1393, "size": 3819,
"lmtime": 1771460496306, "lmtime": 1771692642685,
"modified": false "modified": false
} }
}, },
"scss": {
"app.scss": {
"type": "-",
"size": 3651,
"lmtime": 1771692517226,
"modified": false
},
"login.scss": {
"type": "-",
"size": 2507,
"lmtime": 1771691089256,
"modified": false
},
"shared": {
"_ui-components.scss": {
"type": "-",
"size": 3044,
"lmtime": 1771692265024,
"modified": false
}
}
},
"views": { "views": {
"auth": { "auth": {
"login.php": { "login.php": {
"type": "-", "type": "-",
"size": 1521, "size": 1649,
"lmtime": 1771460485980, "lmtime": 1771691098142,
"modified": false "modified": false
} }
}, },
@@ -1324,8 +1406,8 @@
"layouts": { "layouts": {
"app.php": { "app.php": {
"type": "-", "type": "-",
"size": 1014, "size": 1984,
"lmtime": 1771460470614, "lmtime": 1771692509348,
"modified": false "modified": false
}, },
"auth.php": { "auth.php": {
@@ -1334,28 +1416,46 @@
"lmtime": 1771460477300, "lmtime": 1771460477300,
"modified": false "modified": false
} }
},
"users": {
"index.php": {
"type": "-",
"size": 2379,
"lmtime": 1771691646298,
"modified": false
}
},
"settings": {
"database.php": {
"type": "-",
"size": 3470,
"lmtime": 1771692300314,
"modified": false
}
} }
}, },
"scss": { "modules": {
"app.scss": { "jquery-alerts": {
"type": "-", "jquery-alerts.js": {
"size": 2831, "type": "-",
"lmtime": 1771460151261, "size": 1573,
"modified": false "lmtime": 1771691107503,
}, "modified": false
"login.scss": { },
"type": "-", "jquery-alerts.scss": {
"size": 3671, "type": "-",
"lmtime": 1771460219578, "size": 945,
"modified": false "lmtime": 1771691116267,
"modified": false
}
} }
} }
}, },
"routes": { "routes": {
"web.php": { "web.php": {
"type": "-", "type": "-",
"size": 1579, "size": 2243,
"lmtime": 1771460461885, "lmtime": 1771692238609,
"modified": false "modified": false
} }
}, },
@@ -1363,10 +1463,24 @@
"Core": { "Core": {
"Application.php": { "Application.php": {
"type": "-", "type": "-",
"size": 4572, "size": 5346,
"lmtime": 1771460438825, "lmtime": 1771692614516,
"modified": false "modified": false
}, },
"Database": {
"ConnectionFactory.php": {
"type": "-",
"size": 1175,
"lmtime": 1771691906777,
"modified": false
},
"Migrator.php": {
"type": "-",
"size": 4739,
"lmtime": 1771692444377,
"modified": false
}
},
"Http": { "Http": {
"Request.php": { "Request.php": {
"type": "-", "type": "-",
@@ -1456,8 +1570,30 @@
}, },
"AuthService.php": { "AuthService.php": {
"type": "-", "type": "-",
"size": 1584, "size": 1599,
"lmtime": 1771459658223, "lmtime": 1771692611409,
"modified": false
}
},
"Users": {
"UserRepository.php": {
"type": "-",
"size": 2878,
"lmtime": 1771691981226,
"modified": false
},
"UsersController.php": {
"type": "-",
"size": 3359,
"lmtime": 1771691595738,
"modified": false
}
},
"Settings": {
"SettingsController.php": {
"type": "-",
"size": 2609,
"lmtime": 1771692449213,
"modified": false "modified": false
} }
} }
@@ -1511,12 +1647,6 @@
} }
}, },
"tmp": {} "tmp": {}
},
".gitignore": {
"type": "-",
"size": 82,
"lmtime": 1771460725903,
"modified": false
} }
} }
}, },

517
DOCS/API.md Normal file
View File

@@ -0,0 +1,517 @@
# shopPRO REST API
REST API do integracji z ordersPRO i innymi systemami zewnetrznymi.
## Autentykacja
Kazde zapytanie wymaga headera `X-Api-Key` z kluczem API.
```
X-Api-Key: {klucz_api}
```
Klucz przechowywany jest w `pp_settings` jako parametr `api_key`. API jest stateless (bez sesji).
## Format odpowiedzi
### Sukces (HTTP 200)
```json
{
"status": "ok",
"data": { ... }
}
```
### Blad
```json
{
"status": "error",
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
Kody bledow:
| Kod | HTTP | Opis |
|-----|------|------|
| `UNAUTHORIZED` | 401 | Brak lub nieprawidlowy klucz API |
| `BAD_REQUEST` | 400 | Brakujace lub niepoprawne parametry |
| `NOT_FOUND` | 404 | Nie znaleziono zasobu/endpointu/akcji |
| `METHOD_NOT_ALLOWED` | 405 | Nieprawidlowa metoda HTTP |
| `INTERNAL_ERROR` | 500 | Blad wewnetrzny serwera |
## Endpointy
### Zamowienia
#### Lista zamowien
```
GET api.php?endpoint=orders&action=list
```
Parametry filtrowania (opcjonalne):
| Parametr | Typ | Opis |
|----------|-----|------|
| `status` | int | Filtruj po statusie zamowienia |
| `paid` | int (0/1) | Filtruj po statusie platnosci |
| `date_from` | date (YYYY-MM-DD) | Zamowienia od daty |
| `date_to` | date (YYYY-MM-DD) | Zamowienia do daty |
| `updated_since` | datetime (YYYY-MM-DD HH:MM:SS) | Zamowienia zmodyfikowane od podanej daty (klucz do pollingu) |
| `number` | string | Szukaj po numerze zamowienia |
| `client` | string | Szukaj po imieniu, nazwisku lub emailu klienta |
| `page` | int | Numer strony (domyslnie 1) |
| `per_page` | int | Wynikow na strone (domyslnie 50, max 100) |
Odpowiedz:
```json
{
"status": "ok",
"data": {
"items": [
{
"id": 42,
"number": "2026/02/001",
"date_order": "2026-02-19 10:30:00",
"updated_at": "2026-02-19 12:00:00",
"status": 4,
"paid": 1,
"client_name": "Jan",
"client_surname": "Kowalski",
"client_email": "jan@example.com",
"client_phone": "111222333",
"client_street": "Testowa 1",
"client_postal_code": "00-000",
"client_city": "Warszawa",
"firm_name": null,
"firm_nip": null,
"transport": "Kurier DPD",
"transport_cost": 15.00,
"payment_method": "Przelew bankowy",
"summary": 150.00
}
],
"total": 1,
"page": 1,
"per_page": 50
}
}
```
#### Szczegoly zamowienia
```
GET api.php?endpoint=orders&action=get&id={order_id}
```
Zwraca pelne dane zamowienia z produktami i historia statusow.
#### Zmiana statusu zamowienia
```
PUT api.php?endpoint=orders&action=change_status&id={order_id}
Content-Type: application/json
{
"status_id": 5,
"send_email": true
}
```
Odpowiedz:
```json
{
"status": "ok",
"data": {
"order_id": 42,
"status_id": 5,
"changed": true
}
}
```
#### Oznacz jako oplacone
```
PUT api.php?endpoint=orders&action=set_paid&id={order_id}
```
Opcjonalnie w body: `{"send_email": true}`
#### Oznacz jako nieoplacone
```
PUT api.php?endpoint=orders&action=set_unpaid&id={order_id}
```
### Produkty
#### Lista produktow
```
GET api.php?endpoint=products&action=list
```
Parametry filtrowania (opcjonalne):
| Parametr | Typ | Opis |
|----------|-----|------|
| `search` | string | Szukaj po nazwie, EAN lub SKU |
| `status` | int (0/1) | Filtruj po statusie (1 = aktywny, 0 = nieaktywny) |
| `promoted` | int (0/1) | Filtruj po promocji |
| `attribute_{id}` | int | Filtruj po atrybucie — `attribute_1=3` oznacza atrybut 1 = wartosc 3 (wiele filtrow AND) |
| `sort` | string | Sortuj po: id, name, price_brutto, status, promoted, quantity (domyslnie id) |
| `sort_dir` | string | Kierunek: ASC lub DESC (domyslnie DESC) |
| `page` | int | Numer strony (domyslnie 1) |
| `per_page` | int | Wynikow na strone (domyslnie 50, max 100) |
Odpowiedz:
```json
{
"status": "ok",
"data": {
"items": [
{
"id": 1,
"sku": "PROD-001",
"ean": "5901234123457",
"name": "Produkt testowy",
"price_brutto": 99.99,
"price_brutto_promo": null,
"price_netto": 81.29,
"price_netto_promo": null,
"quantity": 10,
"status": 1,
"promoted": 0,
"vat": 23,
"weight": 0.5,
"main_image": "product1.jpg",
"date_add": "2026-01-15 10:00:00",
"date_modify": "2026-02-19 12:00:00"
}
],
"total": 1,
"page": 1,
"per_page": 50
}
}
```
#### Szczegoly produktu
```
GET api.php?endpoint=products&action=get&id={product_id}
```
Zwraca pelne dane produktu z jezykami, zdjeciami, kategoriami i atrybutami.
Odpowiedz:
```json
{
"status": "ok",
"data": {
"id": 1,
"sku": "PROD-001",
"ean": "5901234123457",
"price_brutto": 99.99,
"price_brutto_promo": null,
"price_netto": 81.29,
"price_netto_promo": null,
"quantity": 10,
"status": 1,
"promoted": 0,
"vat": 23,
"weight": 0.5,
"stock_0_buy": 0,
"custom_label_0": null,
"set_id": null,
"product_unit_id": 1,
"producer_id": 3,
"date_add": "2026-01-15 10:00:00",
"date_modify": "2026-02-19 12:00:00",
"languages": {
"pl": {
"name": "Produkt testowy",
"short_description": "Krotki opis",
"description": "<p>Pelny opis produktu</p>",
"meta_description": null,
"meta_keywords": null,
"meta_title": null,
"seo_link": "produkt-testowy",
"copy_from": null,
"warehouse_message_zero": null,
"warehouse_message_nonzero": null,
"tab_name_1": null,
"tab_description_1": null,
"tab_name_2": null,
"tab_description_2": null,
"canonical": null
}
},
"images": [
{"id": 1, "src": "product1.jpg", "alt": "Zdjecie produktu"}
],
"categories": [1, 5],
"attributes": [
{
"attribute_id": 1,
"attribute_type": 1,
"attribute_names": {"pl": "Kolor", "en": "Color"},
"value_id": 3,
"value_names": {"pl": "Czerwony", "en": "Red"}
}
],
"variants": [
{
"id": 101,
"permutation_hash": "1-3|2-5",
"sku": "PROD-001-RED-L",
"ean": null,
"price_brutto": 109.99,
"price_brutto_promo": null,
"price_netto": 89.42,
"price_netto_promo": null,
"quantity": 5,
"stock_0_buy": 0,
"weight": 0.5,
"status": 1,
"attributes": [
{"attribute_id": 1, "attribute_names": {"pl": "Kolor"}, "value_id": 3, "value_names": {"pl": "Czerwony"}},
{"attribute_id": 2, "attribute_names": {"pl": "Rozmiar"}, "value_id": 5, "value_names": {"pl": "L"}}
]
}
]
}
}
```
#### Tworzenie produktu
```
POST api.php?endpoint=products&action=create
Content-Type: application/json
{
"price_brutto": 99.99,
"vat": 23,
"quantity": 10,
"status": 1,
"sku": "PROD-001",
"ean": "5901234123457",
"weight": 0.5,
"languages": {
"pl": {
"name": "Nowy produkt",
"description": "<p>Opis produktu</p>"
}
},
"categories": [1, 5],
"products_related": [10, 20]
}
```
Wymagane: `languages` (min. 1 jezyk z `name`) oraz `price_brutto`.
Odpowiedz (HTTP 201):
```json
{
"status": "ok",
"data": {
"id": 42
}
}
```
#### Aktualizacja produktu
```
PUT api.php?endpoint=products&action=update&id={product_id}
Content-Type: application/json
{
"price_brutto": 129.99,
"status": 1,
"languages": {
"pl": {
"name": "Zaktualizowana nazwa"
}
}
}
```
Partial update — wystarczy przeslac tylko zmienione pola. Pola nieprzeslane zachowuja aktualna wartosc.
Odpowiedz: pelne dane produktu (jak w `get`).
### Warianty produktow
#### Lista wariantow produktu
```
GET api.php?endpoint=products&action=variants&id={product_id}
```
Zwraca warianty produktu nadrzednego wraz z dostepnymi atrybutami.
Odpowiedz:
```json
{
"status": "ok",
"data": {
"product_id": 1,
"available_attributes": [
{
"id": 1,
"type": 1,
"status": 1,
"names": {"pl": "Kolor", "en": "Color"},
"values": [
{"id": 3, "names": {"pl": "Czerwony", "en": "Red"}, "is_default": 0, "impact_on_the_price": null},
{"id": 4, "names": {"pl": "Niebieski", "en": "Blue"}, "is_default": 0, "impact_on_the_price": 10.0}
]
}
],
"variants": [
{
"id": 101,
"permutation_hash": "1-3",
"sku": "PROD-001-RED",
"ean": null,
"price_brutto": 109.99,
"price_brutto_promo": null,
"price_netto": 89.42,
"price_netto_promo": null,
"quantity": 5,
"stock_0_buy": 0,
"weight": 0.5,
"status": 1,
"attributes": [
{"attribute_id": 1, "attribute_names": {"pl": "Kolor"}, "value_id": 3, "value_names": {"pl": "Czerwony"}}
]
}
]
}
}
```
#### Tworzenie wariantu
```
POST api.php?endpoint=products&action=create_variant&id={product_id}
Content-Type: application/json
{
"attributes": {"1": 3, "2": 5},
"sku": "PROD-001-RED-L",
"ean": "5901234123458",
"price_brutto": 109.99,
"quantity": 5,
"weight": 0.5
}
```
Wymagane: `attributes` (mapa attribute_id -> value_id, min. 1). Kombinacja atrybutow musi byc unikalna.
Odpowiedz (HTTP 201): pelne dane wariantu.
#### Aktualizacja wariantu
```
PUT api.php?endpoint=products&action=update_variant&id={variant_id}
Content-Type: application/json
{
"sku": "PROD-001-RED-XL",
"price_brutto": 119.99,
"quantity": 3
}
```
Partial update — mozna zmienic: sku, ean, price_brutto, price_netto, price_brutto_promo, price_netto_promo, quantity, stock_0_buy, weight, status.
Odpowiedz: pelne dane wariantu.
#### Usuwanie wariantu
```
DELETE api.php?endpoint=products&action=delete_variant&id={variant_id}
```
Odpowiedz:
```json
{
"status": "ok",
"data": {"id": 101, "deleted": true}
}
```
### Slowniki
#### Lista statusow zamowien
```
GET api.php?endpoint=dictionaries&action=statuses
```
Odpowiedz:
```json
{
"status": "ok",
"data": [
{"id": 0, "name": "Nowe"},
{"id": 1, "name": "Oplacone"},
{"id": 4, "name": "W realizacji"},
{"id": 6, "name": "Wyslane"}
]
}
```
#### Lista metod transportu
```
GET api.php?endpoint=dictionaries&action=transports
```
#### Lista metod platnosci
```
GET api.php?endpoint=dictionaries&action=payment_methods
```
#### Lista atrybutow
```
GET api.php?endpoint=dictionaries&action=attributes
```
Zwraca aktywne atrybuty z wartosciami i wielojezycznymi nazwami.
Odpowiedz:
```json
{
"status": "ok",
"data": [
{
"id": 1,
"type": 1,
"status": 1,
"names": {"pl": "Kolor", "en": "Color"},
"values": [
{"id": 3, "names": {"pl": "Czerwony"}, "is_default": 0, "impact_on_the_price": null},
{"id": 4, "names": {"pl": "Niebieski"}, "is_default": 1, "impact_on_the_price": 10.0}
]
}
]
}
```
## Polling
Aby pobierac tylko nowe/zmienione zamowienia, uzyj parametru `updated_since`:
```
GET api.php?endpoint=orders&action=list&updated_since=2026-02-19 12:00:00
```
Kolumna `updated_at` w `pp_shop_orders` jest aktualizowana automatycznie przy kazdej modyfikacji zamowienia (zmiana statusu, platnosci, edycja danych, tworzenie zamowienia).
## Konfiguracja
Klucz API ustawia sie w panelu admina w ustawieniach sklepu lub bezposrednio w bazie:
```sql
INSERT INTO pp_settings (param, value) VALUES ('api_key', 'twoj-klucz-api');
-- lub
UPDATE pp_settings SET value = 'twoj-klucz-api' WHERE param = 'api_key';
```
## Architektura
- Entry point: `api.php`
- Router: `\api\ApiRouter` (`autoload/api/ApiRouter.php`)
- Kontrolery: `autoload/api/Controllers/`
- `OrdersApiController` — zamowienia (5 akcji)
- `ProductsApiController` — produkty (8 akcji: list, get, create, update, variants, create_variant, update_variant, delete_variant)
- `DictionariesApiController` — slowniki (4 akcje: statuses, transports, payment_methods, attributes)