feat: Add API functions for product management and validation, including JSON response handling
This commit is contained in:
12
.vscode/ftp-kr.sync.cache.json
vendored
12
.vscode/ftp-kr.sync.cache.json
vendored
@@ -125,8 +125,8 @@
|
||||
},
|
||||
"class.Products.php": {
|
||||
"type": "-",
|
||||
"size": 49998,
|
||||
"lmtime": 1772612176689,
|
||||
"size": 50211,
|
||||
"lmtime": 1772916736693,
|
||||
"modified": false
|
||||
},
|
||||
"class.Site.php": {
|
||||
@@ -193,8 +193,8 @@
|
||||
},
|
||||
"class.Products.php": {
|
||||
"type": "-",
|
||||
"size": 41405,
|
||||
"lmtime": 1772612327756,
|
||||
"size": 42460,
|
||||
"lmtime": 1772916728114,
|
||||
"modified": false
|
||||
},
|
||||
"class.Users.php": {
|
||||
@@ -786,8 +786,8 @@
|
||||
"products": {
|
||||
"main_view.php": {
|
||||
"type": "-",
|
||||
"size": 77412,
|
||||
"lmtime": 1772612193848,
|
||||
"size": 77741,
|
||||
"lmtime": 1772916753426,
|
||||
"modified": false
|
||||
},
|
||||
"product_history.php": {
|
||||
|
||||
225
api.php
225
api.php
@@ -37,6 +37,52 @@ $mdb = new medoo( [
|
||||
return R::getRedBean() -> dispense( $type );
|
||||
} );
|
||||
|
||||
function api_json_response( $data, $http_code = 200 )
|
||||
{
|
||||
http_response_code( (int) $http_code );
|
||||
echo json_encode( $data );
|
||||
exit;
|
||||
}
|
||||
|
||||
function api_validate_api_key( $mdb )
|
||||
{
|
||||
$api_key = trim( (string) \S::get( 'api_key' ) );
|
||||
$stored_key = trim( (string) $mdb -> get( 'settings', 'setting_value', [ 'setting_key' => 'api_key' ] ) );
|
||||
|
||||
if ( $api_key === '' || $stored_key === '' || !hash_equals( $stored_key, $api_key ) )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Invalid api_key' ], 401 );
|
||||
}
|
||||
}
|
||||
|
||||
function api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id )
|
||||
{
|
||||
return $mdb -> query(
|
||||
'SELECT p.id, p.name, p.title, p.google_product_category
|
||||
FROM products p
|
||||
JOIN clients cl ON p.client_id = cl.id
|
||||
WHERE p.offer_id = :offer_id
|
||||
AND cl.id = :client_id
|
||||
LIMIT 1',
|
||||
[
|
||||
':offer_id' => (string) $offer_id,
|
||||
':client_id' => (int) $client_id
|
||||
]
|
||||
) -> fetch( \PDO::FETCH_ASSOC );
|
||||
}
|
||||
|
||||
function api_normalize_product_text( $value )
|
||||
{
|
||||
$value = trim( (string) $value );
|
||||
|
||||
if ( $value === '' )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
// dodawanie domeny przez API
|
||||
if ( \S::get( 'action' ) == 'domain_tester_add' )
|
||||
{
|
||||
@@ -124,14 +170,7 @@ if ( \S::get( 'action' ) == 'campaign_comment_add' )
|
||||
// Zmiana custom_label_4 dla produktu przez API
|
||||
if ( \S::get( 'action' ) == 'product_custom_label_4_set' )
|
||||
{
|
||||
$api_key = trim( \S::get( 'api_key' ) );
|
||||
$stored_key = $mdb -> get( 'settings', 'setting_value', [ 'setting_key' => 'api_key' ] );
|
||||
|
||||
if ( !$api_key || !$stored_key || $api_key !== $stored_key )
|
||||
{
|
||||
echo json_encode( [ 'result' => 'error', 'message' => 'Invalid api_key' ] );
|
||||
exit;
|
||||
}
|
||||
api_validate_api_key( $mdb );
|
||||
|
||||
$offer_id = trim( \S::get( 'offer_id' ) );
|
||||
$client_id_param = trim( \S::get( 'client_id' ) );
|
||||
@@ -139,34 +178,166 @@ if ( \S::get( 'action' ) == 'product_custom_label_4_set' )
|
||||
|
||||
if ( !$offer_id || !$client_id_param )
|
||||
{
|
||||
echo json_encode( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ] );
|
||||
exit;
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 );
|
||||
}
|
||||
|
||||
$product = $mdb -> query(
|
||||
'SELECT p.id
|
||||
FROM products p
|
||||
JOIN clients cl ON p.client_id = cl.id
|
||||
WHERE p.offer_id = :offer_id
|
||||
AND cl.id = :client_id
|
||||
LIMIT 1',
|
||||
[
|
||||
':offer_id' => $offer_id,
|
||||
':client_id' => (int) $client_id_param
|
||||
]
|
||||
) -> fetch( \PDO::FETCH_ASSOC );
|
||||
$product = api_get_product_by_offer_and_client( $mdb, $offer_id, (int) $client_id_param );
|
||||
|
||||
if ( !$product )
|
||||
{
|
||||
echo json_encode( [ 'result' => 'error', 'message' => 'Product not found' ] );
|
||||
exit;
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 );
|
||||
}
|
||||
|
||||
\factory\Products::set_product_data( $product['id'], 'custom_label_4', $custom_label_4 );
|
||||
\factory\Products::add_product_comment( $product['id'], 'Zmiana etykiety 4 na: ' . $custom_label_4 . ' (API)' );
|
||||
|
||||
echo json_encode( [ 'result' => 'ok' ] );
|
||||
exit;
|
||||
api_json_response( [ 'result' => 'ok' ] );
|
||||
}
|
||||
|
||||
// Zmiana tytulu produktu przez API
|
||||
if ( \S::get( 'action' ) == 'product_title_set' )
|
||||
{
|
||||
api_validate_api_key( $mdb );
|
||||
|
||||
$offer_id = trim( (string) \S::get( 'offer_id' ) );
|
||||
$client_id_param = (int) \S::get( 'client_id' );
|
||||
$new_title = api_normalize_product_text( \S::get( 'title' ) );
|
||||
|
||||
if ( $offer_id === '' || $client_id_param <= 0 )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 );
|
||||
}
|
||||
|
||||
$product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param );
|
||||
|
||||
if ( !$product )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 );
|
||||
}
|
||||
|
||||
$old_title = (string) ( $product['title'] ?? '' );
|
||||
\factory\Products::set_product_data( (int) $product['id'], 'title', $new_title );
|
||||
|
||||
$old_title_for_log = trim( $old_title ) !== '' ? $old_title : '[pusty]';
|
||||
$new_title_for_log = $new_title !== null ? $new_title : '[pusty]';
|
||||
\factory\Products::add_product_comment(
|
||||
(int) $product['id'],
|
||||
'Zmiana tytulu przez API: ' . $old_title_for_log . ' -> ' . $new_title_for_log
|
||||
);
|
||||
|
||||
api_json_response( [
|
||||
'result' => 'ok',
|
||||
'product_id' => (int) $product['id'],
|
||||
'offer_id' => $offer_id,
|
||||
'client_id' => $client_id_param,
|
||||
'title' => $new_title
|
||||
] );
|
||||
}
|
||||
|
||||
// Sprawdzenie, czy tytul produktu byl juz zmieniony
|
||||
if ( \S::get( 'action' ) == 'product_title_changed_check' )
|
||||
{
|
||||
api_validate_api_key( $mdb );
|
||||
|
||||
$offer_id = trim( (string) \S::get( 'offer_id' ) );
|
||||
$client_id_param = (int) \S::get( 'client_id' );
|
||||
|
||||
if ( $offer_id === '' || $client_id_param <= 0 )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 );
|
||||
}
|
||||
|
||||
$product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param );
|
||||
|
||||
if ( !$product )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 );
|
||||
}
|
||||
|
||||
$base_name = trim( (string) ( $product['name'] ?? '' ) );
|
||||
$custom_title = trim( (string) ( $product['title'] ?? '' ) );
|
||||
$is_changed = $custom_title !== '' && $custom_title !== $base_name;
|
||||
|
||||
api_json_response( [
|
||||
'result' => 'ok',
|
||||
'product_id' => (int) $product['id'],
|
||||
'offer_id' => $offer_id,
|
||||
'client_id' => $client_id_param,
|
||||
'title_changed' => $is_changed,
|
||||
'default_name' => $base_name,
|
||||
'custom_title' => $custom_title !== '' ? $custom_title : null
|
||||
] );
|
||||
}
|
||||
|
||||
// Zmiana Google Product Category przez API
|
||||
if ( \S::get( 'action' ) == 'product_google_category_set' )
|
||||
{
|
||||
api_validate_api_key( $mdb );
|
||||
|
||||
$offer_id = trim( (string) \S::get( 'offer_id' ) );
|
||||
$client_id_param = (int) \S::get( 'client_id' );
|
||||
$google_category = api_normalize_product_text( \S::get( 'google_product_category' ) );
|
||||
|
||||
if ( $offer_id === '' || $client_id_param <= 0 )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 );
|
||||
}
|
||||
|
||||
$product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param );
|
||||
|
||||
if ( !$product )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 );
|
||||
}
|
||||
|
||||
$old_category = (string) ( $product['google_product_category'] ?? '' );
|
||||
\factory\Products::set_product_data( (int) $product['id'], 'google_product_category', $google_category );
|
||||
|
||||
$old_category_for_log = trim( $old_category ) !== '' ? $old_category : '[pusty]';
|
||||
$new_category_for_log = $google_category !== null ? $google_category : '[pusty]';
|
||||
\factory\Products::add_product_comment(
|
||||
(int) $product['id'],
|
||||
'Zmiana Google Product Category przez API: ' . $old_category_for_log . ' -> ' . $new_category_for_log
|
||||
);
|
||||
|
||||
api_json_response( [
|
||||
'result' => 'ok',
|
||||
'product_id' => (int) $product['id'],
|
||||
'offer_id' => $offer_id,
|
||||
'client_id' => $client_id_param,
|
||||
'google_product_category' => $google_category
|
||||
] );
|
||||
}
|
||||
|
||||
// Odczyt Google Product Category przez API
|
||||
if ( \S::get( 'action' ) == 'product_google_category_get' )
|
||||
{
|
||||
api_validate_api_key( $mdb );
|
||||
|
||||
$offer_id = trim( (string) \S::get( 'offer_id' ) );
|
||||
$client_id_param = (int) \S::get( 'client_id' );
|
||||
|
||||
if ( $offer_id === '' || $client_id_param <= 0 )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 );
|
||||
}
|
||||
|
||||
$product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param );
|
||||
|
||||
if ( !$product )
|
||||
{
|
||||
api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 );
|
||||
}
|
||||
|
||||
$google_category = trim( (string) ( $product['google_product_category'] ?? '' ) );
|
||||
|
||||
api_json_response( [
|
||||
'result' => 'ok',
|
||||
'product_id' => (int) $product['id'],
|
||||
'offer_id' => $offer_id,
|
||||
'client_id' => $client_id_param,
|
||||
'google_product_category' => $google_category !== '' ? $google_category : null
|
||||
] );
|
||||
}
|
||||
|
||||
// Open Page Rank - zapis
|
||||
@@ -181,4 +352,4 @@ if ( \S::get( 'action' ) == 'domain_opr_save' )
|
||||
|
||||
echo json_encode( ['result' => 'ok'] );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
278
docs/api-public-product-management.md
Normal file
278
docs/api-public-product-management.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# AdsPRO API - Zarzadzanie produktami (publiczna dokumentacja)
|
||||
|
||||
Ten dokument opisuje endpointy HTTP dostepne w `api.php` do zarzadzania danymi produktow.
|
||||
Format jest przygotowany pod integracje automatyczne (AI/agent/workflow).
|
||||
|
||||
## 1. Informacje bazowe
|
||||
|
||||
- Metoda: `GET` lub `POST` (`application/x-www-form-urlencoded` albo query string).
|
||||
- Endpoint bazowy: `https://TWOJA-DOMENA/api.php`
|
||||
- Parametr routingu: `action`
|
||||
- Odpowiedzi: JSON
|
||||
- Kodowanie: UTF-8
|
||||
|
||||
Przyklady zakladaja, ze:
|
||||
- domena: `https://example.com`
|
||||
- klucz API: `YOUR_API_KEY`
|
||||
- klient: `client_id=12`
|
||||
- produkt: `offer_id=SKU-123`
|
||||
|
||||
## 2. Autoryzacja
|
||||
|
||||
Kazdy endpoint produktowy wymaga poprawnego parametru:
|
||||
|
||||
- `api_key` - klucz porownywany z `settings.setting_key = api_key`
|
||||
|
||||
Brak lub zly klucz zwraca:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "error",
|
||||
"message": "Invalid api_key"
|
||||
}
|
||||
```
|
||||
|
||||
HTTP status: `401`
|
||||
|
||||
## 3. Wspolne parametry
|
||||
|
||||
- `offer_id` (string, wymagany) - zewnetrzny identyfikator produktu.
|
||||
- `client_id` (int, wymagany) - lokalny identyfikator klienta.
|
||||
|
||||
Jesli produkt nie istnieje dla pary `offer_id + client_id`, API zwraca:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "error",
|
||||
"message": "Product not found"
|
||||
}
|
||||
```
|
||||
|
||||
HTTP status: `404`
|
||||
|
||||
## 4. Endpointy produktowe
|
||||
|
||||
### 4.1 Ustawienie tytulu produktu
|
||||
|
||||
- `action=product_title_set`
|
||||
- Cel: zapisuje `products.title`
|
||||
|
||||
Parametry:
|
||||
- `api_key` (string, wymagany)
|
||||
- `offer_id` (string, wymagany)
|
||||
- `client_id` (int, wymagany)
|
||||
- `title` (string, opcjonalny)
|
||||
|
||||
Uwagi:
|
||||
- `title` jest przycinany (`trim`).
|
||||
- Pusty `title` ustawia `products.title = NULL` (czyszczenie pola).
|
||||
- Zmiana zapisuje komentarz techniczny w `products_comments`.
|
||||
|
||||
Przyklad:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://example.com/api.php" \
|
||||
-d "action=product_title_set" \
|
||||
-d "api_key=YOUR_API_KEY" \
|
||||
-d "client_id=12" \
|
||||
-d "offer_id=SKU-123" \
|
||||
-d "title=Buty biegowe meskie Air Run 42"
|
||||
```
|
||||
|
||||
Przyklad odpowiedzi:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "ok",
|
||||
"product_id": 987,
|
||||
"offer_id": "SKU-123",
|
||||
"client_id": 12,
|
||||
"title": "Buty biegowe meskie Air Run 42"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Sprawdzenie, czy tytul byl zmieniony
|
||||
|
||||
- `action=product_title_changed_check`
|
||||
- Cel: sprawdza, czy custom tytul rozni sie od bazowej nazwy produktu
|
||||
|
||||
Logika pola `title_changed`:
|
||||
- `true` gdy `products.title` jest niepuste i inne niz `products.name`
|
||||
- `false` w pozostalych przypadkach
|
||||
|
||||
Parametry:
|
||||
- `api_key` (string, wymagany)
|
||||
- `offer_id` (string, wymagany)
|
||||
- `client_id` (int, wymagany)
|
||||
|
||||
Przyklad:
|
||||
|
||||
```bash
|
||||
curl -G "https://example.com/api.php" \
|
||||
--data-urlencode "action=product_title_changed_check" \
|
||||
--data-urlencode "api_key=YOUR_API_KEY" \
|
||||
--data-urlencode "client_id=12" \
|
||||
--data-urlencode "offer_id=SKU-123"
|
||||
```
|
||||
|
||||
Przyklad odpowiedzi:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "ok",
|
||||
"product_id": 987,
|
||||
"offer_id": "SKU-123",
|
||||
"client_id": 12,
|
||||
"title_changed": true,
|
||||
"default_name": "Buty Air Run - wariant bazowy",
|
||||
"custom_title": "Buty biegowe meskie Air Run 42"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Ustawienie Google Product Category
|
||||
|
||||
- `action=product_google_category_set`
|
||||
- Cel: zapisuje `products.google_product_category`
|
||||
|
||||
Parametry:
|
||||
- `api_key` (string, wymagany)
|
||||
- `offer_id` (string, wymagany)
|
||||
- `client_id` (int, wymagany)
|
||||
- `google_product_category` (string, opcjonalny)
|
||||
|
||||
Uwagi:
|
||||
- wartosc jest przycinana (`trim`)
|
||||
- pusta wartosc ustawia `NULL` (czyszczenie pola)
|
||||
- zmiana zapisuje komentarz techniczny w `products_comments`
|
||||
|
||||
Przyklad:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://example.com/api.php" \
|
||||
-d "action=product_google_category_set" \
|
||||
-d "api_key=YOUR_API_KEY" \
|
||||
-d "client_id=12" \
|
||||
-d "offer_id=SKU-123" \
|
||||
-d "google_product_category=Apparel & Accessories > Shoes"
|
||||
```
|
||||
|
||||
Przyklad odpowiedzi:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "ok",
|
||||
"product_id": 987,
|
||||
"offer_id": "SKU-123",
|
||||
"client_id": 12,
|
||||
"google_product_category": "Apparel & Accessories > Shoes"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 Odczyt Google Product Category
|
||||
|
||||
- `action=product_google_category_get`
|
||||
- Cel: odczytuje `products.google_product_category`
|
||||
|
||||
Parametry:
|
||||
- `api_key` (string, wymagany)
|
||||
- `offer_id` (string, wymagany)
|
||||
- `client_id` (int, wymagany)
|
||||
|
||||
Przyklad:
|
||||
|
||||
```bash
|
||||
curl -G "https://example.com/api.php" \
|
||||
--data-urlencode "action=product_google_category_get" \
|
||||
--data-urlencode "api_key=YOUR_API_KEY" \
|
||||
--data-urlencode "client_id=12" \
|
||||
--data-urlencode "offer_id=SKU-123"
|
||||
```
|
||||
|
||||
Przyklad odpowiedzi:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "ok",
|
||||
"product_id": 987,
|
||||
"offer_id": "SKU-123",
|
||||
"client_id": 12,
|
||||
"google_product_category": "Apparel & Accessories > Shoes"
|
||||
}
|
||||
```
|
||||
|
||||
Jesli kategoria nie jest ustawiona:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "ok",
|
||||
"product_id": 987,
|
||||
"offer_id": "SKU-123",
|
||||
"client_id": 12,
|
||||
"google_product_category": null
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 Ustawienie custom_label_4 (istniejacy endpoint)
|
||||
|
||||
- `action=product_custom_label_4_set`
|
||||
- Cel: zapisuje `products.custom_label_4`
|
||||
|
||||
Parametry:
|
||||
- `api_key` (string, wymagany)
|
||||
- `offer_id` (string, wymagany)
|
||||
- `client_id` (int, wymagany)
|
||||
- `custom_label_4` (string, opcjonalny)
|
||||
|
||||
Przyklad:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://example.com/api.php" \
|
||||
-d "action=product_custom_label_4_set" \
|
||||
-d "api_key=YOUR_API_KEY" \
|
||||
-d "client_id=12" \
|
||||
-d "offer_id=SKU-123" \
|
||||
-d "custom_label_4=bestseller"
|
||||
```
|
||||
|
||||
Przyklad odpowiedzi:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Walidacja i bledy
|
||||
|
||||
### 5.1 Brak wymaganych parametrow
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "error",
|
||||
"message": "Missing required params: offer_id, client_id"
|
||||
}
|
||||
```
|
||||
|
||||
HTTP status: `422`
|
||||
|
||||
### 5.2 Nieznana akcja
|
||||
|
||||
`api.php` nie zwraca centralnego bledu dla nieznanej `action`.
|
||||
Przy integracji AI zawsze ustawiaj jawnie `action` i weryfikuj, czy odpowiedz to JSON.
|
||||
|
||||
## 6. Zalecany kontrakt dla agentow AI
|
||||
|
||||
- Zawsze wysylaj `api_key`, `action`, `client_id`, `offer_id`.
|
||||
- Po `product_title_set` od razu wywolaj `product_title_changed_check`.
|
||||
- Po `product_google_category_set` od razu wywolaj `product_google_category_get`.
|
||||
- Traktuj `null` jako brak wartosci.
|
||||
- Przy statusach `401`, `404`, `422` przerywaj workflow i zwracaj czytelny blad operatorowi.
|
||||
|
||||
## 7. Minimalny scenariusz end-to-end
|
||||
|
||||
1. Ustaw tytul (`product_title_set`)
|
||||
2. Potwierdz zmiane (`product_title_changed_check`)
|
||||
3. Ustaw kategorie (`product_google_category_set`)
|
||||
4. Odczytaj kategorie (`product_google_category_get`)
|
||||
|
||||
To daje prosty, deterministyczny przeplyw dla automatyzacji AI.
|
||||
Reference in New Issue
Block a user