feat(api): Introduce shopPRO API documentation and endpoints

- Added api-reference.json for API specifications including authentication, response formats, and available endpoints.
- Created index.html for public API documentation, dynamically loading endpoint details from api-reference.json.
- Removed htaccess.conf file and migrated routing logic to pp_routes for improved maintainability.
- Added new 'type' column in pp_routes to differentiate between entity and system routes.
This commit is contained in:
Jacek
2026-03-08 10:29:06 +01:00
parent a2073b48a8
commit 675963e931
10 changed files with 614 additions and 1401 deletions

292
api-docs/api-reference.json Normal file
View File

@@ -0,0 +1,292 @@
{
"name": "shopPRO API",
"version": "1.0.0",
"entrypoint": "/api.php",
"authentication": {
"type": "header",
"header": "X-Api-Key",
"required": true,
"description": "API key stored in pp_settings.param=api_key"
},
"response_format": {
"success": {
"status": "ok",
"data": {}
},
"error": {
"status": "error",
"code": "BAD_REQUEST",
"message": "Human-readable error message"
},
"error_codes": [
{ "code": "UNAUTHORIZED", "http": 401 },
{ "code": "BAD_REQUEST", "http": 400 },
{ "code": "NOT_FOUND", "http": 404 },
{ "code": "METHOD_NOT_ALLOWED", "http": 405 },
{ "code": "INTERNAL_ERROR", "http": 500 }
]
},
"endpoints": [
{
"group": "orders",
"action": "list",
"method": "GET",
"url_template": "/api.php?endpoint=orders&action=list",
"query_params": [
{ "name": "status", "type": "string", "required": false },
{ "name": "paid", "type": "string", "required": false },
{ "name": "date_from", "type": "string", "required": false, "format": "YYYY-MM-DD" },
{ "name": "date_to", "type": "string", "required": false, "format": "YYYY-MM-DD" },
{ "name": "updated_since", "type": "string", "required": false, "format": "YYYY-MM-DD HH:MM:SS" },
{ "name": "number", "type": "string", "required": false },
{ "name": "client", "type": "string", "required": false },
{ "name": "page", "type": "integer", "required": false, "default": 1, "min": 1 },
{ "name": "per_page", "type": "integer", "required": false, "default": 50, "min": 1, "max": 100 }
]
},
{
"group": "orders",
"action": "get",
"method": "GET",
"url_template": "/api.php?endpoint=orders&action=get&id={order_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
]
},
{
"group": "orders",
"action": "change_status",
"method": "PUT",
"url_template": "/api.php?endpoint=orders&action=change_status&id={order_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
],
"json_body": {
"required_fields": ["status_id"],
"fields": {
"status_id": { "type": "integer" },
"send_email": { "type": "boolean", "required": false }
}
}
},
{
"group": "orders",
"action": "set_paid",
"method": "PUT",
"url_template": "/api.php?endpoint=orders&action=set_paid&id={order_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
],
"json_body": {
"required_fields": [],
"fields": {
"send_email": { "type": "boolean", "required": false }
}
}
},
{
"group": "orders",
"action": "set_unpaid",
"method": "PUT",
"url_template": "/api.php?endpoint=orders&action=set_unpaid&id={order_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
]
},
{
"group": "products",
"action": "list",
"method": "GET",
"url_template": "/api.php?endpoint=products&action=list",
"query_params": [
{ "name": "search", "type": "string", "required": false },
{ "name": "status", "type": "string", "required": false },
{ "name": "promoted", "type": "string", "required": false },
{ "name": "attribute_{id}", "type": "integer", "required": false, "description": "e.g. attribute_5=12" },
{ "name": "sort", "type": "string", "required": false, "default": "id", "allowed": ["id", "name", "price_brutto", "status", "promoted", "quantity"] },
{ "name": "sort_dir", "type": "string", "required": false, "default": "DESC", "allowed": ["ASC", "DESC"] },
{ "name": "page", "type": "integer", "required": false, "default": 1, "min": 1 },
{ "name": "per_page", "type": "integer", "required": false, "default": 50, "min": 1, "max": 100 }
]
},
{
"group": "products",
"action": "get",
"method": "GET",
"url_template": "/api.php?endpoint=products&action=get&id={product_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
]
},
{
"group": "products",
"action": "create",
"method": "POST",
"url_template": "/api.php?endpoint=products&action=create",
"json_body": {
"required_fields": ["languages", "price_brutto"],
"rules": [
"languages must be an object with at least one language entry containing name",
"price_brutto must be numeric and >= 0"
]
}
},
{
"group": "products",
"action": "update",
"method": "PUT",
"url_template": "/api.php?endpoint=products&action=update&id={product_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
],
"json_body": {
"required_fields": [],
"rules": ["partial update; only changed fields are needed"]
}
},
{
"group": "products",
"action": "variants",
"method": "GET",
"url_template": "/api.php?endpoint=products&action=variants&id={product_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
]
},
{
"group": "products",
"action": "create_variant",
"method": "POST",
"url_template": "/api.php?endpoint=products&action=create_variant&id={product_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
],
"json_body": {
"required_fields": ["attributes"],
"fields": {
"attributes": { "type": "object", "description": "Map attribute_id -> value_id" }
}
}
},
{
"group": "products",
"action": "update_variant",
"method": "PUT",
"url_template": "/api.php?endpoint=products&action=update_variant&id={variant_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
],
"json_body": {
"required_fields": [],
"rules": ["partial update of variant fields"]
}
},
{
"group": "products",
"action": "delete_variant",
"method": "DELETE",
"url_template": "/api.php?endpoint=products&action=delete_variant&id={variant_id}",
"query_params": [
{ "name": "id", "type": "integer", "required": true, "min": 1 }
]
},
{
"group": "products",
"action": "upload_image",
"method": "POST",
"url_template": "/api.php?endpoint=products&action=upload_image",
"json_body": {
"required_fields": ["id", "file_name", "content_base64"],
"fields": {
"id": { "type": "integer", "description": "product id" },
"file_name": { "type": "string" },
"content_base64": { "type": "string", "description": "base64 payload" },
"alt": { "type": "string", "required": false },
"o": { "type": "integer", "required": false, "description": "image position" }
}
}
},
{
"group": "dictionaries",
"action": "statuses",
"method": "GET",
"url_template": "/api.php?endpoint=dictionaries&action=statuses"
},
{
"group": "dictionaries",
"action": "transports",
"method": "GET",
"url_template": "/api.php?endpoint=dictionaries&action=transports"
},
{
"group": "dictionaries",
"action": "payment_methods",
"method": "GET",
"url_template": "/api.php?endpoint=dictionaries&action=payment_methods"
},
{
"group": "dictionaries",
"action": "attributes",
"method": "GET",
"url_template": "/api.php?endpoint=dictionaries&action=attributes"
},
{
"group": "dictionaries",
"action": "ensure_attribute",
"method": "POST",
"url_template": "/api.php?endpoint=dictionaries&action=ensure_attribute",
"json_body": {
"required_fields": ["name"],
"fields": {
"name": { "type": "string" },
"type": { "type": "integer", "required": false, "default": 0 },
"lang": { "type": "string", "required": false, "default": "pl" }
}
}
},
{
"group": "dictionaries",
"action": "ensure_attribute_value",
"method": "POST",
"url_template": "/api.php?endpoint=dictionaries&action=ensure_attribute_value",
"json_body": {
"required_fields": ["attribute_id", "name"],
"fields": {
"attribute_id": { "type": "integer" },
"name": { "type": "string" },
"lang": { "type": "string", "required": false, "default": "pl" }
}
}
},
{
"group": "dictionaries",
"action": "ensure_producer",
"method": "POST",
"url_template": "/api.php?endpoint=dictionaries&action=ensure_producer",
"json_body": {
"required_fields": ["name"],
"fields": {
"name": { "type": "string" }
}
}
},
{
"group": "categories",
"action": "list",
"method": "GET",
"url_template": "/api.php?endpoint=categories&action=list"
}
],
"examples": {
"curl_list_products": "curl -X GET \"https://twoja-domena.pl/api.php?endpoint=products&action=list&page=1&per_page=20\" -H \"X-Api-Key: TWOJ_KLUCZ\"",
"curl_get_order": "curl -X GET \"https://twoja-domena.pl/api.php?endpoint=orders&action=get&id=42\" -H \"X-Api-Key: TWOJ_KLUCZ\"",
"curl_create_product": "curl -X POST \"https://twoja-domena.pl/api.php?endpoint=products&action=create\" -H \"X-Api-Key: TWOJ_KLUCZ\" -H \"Content-Type: application/json\" -d \"{\\\"price_brutto\\\":99.99,\\\"languages\\\":{\\\"pl\\\":{\\\"name\\\":\\\"Nowy produkt\\\"}}}\""
},
"source_of_truth": [
"autoload/api/ApiRouter.php",
"autoload/api/Controllers/OrdersApiController.php",
"autoload/api/Controllers/ProductsApiController.php",
"autoload/api/Controllers/DictionariesApiController.php",
"autoload/api/Controllers/CategoriesApiController.php"
]
}

60
api-docs/index.html Normal file
View File

@@ -0,0 +1,60 @@
<!doctype html>
<html lang="pl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>shopPRO API docs</title>
<style>
:root { color-scheme: light; }
body { font-family: Arial, sans-serif; margin: 24px; line-height: 1.4; }
h1, h2 { margin-bottom: 8px; }
.meta { color: #444; margin-bottom: 16px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; vertical-align: top; }
th { background: #f4f4f4; }
code { background: #f7f7f7; padding: 2px 4px; border-radius: 3px; }
</style>
</head>
<body>
<h1>shopPRO API - public docs</h1>
<div class="meta" id="meta">Ladowanie...</div>
<p>Machine-readable JSON: <a href="./api-reference.json">api-reference.json</a></p>
<h2>Endpointy</h2>
<table>
<thead>
<tr>
<th>Group</th>
<th>Action</th>
<th>Method</th>
<th>URL template</th>
</tr>
</thead>
<tbody id="rows"></tbody>
</table>
<script>
fetch("./api-reference.json")
.then(function (res) { return res.json(); })
.then(function (spec) {
var meta = document.getElementById("meta");
meta.textContent = spec.name + " v" + spec.version + " | entrypoint: " + spec.entrypoint;
var rows = document.getElementById("rows");
spec.endpoints.forEach(function (ep) {
var tr = document.createElement("tr");
tr.innerHTML =
"<td>" + ep.group + "</td>" +
"<td>" + ep.action + "</td>" +
"<td><code>" + ep.method + "</code></td>" +
"<td><code>" + ep.url_template + "</code></td>";
rows.appendChild(tr);
});
})
.catch(function () {
var meta = document.getElementById("meta");
meta.textContent = "Nie udalo sie wczytac api-reference.json";
});
</script>
</body>
</html>