API reference

REST API Specifikace v1

1319.png


Verze: 3.2 | Datum: 22. června 2026 | QR Payment podpora pro články (s režimy: autor/generální/žádný); Štítky API s paginací, filtrováním a kategoriemi; Koncový bod Tag Categories; ArticleResource rozšířený o blok s VS, text, představuje QR URL.qrpayment
dokument se seznamem koncových bodů a jejich metod
Specifikace API: seznam koncových bodů a toho, co která metoda dělá.

📋 Přehled

Tato dokumentace popisuje REST API pro práci s články, kategoriemi, tagy, uživateli, produkty, e-shop pouze pro čtení vrstva a parametry v CMS systému linkcms1. API je navrženo s důrazem na bezpečnost a multi-site podporu.

Základní URL: https://{domain}{BASE
PATH}/api/v1

Neversonovaný fakturační webhook

Vedle REST API v1 existuje jeden systémový webhook mimo /api/v1, který je výhradně pro integraci fakturace s Fakturoidem:

MetodaCestaÚčelAutentizace
POST/webhook/fakturoid/tariffSynchronizace stavu tarifních faktur ( paid, cancelled, sent, open) do billing domény webuX-FAKTUROID-WEBHOOK-SECRETnebo X-SignatureHMAC SHA-256

Webhook není určen pro veřejné klienty. Slouží k automatickému změně stavu , , a auditnímu zápisu do . sitetariffinvoicessitetarifforderssitetariffsubscriptionssitetarifffakturoidsynclog

Bezhlavá integrace domény API ( )apidomain

Pro weby obsluhované přes systémovou doménuweb-{site lze nastavit konfiguraci (v ). id}.{SYSTEMBASEDOMAIN}apidomainsiteconfigurations

  • Trigger: request na API endpoint ( /api/...) přijatý přes systémovou doménu.
  • Chování: backend v API response přepíše veřejné URL tak, aby směřovaly na místo systémové domény.apidomain
  • Rozsah přepisu: generované URL ( url, pagination links) a absolutní odkazy uvnitř HTML bodyu článků/produktů.
  • Běžné weby na vlastní doméně se tímto nastavením neovlivní.
Počet koncových bodů: 102 (10 auth + 5 článků + 3 kategorie + 3 tagy/tag-categories + 1 uživatelé + 5 produktů + 19 eshop + 1 parametry + 4 analytiky + 28 webbuilder + 2 formuláře + 2 verze + 19 sekce builder/pipeline) ⚠️ POZNÁMKA: Dokumentace2 níže, koncový bod popisů hlavních kategorií tagy, uživatel, produkty, eshop produkty, eshop košík, eshop kupóny, eshop pokladna/objednávky, eshop doprava/platby, eshop transakce/webhooky, eshop exporty, parametry). Plná dokumentace WebBuilder API (28 endpointů), Forms API (2 endpointy) a nové Section Builder API (19 endpointů) je v samostatných sekcích.

Hlavní funkce API

✅ Články (4 koncové body)

  • Vytváření článků s médii, kategoriemi, tagy a parametry
  • Získání detailu článku s autorem, kategoriemi a souvisejícím obsahem
  • Seznam článků s filtrováním podle kategorie, statusu, dat
  • Fulltextové vyhledávání

✅ Kategorie (3 koncové body)

  • Hierarchický strom všech kategorií
  • Načtení podkategorií konkrétní kategorie
  • Detail kategorie s články a navigací

✅ Tagy a kategorie tagů (3 koncové body)

  • Seznam všech značek s počtem článků + filtrování podle kategorií
  • Detailní tag
  • Seznam kategorií tagů (nové)

✅ Uživatelé/Autoři (1 koncový bod)

  • Kompletní profil autora včetně bio, sociálních sítí, článků a statistik

✅ Produkty (5 koncových bodů - ČTĚTE A ZÁPIS)

  • Seznam produktů s filtrováním podle kategorií, parametrů a produktových polí
  • Detail produktu s kompletními informacemi včetně dynamických polí
  • Vytvoření, aktualizace a soft-delete produktu přes API
  • Flexibilní filtrování pomocí operátorů (eq, gte, lte, like)

✅ E-shop (19 koncových bodů)

  • Seznam prodejných produktů z existujícího katalogu
  • Detail prodávaného produktu včetně cen a inventárního bloku
  • Přehled varianty produktu seskupených do variantní rodiny
  • Dostupnost produktu včetně rezervovaného množství a stavu objednávek
  • API košíku (načtení košíku, přidání položky, změna množství, odebrání položky)
  • API kupónů (aplikace kupónu na košík, odebrání kupónu)
  • API pokladna a objednávek ( POST /eshop/checkout, GET /eshop/orders/{id})
  • API doprav a plateb ( GET /eshop/shipping-methods, GET /eshop/payment-methods)
  • API transakce ( GET/POST /eshop/orders/{id}/transactions, GET /eshop/transactions/{id})
  • API webhooků platebních bran ( POST /eshop/webhooks/{provider}) — veřejný koncový bod
  • API exportů ( POST /eshop/export) pro ruční spuštění exportního běhu
  • Automatická rezervace skladu při práci s košíkem
  • Spotřeba rezervace při pokladně a převodu košíku do stavuconverted
  • Automatický přechod objednávky do stavu paidzachycení plateb
  • E-shop vrstev respektujeproduct atypes.config.iseshopenabledsiteid

✅ Parametrie (1 koncový bod)

  • Definice vlastních parametrů pro různé typy obsahu
  • Podporuje 14 datových typů (text, celé číslo, soubor, obrázek, url atd.)

✅ Analýza obsahu (4 koncové body)

  • Konfigurace na úrovni webu GA4/GSC vsiteconfigurations (nikoliv sites.configurations)
  • Statistiky zobrazení konkrétních stránek/článku/produktu za období i za celou dobu
  • Rozpad zobrazení podle kanálů ( sessionDefaultChannelGroup)
  • Vstupní klíčová slova stránek z Google Search Console

Bezpečnostní mechanismy

🔒 Tokenová autentizace s vazbou na 🔒 Automatická filtrace dat podle webu 🔒 Omezení sazby (1000/h čtení, 100/h zápis) 🔒 Pouze HTTPS 🔒 Kompletní protokol auditu všech požadavků 🔒 Oprávnění na úrovni tokenu (rozsahy) siteid


🔐 Autentizace

Všechny koncové body API vyžadují autentizaci pomocí tokenu API . Token je vázán na konkrétnísiteid a umožňuje přístup pouze k datům daného webu.

Autentizační metoda

Žeton na doručitele (doporučeno)Authorization: Bearer {yourtoken}

Struktura API tokenu

API token je uložen v databázové tabulceapitokens :

PólTypPopis
idINTID tokenu
useridINTID uživatele
siteidINTID webu, ke kterému token patří
tokenhashVARCHAR(64)SHA-256 hash tokenu (plain token se ukládá pouze při vytvoření)
nameVARCHAR(255)Popis tokenu
scopesJSONOprávnění (např. articles:read, products:write, )
expiresatDATUM A ČASDatum vypršení (NULL = neexpiruje). Ukládá se a vyhodnocuje v UTC .
lastusedatDATUM A ČASPoslední použití
createdatČASOVÉ RAZÍTKODatum vytvoření
updatedatČASOVÉ RAZÍTKOAktualizace data

Rozsahový systém a hierarchické oprávnění

API používá hierarchický rozsah systému ve formátu {resource}:{action}:

Rozsah zástupných znaků (plný přístup):
  • (ADMIN ALL) - plný přístup ke všem API endpointům
Rozsahy založené na zdrojích:
  • articles:write- vytváření, editace, mazání článků (implicitně zahrnuje articles:read)
  • articles:read- pouze čtení článků
  • products:write- vytváření, editace, mazání produktů (implicitně zahrnuje products:read)
  • products:read- pouze čtení produktů
  • eshop:write- správa e-shopu provozní vrstvy (implicitně zahrnuje eshop:read)
  • eshop:read- čtení e-shop koncových bodů pouze pro čtení ( /api/v1/eshop/...)
  • categories:write- správa kategorií (implicitně zahrnuje categories:read)
  • categories:read- čtení kategorií
  • tags:write- správa tagů (implicitně zahrnuje tags:read)
  • tags:read- čtení tagů
  • users:write- správa uživatelů (implicitně zahrnuje users:read)
  • users:read- čtení uživatelů
  • parameters:write- správa parametrů (implicitně zahrnuje parameters:read)
  • parameters:read- čtení parametrů
  • forms:write- správa formulářů (implicitně zahrnuje forms:read)
  • forms:read- čtení formulátorů
  • messages:write- správa zpráv z formulářů (implicitně zahrnuje messages:read)
  • messages:read- čtení zpráv z formulářů
  • sections:write- správa katalogu sekcí a stránek stavitele (implicitně zahrnuje sections:read)
  • sections:read- čtení katalogu sekcí, tvůrce stránek, náhledy a verze
  • section-pipeline:write- vytváření a správa AI pipeline jobů (implicitně zahrnuje section-pipeline:read)
  • section-pipeline:read- čtení AI pipeline jobů a výsledků
Hierarchie:
  • (ADMIN ALL) má přístup ke všem endpointům
  • {resource}:writemá přístup k zápisu operacím + automaticky ik čtení operacím
  • {resource}:readmá přístup pouze k read operacím
  • Příklad: Token se scope articles:writemá přístup k:
    • POST /api/v1/articles (vytvoření článku)
    • PUT /api/v1/articles/{id} (editace článku)
    • DELETE /api/v1/articles/{id} (smazání článku)
    • GET /api/v1/articles (čtení článků) - automaticky zahrnuto
    • GET /api/v1/articles/{id} (detail článku) - automaticky zahrnuto

    Generování tokenu

    Token se vrací pouze při vytvoření (plain token). V DB se ukládá pouze . tokenhash

    Timezone pravidlo pro expiraci tokenů

    • expiresatje interně vyhodnocováno v UTC .
    • Pokud token vytváříte mimo API (seed/import), ukládejte jako UTC datum/čas.expiresat
    • API odpověď vrací ISO 8601 datum s timezone offsetem; při porovnávání vždy porovnávejte jako časový okamžik (ne jako lokální string bez timezone).

    Ověření tokenu (middleware)

    // Pseudokód pro middleware

    function verifyApiToken($token) { $tokenHash = hash('sha256', $token); $utcNow = gmdate('Y-m-d H:i:s'); $tokenRecord = ApiToken::where('tokenhash', $tokenHash) ->where(function($query) { $query->whereNull('expiresat') ->orWhere('expiresat', '>', $utcNow); }) ->first();

    if (!$tokenRecord) { return ['error' => 'Invalid or expired token', 'code' => 401]; }

    // Aktualizace lastusedat $tokenRecord->lastusedat = $utcNow; $tokenRecord->save();

    // Nastavení globálního siteid pro další requesty $SERVER['APISITEID'] = $tokenRecord->siteid;

    return ['success' => true, 'siteid' => $tokenRecord->siteid]; }

    Koncový bod autorizace (v1)

    POST /api/v1/auth/register

    POST /api/v1/auth/password/forgot POST /api/v1/auth/password/reset POST /api/v1/auth/login GET /api/v1/auth/me PUT /api/v1/auth/me PUT /api/v1/auth/me/password POST /api/v1/auth/logout GET /api/v1/auth/tokens DELETE /api/v1/auth/tokens/{id}

    POST /api/v1/auth/registervytváří nový účet a spouští ověřovací e-mail. POST /api/v1/auth/password/forgotpošlete reset hesla na pevně daný e-mailový účet. POST /api/v1/auth/password/resetnastavím nové heslo přes jednorázový klíč. GET /api/v1/auth/mevrací aktuální autentizační kontext včetně uživatele, rolí, rozsahu a metadat tokenu. PUT /api/v1/auth/meupravuje vlastní profil a PUT /api/v1/auth/me/passwordmění vlastní heslo.

    Poznámka: správa tokenů je dostupná také v administraci (AJAX) na URL /admin/api.

    Chybové odpovědi pro autentizaci

    {

    "error": "Unauthorized", "message": "Invalid or missing API token", "code": 401 }

    {

    "error": "Forbidden", "message": "Token does not have required permissions", "code": 403 }


    📈 API pro analýzu obsahu (GA4 + GSC)

    Endpointy slouží pro analytiku konkrétních stránek (nebo konkrétního článku/produktu přes mapování do urls).

    Konfigurace je výhradně per-site v tabulkáchconfigurationsdefinitions + . siteconfigurations

    Rozsah požadavků

    • analytics:read- čtení konfigurace a statistiky
    • analytics:write- změna konfigurace

    Hierarchie platí standardně: analytics:writezahrnuje analytics:read, zahrnuje vše.

    Konfigurační klíče ( konfigurace stránek)

    • analyticsga4enabled( 0|1)
    • analyticsga4propertyid(např. 123456789)
    • analyticsgscenabled( 0|1)
    • analyticsgscsiteurl(např. https://example.com/)
    • analyticsgoogleserviceaccountjson(JSON service accountu s přístupem pouze pro čtení)

    Koncový bod

    GET /api/v1/admin/content-analytics/config

    PUT /api/v1/admin/content-analytics/config GET /api/v1/admin/content-analytics/page

    GET /api/v1/admin/content-analytics/pagepodporuje tři způsoby cílených stránek:
    • url=/konkretni-strankanebo absolutní URL
    • articleid={id}
    • productid={id}

    Volitelné datumové parametry:

    • from=YYYY-MM-DD(výchozí posledních 30 dní)
    • to=YYYY-MM-DD(výchozí dnešek)

    Odpověď obsahuje:

    • views.period.total+views.period.bychannel[]
    • views.alltime.total+views.alltime.bychannel[]
    • keywords.items[](dotaz, kliky, imprese, CTR, pozice)

    🧱 API pro tvorbu sekcí

    Section Builder je nový kanonický subsystém pro správu katalogu sekcí, builder stránek, publikování workflow, verzování a AI pipeline HTML → sekce. Tato vrstva je navržena jako stránka JSON + katalog sekcí, nikoli jako volný HTML editor.

    Datový model

    • sectiondefinitions- katalog dostupných sekcí
    • sectiondefinitionversions- verze schémat a render konfigurační sekcí
    • sectionpages- stavitel stránek svázané seurls
    • sectioninstances- konkrétní instance sekcí v regionech stránky
    • sectionpageversions- publikovat snapshoty s historií vrácení změn
    • sectionpipelinejobs- asynchronní AI pipeline job

    Rozsah požadavků

    • sections:read- čtení definic, stránek, náhledů a verzí
    • sections:write- CRUD definic, CRUD stránky, publikování, restaurování, správa instancí
    • section-pipeline:read- čtení pipeline jobs
    • section-pipeline:write- zakládání projektů potrubí

    Výčet stavů

    • development
    • active
    • suspend

    Regionální stránky

    Základní regiony první implementace:

    • header
    • main
    • sidebar
    • footer

    1. Seznam definic sekcí

    0. Sekce katalogu

    GET /api/v1/sections/catalog

    Parametry dotazu:

    • category- filtr podle kategorie sekce
    • search- plný text přes name,keyname ,description
    • includedevelopment- 1|0, výchozí 1( 0vrací se jen active)

    Response vrací normalizovaný katalog s aktuálními verzemi sekce (currentversion ) pro admin knihovnu sekcí.

    GET /api/v1/sections/definitions

    Parametry dotazu:

    • status- filtr podle statusu
    • category- filtr podle kategorie
    • includeglobal- 1|0, výchozí1

    2. Vytvoření definice sekce

    POST /api/v1/sections/definitions

    Tělo požadavku:

    {

    "keyname": "herosplit", "name": "Hero Split", "category": "content", "description": "Hero sekce s obrázkem a CTA", "status": "development", "isglobal": false, "schema": { "fields": [ {"key": "title", "type": "text", "required": true}, {"key": "body", "type": "richtext", "required": false} ] }, "uischema": { "groups": [ {"key": "content", "label": "Obsah"} ] }, "validationrules": { "requiredregions": [] }, "bindings": [ "displayData.title" ], "renderconfig": { "partial": "sections/hero-split.twig" } }

    Pravidla pro : pageprofile

    • povolené hodnoty:contentpage , , , , , ,articledetailproductdetailhomepagesectionbuilder
    • při nepovolené hodnotě API vrací 400 Validation Failed.

    3. Podrobná definice sekce

    GET /api/v1/sections/definitions/{id}

    4. Aktualizace definice sekce

    PUT /api/v1/sections/definitions/{id}

    Aktualizace metadat definice bez vytvoření nové verze schématu.

    5. Nová verze definice sekce

    POST /api/v1/sections/definitions/{id}/versions

    Vytvoří nový snímek schématu a nastaví ho jako current. versionid

    6. Seznam tvůrců stránek

    6a. Profily stránek s metadaty

    GET /api/v1/sections/page-profiles

    Vrací editor metadat pro:

    • key- identifikátor profilu,
    • label- uživatelský název,
    • allowedregions- region povoleny pro profil,
    • recommendedsections- doporučené sekce key pro profil.

    Endpoint je určen primárně pro admin editor (výběry profilu/regionu + UI guide).

    GET /api/v1/sections/pages

    Parametry dotazu:

    • status
    • pageprofile
    • search

    7. Vytvoření stránek builderu

    POST /api/v1/sections/pages

    Tělo požadavku:

    {

    "title": "O nás", "slug": "o-nas", "pageprofile": "contentpage", "layoutkey": "default", "status": "development", "urlbindingmode": "newurl", "metadata": { "metatitle": "O nás", "metadescription": "Představení společnosti" }, "urlbinding": { "path": "/o-nas" } }

    urlbindingmodepodporuje 3 varianty:
    • generic- typová/generická stránka bez veřejné URL vazby,urlid zůstane null,
    • newurl- backend vytvoří nový záznam v urlsa naváže ho na stránku,
    • existingurl- backend naváže stránku na existující URL vybranou přes vyhledávací koncový bod.

    Pravidla:

    • generic: nevyžaduje ,urlbinding
    • newurlvyžaduji ,urlbinding.path
    • existingurlvyžaduji ,urlbinding.urlid
    • běžný klient nemá používat urlid přímo jako top-level vstup,
    • pro newurl a existingurl backend interně nastavuje Section Page routing do tabulky urls.

    Response obsahuje vedle urlid i čitelné metadata vazby:

    • urlbindingmode,
    • urlbinding.isgeneric,
    • urlbinding.url (id, domain, path, handler, model, modelid, label).

    Pravidla pro regionkey:

    • contentpage, articledetail, productdetail, sectionbuilder: header, main, sidebar, footer,
    • homepage: header, main, footer,
    • nepovolený region vrací 400 Validation Failed.

    8. Vyhledání existujících URL pro builder

    GET /api/v1/sections/pages/url-search?q=o-nas

    Query parametry:

    • q - fulltext prefix/substring pro URL cestu,
    • limit - volitelný limit, default 10, max 25.

    Response:

    [

    { "id": 123, "domain": "loc-dom9.cz", "path": "/o-nas", "handler": "getArticleDetails", "model": "articles", "modelid": 456, "label": "loc-dom9.cz/o-nas" } ]

    9. Detail builder stránky

    GET /api/v1/sections/pages/{id}

    Vrací stránku včetně instancí seskupených do regionů.

    10. Aktualizace builder stránky

    PUT /api/v1/sections/pages/{id}

    Podporuje optimistic locking pro autosave. Klient může poslat:

    • expectedupdatedat (ISO 8601)
    • expectedstatehash (SHA-256 hash posledního načteného stavu stránky)

    Aktualizace podporuje stejný kontrakt urlbindingmode + urlbinding jako create.

    Při konfliktu server vrací 409 Edit Conflict.

    11. Přidání instance sekce do stránky

    POST /api/v1/sections/pages/{id}/instances

    Request body:

    {

    "sectiondefinitionid": 15, "sectiondefinitionversionid": 42, "regionkey": "main", "sortorder": 10, "instancekey": "hero01", "mode": "static", "status": "development", "props": { "title": "Vítejte", "body": "Obsah sekce" }, "bindings": {}, "meta": { "cssclass": "hero hero--large" } }

    Klíče objektu regions musí používat pouze regiony povolené pro pageprofile dané stránky.

    Validační chování:

    • backend validuje props proti schema.fields a validationrules vybrané sectiondefinitionversion,
    • navíc aplikuje DB pravidla z sectionvalidationrules (scope site/definition/version, level error nebo warning),
    • při chybě vrací 400 Validation Failed s polem errors,
    • warningy neblokují zápis a vrací se v meta.validationwarnings,
    • neznámá pole mimo schema může vrátit v poli warnings.

    11. Aktualizace instance sekce

    PUT /api/v1/sections/pages/{id}/instances/{instanceId}

    Při update se znovu aplikuje validace proti navázané sectiondefinitionversionid instance.

    Podporuje optimistic locking pro autosave. Klient může poslat:

    • expectedupdatedat (ISO 8601)
    • expectedstatehash (SHA-256 hash posledního načteného stavu instance)

    Při konfliktu server vrací 409 Edit Conflict.

    12. Reorder instancí na stránce

    POST /api/v1/sections/pages/{id}/instances/reorder

    Request body:

    {

    "regions": { "header": [1], "main": [2, 3, 4], "sidebar": [], "footer": [5] } }

    13. Publish stránky

    POST /api/v1/sections/pages/{id}/publish

    Vytvoří snapshot do sectionpageversions, aktualizuje publishedjson a currentversionid.

    Publish je blokován, pokud některá instance na stránce neprojde schema/validation kontrolou. V takovém případě API vrátí 400 Validation Failed s detailním rozpisem chyb po instancích a volitelným polem warnings.

    14. Obnova verze stránky

    POST /api/v1/sections/pages/{id}/restore/{versionId}

    Obnoví snapshot jako nový draft a znovu zkonstruuje sectioninstances.

    15. Seznam verzí stránky

    GET /api/v1/sections/pages/{id}/versions

    16. Preview stránky

    GET /api/v1/sections/pages/{id}/preview

    Vrací runtime payload pro builder preview. První implementace vrací JSON payload, ne renderovaný HTML dokument.

    17. Seznam AI pipeline jobů

    GET /api/v1/sections/pipeline/jobs

    Query parametry:

    • status - filtr podle stavu (queued, processing, completed, failed, cancelled)
    • sectionpageid - filtr podle builder stránky
    • limit - počet vrácených položek (1-200, default 50)

    Runtime metadata workeru mohou být vrácena v result.runtime:

    • attemptcount - počet zpracovaných pokusů,
    • nextattemptat - ISO timestamp dalšího automatického retry,
    • lasterror - poslední chyba workeru,
    • lastfailedat - čas posledního selhání,
    • detailedsteps - detailní stav pipeline kroků (cleaninghtml, parsingdom, splittingblocks, detectingsections, classifyingsections, mappingprops, generatingjson, validatingjson, generatingtwig, rendering, completed|failed|retrywait),
    • aiprovider - použitý provider klasifikace (heuristic nebo fireworks).
    • aimodel - konkrétní model použitý pro klasifikaci (pokud provider=fireworks).
    • aiattempts - seznam pokusů/modelů při policy routingu (primary/escalation/fallback).

    18. Vytvoření AI pipeline jobu

    POST /api/v1/sections/pipeline/jobs

    Request body:

    {

    "sectionpageid": 77, "sourcetype": "html", "sourcepayload": { "html": "..." } }

    Dedup chování:

    • backend vypočítá deterministický hash vstupu (siteid, sectionpageid, sourcetype, canonical sourcepayload),
    • pokud už existuje job se stejným hashem ve stavu queued nebo processing, nový job se nevytváří,
    • endpoint vrátí existující job s HTTP 200 a meta.deduphit=true + meta.reusedjobid,
    • při standardním vytvoření vrací HTTP 201.

    19. Detail AI pipeline jobu

    GET /api/v1/sections/pipeline/jobs/{id}

    Vrací stav jobu, current step, confidence score, result payload a error message.

    Součástí response může být:

    • detailedsteps - flattened výpis kroků pipeline (pro UI observability),
    • runtime - kompletní runtime metadata workeru.
    • suggestedsections[].meta.validationwarnings - seznam logických/UX warningů pro sekci,
    • suggestedsections[].meta.validationerrors - seznam validačních chyb sekce,
    • suggestedsections[].meta.validationskipped=true - sekce byla vynechána pro render/apply fázi.

    20. Retry AI pipeline jobu

    POST /api/v1/sections/pipeline/jobs/{id}/retry

    Vrátí existující job zpět do stavu queued a vyčistí errormessage, startedat, finishedat.

    Pokud job používá retry/backoff runtime metadata, resetuje i result.runtime.nextattemptat a result.runtime.lasterror.

    21. Aplikace výsledku pipeline do stránky

    POST /api/v1/sections/pipeline/jobs/{id}/apply

    Pouze pro completed job navázaný na sectionpageid.

    Request body (volitelný):

    {

    "mode": "replace", "dryrun": false }

    • mode: replace (default) nebo append
    • dryrun: pokud true, endpoint vrátí plán změn bez zápisu do DB

    Co endpoint provede:

    • načte resultjson.suggestedsections
    • přemapuje sectionkey na sectiondefinitions.keyname (site nebo global)
    • každou navrženou sekci znovu validuje proti cílové sectiondefinitionversion
    • vytvoří nové sectioninstances do draftu stránky
    • přepočítá draftjson

    Response obsahuje applyresult (createdinstances, skippedinstances, warnings, plannedpreview). Nevalidní sekce se přeskočí a vrátí jako warning.


    📝 1. Přidání článku

    Vytvoří nový článek v systému.

    Endpoint

    POST /api/v1/articles

    Oprávnění

    • Token musí mít scope: articles:write

    Request Headers

    Content-Type: application/json

    Authorization: Bearer YOURAPITOKEN

    Request Body

    {

    "title": "Název článku", "subtitle": "Podtitul článku (volitelné)", "shorttext": "Krátký úvodní text", "snippet": "Snippet pro seznam článků (volitelné)", "body": "

    HTML obsah článku

    ", "status": "active", "categoryid": 15, "categoryids": [15, 23, 45], "tagids": [7, 9, 11], "meta": { "title": "SEO titulek", "description": "SEO popis pro meta description", "keywords": "klíčová, slova, api", "autorname": "Jan Novák", "zdroj": "" }, "images": { "articleDetail": { "url": "https://example.com/images/banner.jpg" }, "articles": { "url": "https://example.com/images/list-thumbnail.jpg" }, "gallery": [ { "url": "https://{domain}/images/gallery-1.jpg" }, { "url": "https://{domain}/images/gallery-2.jpg" }, { "url": "https://{domain}/images/gallery-3.jpg" } ] }, "parameters": { "1": "středně pokročilý", "2": 15, "3": "https://youtube.com/watch?v=xyz123", "4": "https://maps.google.com/...", "5": 456, "6": 789 }, "parametervalues": { "1": "legacy kompatibilní klíč - fallback pokud neposíláte parameters" } }

    Popis polí

    PoleTypPovinnéPopis
    titlestring✅Název článku
    subtitlestring❌Podtitul
    shorttextstring❌Krátký úvodní text
    snippetstring❌Snippet pro seznam
    bodystring✅HTML obsah článku
    statusenum❌development, active, schedule, suspend. Pokud chybí, uloží se development. Status schedule automaticky přiřadí publishat na nejbližší volný publikační slot (pokud je publishat prázdné). Pokud žádný slot není k dispozici, status se uloží jako development.
    categoryidint❌Hlavní kategorie článku. Podmíněně povinné: Alespoň jedno z categoryid nebo categoryids musí být zadáno.
    categoryidsarray[int]❌Alternativní pole ID kategorií. Fallback logika: Pokud categoryid chybí, vezme se categoryids[0] jako hlavní kategorie.
    tagidsarray[int]❌Pole existujících ID tagů. Create endpoint aktuálně neumí přijmout názvy tagů v poli tags.
    metaobject❌Metadata článku
    imagesobject❌Obrázky - podporuje URL stahování nebo fileid již nahraných souborů
    images.articleDetailobject❌Hlavní obrázek článku (banner, hero image)
    images.articleDetail.urlstring❌URL adresa obrázku ke stažení (alternativa k fileid)
    images.articleDetail.fileidinteger❌ID již nahraného souboru (preferováno před URL)
    images.articlesobject❌Náhledový obrázek pro výpisy článků (list image)
    images.articles.urlstring❌URL adresa obrázku ke stažení (alternativa k fileid)
    images.articles.fileidinteger❌ID již nahraného souboru (preferováno před URL)
    images.galleryarray[object]❌Pole objektů s URL adresami nebo fileid fotogalerie
    images.gallery[].urlstring❌URL adresa obrázku ke stažení (alternativa k fileid)
    images.gallery[].fileidinteger❌ID již nahraného souboru (preferováno před URL)
    parametersobject❌Hodnoty parametrů (klíč = parameterid, hodnota podle datatype)
    parametervaluesobject❌Legacy alias pro parameters (pro zpětnou kompatibilitu). Pokud jsou poslány oba, použije se parameters.

    Poznámky k polím

    Automaticky generovaná pole (není třeba posílat v requestu):
    • id - AUTOINCREMENT
    • siteid - automaticky z API tokenu (viz autentizace)
    • createdat - automaticky CURRENTTIMESTAMP
    • updatedat - automaticky ON UPDATE CURRENTTIMESTAMP
    • deletedat - používá se pro soft delete (null = není smazáno)
    • meta.mainCatid - backend ho doplní podle hlavní kategorie
    Status hodnoty:
    • development - Článek je v režimu vývoje (viditelný pouze pro administrátory)
    • active - Článek je aktivní a veřejně dostupný
    • suspend - Článek je pozastaven (není veřejně přístupný)
    Poznámka k deleted: Soft delete se provádí nastavením deleted
    at timestamp, nikoli změnou statusu. Pro smazání článku použijte DELETE endpoint. Body vs Snippet:
    • body je při POST /api/v1/articles povinné
    • snippet je volitelné doplňkové pole pro výpisy
    Pole, která create endpoint aktuálně ignoruje:
    • url - create controller vždy generuje URL automaticky z URL hlavní kategorie a slugu z title
    • userid - autor se bere z tokenu (APIUSERID)
    • publishat, publishendat, manualupdateat - POST create je aktuálně nepersistuje
    • files - create controller zatím neukládá přílohy článku
    • relatedcontent - create controller zatím nevytváří související vazby
    • tags - pole názvů tagů se při create nevyhodnocuje; použijte tagids
    • categories - starší pole categories se při create nevyhodnocuje; použijte categoryid a/nebo categoryids
    • navigationid - create endpoint ho nepřijímá; pokud potřebujete vybírat kategorie jen z jedné navigace, nejdřív použijte GET /api/v1/categories/tree?navigationid={id} a z výsledku vyberte categoryid/categoryids
    Parametry (parameters / parametervalues):
    • API přijímá primárně parameters (objekt parameterid => hodnota).
    • Pro zpětnou kompatibilitu přijímá i parametervalues.
    • Pokud pošlete oba klíče, prioritu má parameters.
    • Při PUT /api/v1/articles/{id} hodnota null nebo prázdný string ("") vymaže existující hodnotu parametru.
    • Nenumerické klíče parameterid jsou ignorovány a request pokračuje.

    Logika generování URL a Slugu

    Create endpoint aktuálně nepoužívá klientem zaslané pole url. URL se vždy vygeneruje automaticky:

  • Z title se vytvoří slug (odstranění diakritiky, převod na malá písmena, mezery na pomlčky).
  • Z databáze se načte URL hlavní kategorie (categoryid nebo fallback categoryids[0]). Lookup je tolerantní k doméně — zkouší postupně aktuální doménu (SITEDOMAIN), původní interní doménu (SITEDOMAINORIGINAL, relevantní u headless webů s apidomain), wildcard a nakonec záznam bez ohledu na doménu (kategorie je již ověřena na siteid).
  • Finální URL vznikne jako {urlhlavnikategorie}/{slug}.html.
  • Pokud URL koliduje, backend přidá náhodný 3znakový suffix před příponu ({slug}-abc.html).
  • Výsledná URL se uloží do tabulky urls vždy pod interní doménou webu (doména URL záznamu hlavní kategorie, fallback SITEDOMAINORIGINAL). U headless webů se veřejná doména (apidomain) používá pouze pro generované absolutní URL v API odpovědích, nikoli pro záznamy v urls.
  • Zpracování obrázků a souborů

    API podporuje automatické stahování obrázků z URL adres (doporučená metoda).

    Stahování obrázků z URL

    Podporované klíče pro obrázky

    API očekává následující klíče v objektu images:

    KlíčÚčelTyp variant
    articleDetailHlavní obrázek článku (banner, detail, hero image)articleDetail
    articlesNáhledový obrázek pro seznamy článkůarticles
    galleryFotogalerie (pole obrázků)gallery
    ⚠️ DŮLEŽITÉ: Toto jsou JEDINÉ klíče, které kód skutečně zpracovává! Nekompat ibilní klíče (zkontrolováno 15.2.2026): Klíče jako bannerimage, listimage, galleryimages NEJSOU implementovány v ArticleController.php a nebudou fungovat!
    Příklad kompletního requestu s obrázky:
    {

    "title": "Testovací článek", "body": "

    Obsah

    ", "status": "active", "categoryid": 15, "images": { "articleDetail": { "url": "https://example.com/images/hero.jpg", "alt": "Hlavní obrázek", "title": "Hero image článku" }, "articles": { "url": "https://example.com/images/thumbnail.jpg", "alt": "Náhled", "title": "Thumbnail pro seznam" }, "gallery": [ { "url": "https://example.com/gallery-1.jpg" }, { "url": "https://example.com/gallery-2.jpg" } ] } }

    Implementace v kódu

    Viz www/api/v1/controllers/ArticleController.php metoda store():

    // Banner image (hlavní obrázek) - klíč: articleDetail

    $bannerData = isset($data['images']['articleDetail']) ? $data['images']['articleDetail'] : null; if ($bannerData && isset($bannerData['url'])) { $result = UploadedFile::downloadAndProcessFromUrl($bannerData['url']); $article->assignImage($result['id'], 'articleDetail'); }

    // List image (náhledový obrázek) - klíč: articles $listData = isset($data['images']['articles']) ? $data['images']['articles'] : null; if ($listData && isset($listData['url'])) { $result = UploadedFile::downloadAndProcessFromUrl($listData['url']); $article->assignImage($result['id'], 'articles'); }

    // Gallery - klíč: gallery (pole) if (isset($data['images']['gallery']) && isarray($data['images']['gallery'])) { foreach ($data['images']['gallery'] as $galleryImage) { if (isset($galleryImage['url'])) { $result = UploadedFile::downloadAndProcessFromUrl($galleryImage['url']); $article->assignImage($result['id'], 'gallery'); } } }

    Typy obrázků

    API rozlišuje 3 typy obrázků, každý s vlastní variantou uložení:

    articleDetail (hlavní obrázek článku):
    • Varianta: articleDetail
    • Použití: Detail článku, náhled při sdílení na sociálních sítích, hlavní vizuál, hero image
    • Volitelné: Ano
    • Příklad použití: Banner/hero image na detailu článku
    articles (náhledový obrázek):
    • Varianta: articles
    • Použití: Seznam článků, sidebar, související články, karty článků
    • Volitelné: Ano
    • Příklad použití: Thumbnail v seznamu článků
    gallery (fotogalerie):
    • Varianta: gallery
    • Použití: Fotogalerie v těle článku, lightbox
    • Volitelné: Ano (pole může obsahovat 0-N obrázků)
    • Příklad použití: Fotogalerie produktu, obrazová příloha
    Technické specifikace
    Limity stahování:
    • Časový limit: 30 sekund na stažení jednoho obrázku
    • Velikost souboru: Maximum 15 MB (15,728,640 bytů)
    • Podporované formáty: JPEG, PNG, WebP (validace po stažení)
    • User-Agent: MiniWeb-CMS/1.0
    • SSL verifikace: Zapnuto (CURLOPTSSLVERIFYPEER=true)
    • Follow redirects: Zapnuto (CURLOPTFOLLOWLOCATION=true)
    Pipeline zpracování:
  • Stažení z URL pomocí cURL
  • Validace MIME typu (image/jpeg, image/png, image/webp)
  • Konverze do WebP formátu (pokud není už WebP)
  • Komprese přes TinyPNG API (snížení velikosti)
  • Nahrání do Google Cloud Storage
  • Vytvoření nového záznamu v tabulce uploadedfiles → získání nového ID (např. 1450)
  • Automatické vygenerování 17 velikostních variant do tabulky imagevariants
  • Propojení s článkem pomocí nově vytvořeného ID (articleimages pivot tabulka)
  • ⚠️ DŮLEŽITÉ: Při použití URL metody se vždy vytvoří nový soubor s novým ID v databázi. I pokud stáhnete stejnou URL vícekrát, vznikne pokaždé nový záznam. Generované varianty (příklady rozměrů):
    • banner: 1500×635 px
    • articleDetail: 800×600 px
    • articles: 400×300 px
    • galleryThumbnail: 300×200 px
    • thumbnail: 150×150 px
    • ... a další (celkem 17 variant)
    Zpracování chyb

    API implementuje continue-on-failure strategii:

    • Pokud stažení jednoho obrázku selže, zpracování pokračuje s dalšími
    • Podporuje partial success (část obrázků úspěšná, část s chybou)
    • Každý obrázek má vlastní try-catch blok
    • Detailní informace v poli imagesprocessed v odpovědi
    Možné chybové stavy:
    ChybaPříčinaChybová zpráva (česky)
    TimeoutStahování trvalo > 30s"Časový limit stahování překročen (30s)"
    File too largeSoubor > 15MB"Soubor je příliš velký (limit 15MB)"
    DNS failureNeplatná doména"Neplatná doména nebo DNS selhání"
    Invalid MIMENení JPEG/PNG/WebP"Nepodporovaný typ obrázku: {mime}"
    HTTP errorHTTP status ≠ 200"HTTP chyba {code}"
    Empty contentPrázdný response"Stažený obsah je prázdný"
    Download failedObecná cURL chyba"Stahování selhalo: {error}"
    Příklad requestu s obrázky
    {

    "title": "Testovací článek s obrázky", "body": "

    Obsah článku

    ", "categoryid": 15, "status": "active", "images": { "articleDetail": { "url": "https://example.com/images/main-banner.jpg" }, "articles": { "url": "https://example.com/images/thumbnail.jpg" }, "gallery": [ { "url": "https://example.com/gallery/photo1.jpg" }, { "url": "https://example.com/gallery/photo2.png" }, { "url": "https://example.com/gallery/photo3.webp" } ] } }

    Příklad odpovědi s imagesprocessed
    {

    "success": true, "data": { "id": 1234, "title": "Testovací článek s obrázky", "body": "

    Obsah článku

    ", "status": "active", "images": [ { "id": 1300, "name": "67abc123-main-banner.webp", "filepath": "example.com/67abc123-main-banner.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/67abc123-main-banner.webp", "mimetype": "image/webp", "size": 45678, "variant": "articleDetail", "createdat": "2025-01-23T10:30:00Z" }, { "id": 1301, "name": "67abc456-thumbnail.webp", "filepath": "example.com/67abc456-thumbnail.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/67abc456-thumbnail.webp", "mimetype": "image/webp", "size": 23456, "variant": "articles", "createdat": "2025-01-23T10:30:01Z" }, { "id": 1302, "name": "67abc789-photo1.webp", "filepath": "example.com/67abc789-photo1.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/67abc789-photo1.webp", "mimetype": "image/webp", "size": 34567, "variant": "gallery", "createdat": "2025-01-23T10:30:02Z" } ], "createdat": "2025-01-23T10:30:00Z", "updatedat": "2025-01-23T10:30:00Z" }, "imagesprocessed": [ { "type": "articleDetail", "status": "success", "url": "https://example.com/images/main-banner.jpg", "fileid": 1300, "publicurl": "https://storage.googleapis.com/bucket/example.com/67abc123-main-banner.webp" }, { "type": "articles", "status": "success", "url": "https://example.com/images/thumbnail.jpg", "fileid": 1301, "publicurl": "https://storage.googleapis.com/bucket/example.com/67abc456-thumbnail.webp" }, { "type": "gallery[0]", "status": "success", "url": "https://example.com/gallery/photo1.jpg", "fileid": 1302, "publicurl": "https://storage.googleapis.com/bucket/example.com/67abc789-photo1.webp" }, { "type": "gallery[1]", "status": "failed", "url": "https://example.com/gallery/photo2.png", "error": "Časový limit stahování překročen (30s)" }, { "type": "gallery[2]", "status": "success", "url": "https://example.com/gallery/photo3.webp", "fileid": 1303, "publicurl": "https://storage.googleapis.com/bucket/example.com/67abc999-photo3.webp" } ] }

    Pole imagesprocessed obsahuje:
    • type: Typ obrázku (articleDetail, articles, gallery[index])
    • status: "success" nebo "failed"
    • url: Původní URL adresa obrázku
    • fileid: ID nahraného souboru v DB (pouze při úspěchu)
    • publicurl: Veřejná URL adresa v GCS (pouze při úspěchu)
    • error: Chybová zpráva v češtině (pouze při selhání)
    Poznámky k implementaci
    Partial success scénář:

    V příkladu výše vidíte typický případ částečného úspěchu:

    • articleDetail: ✅ Úspěšně stažen a zpracován
    • articles: ✅ Úspěšně stažen a zpracován
    • gallery[0]: ✅ Úspěšně stažen
    • gallery[1]: ❌ Selhalo (timeout) - ale zpracování pokračovalo
    • gallery[2]: ✅ Úspěšně stažen

    Článek byl vytvořen se 4 obrázky (banner + list + 2 galerie), přestože jeden obrázek selhal.

    Logování:

    Všechny operace jsou logovány:

    • INFO: Úspěšné stažení a zpracování
    • WARNING: Selhání stažení obrázku (s pokračováním)
    • ERROR: Kritická chyba v pipeline (s výjimkou)
    Varianty v databázi:

    Pro každý úspěšně nahraný obrázek:

    • 1 záznam v uploadedfiles (originál)
    • 17 záznamů v imagevariants (různé velikosti)
    • 1 záznam v articleimages (vazba article ↔ image)

    Metoda 2: Použití fileid (preferováno)

    Kdy použít fileid místo URL:
    • ✅ Soubory již nahrané přes admin rozhraní
    • ✅ Rychlejší operace (bez stahování a zpracování)
    • ✅ Opětovné použití stejného obrázku pro více článků
    • ✅ Garant konzistence (soubor již prošel pipeline)
    • ✅ Žádné vytváření nových záznamů - pouze reference na existující soubor
    ⚠️ DŮLEŽITÉ: Při použití file
    id se NEVYTVÁŘÍ nový soubor v databázi. API pouze odkazuje na již existující záznam a propojí ho s článkem. Stejný fileid můžete použít pro neomezený počet článků. Request s fileid:POST /api/v1/articles

    { "title": "Článek s již nahranými obrázky", "subtitle": "Rychlé přiřazení existujících souborů", "body": "Obsah článku...", "status": "active", "categories": [15, 23], "images": { "articleDetail": { "fileid": 1303 }, "articles": { "fileid": 1304 }, "gallery": [ { "fileid": 1305 }, { "fileid": 1306 }, { "fileid": 1307 } ] } }

    Response (úspěch):{

    "success": true, "article": { "id": 894, "title": "Článek s již nahranými obrázky", "subtitle": "Rychlé přiřazení existujících souborů", "body": "Obsah článku...", "status": "active", "url": "https://example.com/clanek-s-jiz-nahranymi-obrazky", "categories": [ { "id": 15, "title": "electronics", "displayname": "Electronics" }, { "id": 23, "title": "smartphones", "displayname": "Smartphones" } ], "images": [ { "id": 1303, "name": "banner-existing.webp", "filepath": "example.com/banner-existing.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/banner-existing.webp", "mimetype": "image/webp", "size": 45678, "variant": "articleDetail", "createdat": "2025-01-20T14:22:00Z" }, { "id": 1304, "name": "thumb-existing.webp", "filepath": "example.com/thumb-existing.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/thumb-existing.webp", "mimetype": "image/webp", "size": 12345, "variant": "articles", "createdat": "2025-01-20T14:23:00Z" }, { "id": 1305, "name": "gallery1-existing.webp", "filepath": "example.com/gallery1-existing.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/gallery1-existing.webp", "mimetype": "image/webp", "size": 34567, "variant": "gallery", "createdat": "2025-01-20T14:24:00Z" }, { "id": 1306, "name": "gallery2-existing.webp", "filepath": "example.com/gallery2-existing.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/gallery2-existing.webp", "mimetype": "image/webp", "size": 38901, "variant": "gallery", "createdat": "2025-01-20T14:25:00Z" }, { "id": 1307, "name": "gallery3-existing.webp", "filepath": "example.com/gallery3-existing.webp", "publicurl": "https://storage.googleapis.com/bucket/example.com/gallery3-existing.webp", "mimetype": "image/webp", "size": 41234, "variant": "gallery", "createdat": "2025-01-20T14:26:00Z" } ], "createdat": "2025-01-23T11:15:00Z", "updatedat": "2025-01-23T11:15:00Z" }, "imagesprocessed": [ { "type": "articleDetail", "status": "success", "source": "existingfile", "fileid": 1303, "publicurl": "https://storage.googleapis.com/bucket/example.com/banner-existing.webp" }, { "type": "articles", "status": "success", "source": "existingfile", "fileid": 1304, "publicurl": "https://storage.googleapis.com/bucket/example.com/thumb-existing.webp" }, { "type": "gallery[0]", "status": "success", "source": "existingfile", "fileid": 1305, "publicurl": "https://storage.googleapis.com/bucket/example.com/gallery1-existing.webp" }, { "type": "gallery[1]", "status": "success", "source": "existingfile", "fileid": 1306, "publicurl": "https://storage.googleapis.com/bucket/example.com/gallery2-existing.webp" }, { "type": "gallery[2]", "status": "success", "source": "existingfile", "fileid": 1307, "publicurl": "https://storage.googleapis.com/bucket/example.com/gallery3-existing.webp" } ] }

    Klíčové rozdíly oproti URL metodě:
  • source: "existingfile" - soubor byl již nahrán, nedošlo ke stahování
  • Žádné časové prodlevy - okamžité přiřazení (< 100ms vs 5-15s při URL)
  • Žádná validace URL - není potřeba kontrolovat dostupnost
  • Zachování původních metadat - createdat se nemění
  • Chování při kombinaci fileid + URL

    Priorita: fileid > URL

    Pokud zadáte obojí, API použije fileid a url bude ignorováno:

    {

    "images": { "articleDetail": { "fileid": 1303, "url": "https://example.com/ignored.jpg" // ← BUDE IGNOROVÁNO } } }

    V logu uvidíte:

    INFO: Přiřazování banner obrázku z existujícího souboru [fileid: 1303]

    DEBUG: URL ignorováno kvůli přítomnosti fileid

    Chybové stavy při použití fileid

    1. fileid neexistuje:{

    "images": { "articleDetail": { "fileid": 99999 } } }

    Response:

    {

    "imagesprocessed": [ { "type": "articleDetail", "status": "failed", "source": "existingfile", "fileid": 99999, "error": "Soubor s ID 99999 nebyl nalezen nebo nepatří k této stránce" } ] }

    2. fileid patří jinému webu (siteid mismatch):{

    "images": { "articles": { "fileid": 5678 // Patří siteid=2, aktuální požadavek je siteid=7 } } }

    Response:

    {

    "imagesprocessed": [ { "type": "articles", "status": "failed", "source": "existingfile", "fileid": 5678, "error": "Soubor s ID 5678 nebyl nalezen nebo nepatří k této stránce" } ] }

    Bezpečnostní mechanismus:

    API vždy validuje:

    $file = UploadedFile::where('id', $fileId)

    ->where('siteid', $siteId) ->first();

    Tím je zajištěno, že web A nemůže používat soubory webu B.

    Kombinované použití (mix fileid + URL)

    Můžete v jednom requestu kombinovat oba přístupy:

    {

    "images": { "articleDetail": { "fileid": 1303 // ← Existující soubor }, "articles": { "url": "https://new.jpg" // ← Nový soubor ke stažení }, "gallery": [ { "fileid": 1305 }, // ← Existující { "url": "https://new2.jpg" }, // ← Nový { "fileid": 1306 } // ← Existující ] } }

    Response bude obsahovat:

    {

    "imagesprocessed": [ { "type": "articleDetail", "status": "success", "source": "existingfile", // ← fileid "fileid": 1303 }, { "type": "articles", "status": "success", "source": "downloaded", // ← URL "url": "https://new.jpg", "fileid": 1450 // ← Nově vytvořený }, { "type": "gallery[0]", "status": "success", "source": "existingfile", // ← fileid "fileid": 1305 }, { "type": "gallery[1]", "status": "success", "source": "downloaded", // ← URL "url": "https://new2.jpg", "fileid": 1451 // ← Nově vytvořený }, { "type": "gallery[2]", "status": "success", "source": "existingfile", // ← fileid "fileid": 1306 } ] }

    Pole source vám umožní rozlišit:
    • "existingfile" = použit fileid (žádný nový soubor)
    • "downloaded" = staženo z URL (vytvořen nový soubor)

    Srovnání URL vs fileid metod

    AspektURL metodafileid metoda
    VstupURL string (např. https://example.com/image.jpg)Integer (např. 1303)
    Co se stane1. Stáhne soubor
    2. Zpracuje (WebP, komprese)
    3. Nahraje do GCS
    4. Vytvoří nový záznam v DB
    5. Vygeneruje 17 variant
    6. Přiřadí k článku
    1. Ověří, že fileid existuje
    2. Ověří site
    id
    3. Přiřadí existující záznam k článku
    Vytvoří nový soubor v DB?✅ ANO - vždy nové ID❌ NE - používá existující
    Opětovné použití❌ Při každém volání nový soubor✅ Neomezené použití stejného ID
    Čas zpracování5-15 sekund< 100 ms
    DB operace+1 uploadedfiles
    +17 image
    variants
    +1 articleimages
    +1 articleimages (pouze propojení)
    GCS operace+18 nahrání souborů0 (soubor již existuje)
    TinyPNG API volání1 (komprese)0
    Použití v response"fileid": 1450 (nové)"fileid": 1303 (stejné jako vstup)
    Pole source"downloaded""existingfile"
    Příklad - stejný obrázek pro 3 články:
    URL metoda (3× volání):

    Článek 1 → URL → stáhne → fileid: 1450 Článek 2 → URL → stáhne → fileid: 1451 ← NOVÝ SOUBOR Článek 3 → URL → stáhne → fileid: 1452 ← NOVÝ SOUBOR Výsledek: 3 soubory v GCS, 3 záznamy v DB

    fileid metoda (3× volání): Článek 1 → fileid: 1303 → přiřadí existující Článek 2 → fileid: 1303 → přiřadí existující ← STEJNÝ SOUBOR Článek 3 → fileid: 1303 → přiřadí existující ← STEJNÝ SOUBOR Výsledek: 1 soubor v GCS, 1 záznam v DB, 3 propojení


    Metoda 1: URL (doporučeno) - Legacy dokumentace

    > POZNÁMKA: Sekce níže obsahuje starší dokumentaci. Pro aktuálně verifikovaný create kontrakt POST /api/v1/articles je potvrzené pouze JSON tělo přes getJsonBody() a obrázky v images.articleDetail, images.articles, images.gallery s url nebo fileid. Zpracování files, Base64 a multipart uploadu není v aktuálním create controlleru verifikováno jako podporované.

    Backend automaticky stáhne soubor z URL, zpracuje ho a nahraje do Google Cloud Storage.

    Obrázky:
    • Stažení z URL
    • Konverze na WebP formát
    • Komprese přes TinyPNG API
    • Nahrání do GCS
    • Generování variant (banner, articleDetail, thumbnail, gallery...)
    • Propojení s článkem
    Soubory:
    • Historická dokumentace. Aktuální create controller je při POST /api/v1/articles neukládá.
    {

    "images": { "articleDetail": { "url": "https://example.com/images/banner.jpg" }, "articles": { "url": "https://example.com/images/list-thumbnail.jpg" }, "gallery": [ { "url": "https://cdn.example.com/img1.png" }, { "url": "https://cdn.example.com/img2.jpg" } ] }, "files": [ { "url": "https://example.com/docs/file.pdf", "name": "Název souboru", "type": "attachment" } ] }

    Metoda 2: Base64 (pro menší soubory < 5MB) - Legacy

    > POZNÁMKA: Historická sekce. Aktuální create controller tuto metodu nemá v kanonicky verifikovaném flow potvrzenou.

    Vhodné pro mobilní aplikace nebo když máte soubor už v paměti.

    Legacy struktura (pro zpětnou kompatibilitu):{

    "images": { "main": { "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", "alt": "Popisek", "title": "Titulek", "filename": "photo.jpg" } }, "files": [ { "base64": "data:application/pdf;base64,JVBERi0xLj...", "name": "Dokument", "filename": "file.pdf", "type": "attachment" } ] }

    Nová struktura (doporučeno pro konzistenci):{

    "images": { "articleDetail": { "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", "filename": "banner.jpg" }, "articles": { "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", "filename": "thumbnail.jpg" }, "gallery": [ { "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", "filename": "gallery1.jpg" } ] }, "files": [ { "base64": "data:application/pdf;base64,JVBERi0xLj...", "name": "Dokument", "filename": "file.pdf", "type": "attachment" } ] }

    Metoda 3: Multipart Upload (pro velké soubory > 5MB) - Legacy

    > POZNÁMKA: Historická sekce. Aktuální create controller čte JSON body a multipart flow zde není kanonicky verifikovaný.

    Pro velké soubory použijte multipart/form-data:

    Legacy struktura:
    POST /api/v1/articles

    Content-Type: multipart/form-data Authorization: Bearer YOURAPITOKEN

    --boundary Content-Disposition: form-data; name="data" Content-Type: application/json

    { "title": "Název článku", "body": "

    Obsah

    ", "categories": [15], "status": "active", "userid": 42, "url": "/clanek" } --boundary Content-Disposition: form-data; name="mainimage"; filename="banner.jpg" Content-Type: image/jpeg

    [binary data] --boundary Content-Disposition: form-data; name="galleryimage1"; filename="gallery1.jpg" Content-Type: image/jpeg

    [binary data] --boundary Content-Disposition: form-data; name="file1"; filename="attachment.pdf" Content-Type: application/pdf

    [binary data] --boundary--

    Nová struktura (doporučeno):POST /api/v1/articles

    Content-Type: multipart/form-data Authorization: Bearer YOURAPITOKEN

    --boundary Content-Disposition: form-data; name="data" Content-Type: application/json

    { "title": "Název článku", "body": "

    Obsah

    ", "categories": [15], "status": "active", "userid": 42, "url": "/clanek" } --boundary Content-Disposition: form-data; name="articleDetail"; filename="banner.jpg" Content-Type: image/jpeg

    [binary data] --boundary Content-Disposition: form-data; name="articles"; filename="thumbnail.jpg" Content-Type: image/jpeg

    [binary data] --boundary Content-Disposition: form-data; name="gallery[]"; filename="gallery1.jpg" Content-Type: image/jpeg

    [binary data] --boundary Content-Disposition: form-data; name="gallery[]"; filename="gallery2.jpg" Content-Type: image/jpeg

    [binary data] --boundary Content-Disposition: form-data; name="file1"; filename="attachment.pdf" Content-Type: application/pdf

    [binary data] --boundary--

    Zpracování na backendu

    Backend provede následující kroky:

    Pro obrázky:
  • Stažení/příjem obrázku
  • Validace formátu (JPEG, PNG, WebP, GIF)
  • Konverze na WebP
  • Komprese přes TinyPNG API (pokud je klíč nakonfigurován)
  • Upload originálu do GCS: {sitedomain}/{uniqueid}-{normalizedfilename}.webp
  • Generování variant podle konfigurace webu:
  • - banner: dle bannerw × bannerh - articleDetail: dle articleDetailw × articleDetailh - galleryThumbnail: dle galleryThumbnailw × galleryThumbnailh - atd.

  • Upload variant do GCS: {sitedomain}/thumb/{variant}/{filename}
  • Uložení záznamů do tabulek uploadedfiles a imagevariants
  • Propojení s článkem přes pivot tabulku articleimages
  • Pro soubory:
  • Stažení/příjem souboru
  • Validace MIME typu
  • Normalizace názvu souboru (transliterace diakritiky, max 50 znaků)
  • Upload do GCS: {sitedomain}/{uniqueid}-{normalizedfilename}
  • Uložení záznamu do uploadedfiles
  • Propojení s článkem přes pivot tabulku articlefiles
  • Validace URL:
    • URL musí začínat http:// nebo https://
    • URL musí být dostupná (HTTP 200)
    • Maximální velikost stahovaného souboru: 50 MB
    • Timeout: 30 sekund
    • Podporované formáty obrázků: JPEG, PNG, WebP, GIF, SVG
    • Podporované formáty souborů: PDF, DOC, DOCX, XLS, XLSX, ZIP, RAR
    Chybové stavy:{

    "success": false, "error": "Image Processing Error", "message": "Nepodařilo se stáhnout obrázek z URL", "details": { "url": "https://invalid.com/image.jpg", "reason": "Connection timeout" } }

    {

    "success": false, "error": "Validation Error", "message": "Nepodporovaný formát souboru", "details": { "url": "https://example.com/file.exe", "mimetype": "application/x-msdownload", "allowedtypes": ["image/", "application/pdf", "application/msword", ...] } }

    Response (Success - 201 Created)

    {

    "success": true, "data": { "id": 866, "title": "Název článku", "subtitle": "Podtitul článku", "shorttext": "Krátký úvodní text", "snippet": "Snippet pro seznam", "body": "

    HTML obsah článku

    ", "publishat": null, "manualupdateat": null, "meta": { "title": "SEO titulek", "description": "SEO popis pro meta description", "keywords": "klíčová, slova, api", "autorname": "Jan Novák", "zdroj": "" }, "categories": [ { "id": 15, "title": "technologie", "displayname": "Technologie" } ], "author": { "id": 42, "name": "Jan Novák" }, "tags": [ { "id": 7, "name": "php", "slug": "php" } ], "images": { "articles": null, "articleDetail": { "publicurl": "https://storage.googleapis.com/bucket/example.com/thumb/articleDetail/banner.webp", "width": 1300, "height": 530, "imagename": "banner.webp", "variants": { "articleDetail": { "publicurl": "https://storage.googleapis.com/bucket/example.com/thumb/articleDetail/banner.webp", "width": 1300, "height": 530 } } }, "gallery": [] } }, "meta": { "imagesprocessed": [ { "type": "bannerimage", "status": "success", "source": "downloaded", "url": "https://example.com/images/banner.jpg", "fileid": 1450, "publicurl": "https://storage.googleapis.com/bucket/example.com/banner.webp" } ] } }

    Response (Error - 400 Bad Request)

    {

    "type": "/errors/validation", "title": "Validation Failed", "status": 400, "detail": "The request contains invalid data.", "instance": "/api/v1/articles", "errors": { "title": ["Title is required"], "body": ["Body is required"], "categoryid": ["Main category ID (categoryid) is required for URL generation"] } }

    Response (Poznámka ke kolizi URL)

    Create endpoint běžně nevrací 409 Conflict kvůli duplicitní URL. Kolizi řeší automatickým přidáním náhodného suffixu k vygenerovanému slugu. Pokud by backend ani po více pokusech nedokázal najít unikátní URL, vrátí chybu 500.


    📖 2. Zobrazení detailu článku

    Načte kompletní detail konkrétního článku včetně všech vztahů.

    Endpoint

    GET /api/v1/articles/{idOrSlug}

    Oprávnění

    • Token musí mít scope: articles:read

    URL Parameters

    ParametrTypPopis
    idOrSluginteger\stringID článku (číselné, např. 899) nebo plný slug (textový, např. "blog/cista-fasada"). Backend automaticky rozpozná typ pomocí isnumeric().

    Query Parameters

    ParametrTypVýchozíPopis
    includestringallComma-separated: categories, tags, images, files, parameters, author, relatedarticles, readalso

    Request Example

    GET /api/v1/articles/866?include=categories,tags,images,relatedarticles,readalso

    GET /api/v1/articles/blog/cista-fasada?include=all Authorization: Bearer YOURAPITOKEN

    Response (Success - 200 OK)

    {

    "success": true, "data": { "id": 866, "title": "Název článku", "subtitle": "Podtitul", "shorttext": "Krátký popisek", "snippet": "Snippet", "body": "

    HTML obsah článku

    ", "status": "active", "siteid": 7, "publishat": "2026-01-15T10:00:00Z", "publishendat": null, "manualupdateat": "2026-01-15T14:30:00Z", "url": "https://domain.cz/clanky/nazev-clanku", "createdat": "2026-01-10T09:00:00Z", "updatedat": "2026-01-15T14:30:00Z",

    "meta": { "title": "SEO titulek", "description": "SEO popis", "keywords": "klíčová, slova", "contenttyp": "article", "mainCatid": 15, "autorname": "", "zdroj": "" },

    "author": { "id": 42, "email": "[email protected]", "fullname": "John Doe", "bio": "Bio autora", "avatarurl": "https://...", "url": "/autor/john-doe" },

    "categories": [ { "id": 15, "name": "Technologie", "url": "/technologie" }, { "id": 23, "name": "Software", "url": "/technologie/software" } ],

    "tags": [ { "id": 5, "name": "php", "slug": "tag/php" }, { "id": 12, "name": "webový vývoj", "slug": "tag/webovy-vyvoj" } ],

    "images": { "main": { "id": 456, "filename": "article-banner.webp", "publicurl": "https://storage.googleapis.com/.../article-banner.webp", "variants": { "banner": { "url": "https://.../banner/article-banner.webp", "width": 1500, "height": 635 }, "articleDetail": { "url": "https://.../articleDetail/article-banner.webp", "width": 800, "height": 600 } } }, "gallery": [ { "id": 789, "filename": "gallery-1.webp", "publicurl": "https://...", "variants": {} } ] },

    "files": [ { "id": 101, "filename": "priloha.pdf", "publicurl": "https://.../priloha.pdf", "mimetype": "application/pdf", "filesize": 2048576 } ],

    "parameters": [ { "id": 2, "name": "Doba čtení", "datatype": "integer", "value": 5 }, { "id": 5, "name": "Autor článku (externí)", "datatype": "string", "value": "Jan Novák" } ],

    "relatedarticles": [ { "id": 123, "title": "První související článek", "slug": "blog/prvni-souvisejici-clanek", "subtitle": "Podtitul souvisejícího článku", "shorttext": "Krátký popisek souvisejícího článku", "snippet": "Snippet souvisejícího článku...", "status": "active", "siteid": 7, "publishat": "2026-01-10T10:00:00Z", "url": "https://domain.cz/blog/prvni-souvisejici-clanek", "author": { "id": 42, "email": "[email protected]", "details": { "fullname": "John Doe", "bio": "Bio autora", "avatar": { "variants": { "usricosmall": { "publicurl": "https://storage.googleapis.com/.../usricosmall/john.webp", "width": 32, "height": 32 }, "usricobig": { "publicurl": "https://storage.googleapis.com/.../usricobig/john.webp", "width": 64, "height": 64 } } } } }, "tags": [ { "id": 5, "name": "php", "slug": "tag/php" } ], "images": { "articles": { "id": 789, "filename": "article-thumb.webp", "publicurl": "https://storage.googleapis.com/.../article-thumb.webp", "alt": "Název obrázku", "title": "Název obrázku", "variants": { "articles": { "publicurl": "https://storage.googleapis.com/.../articles/thumb.webp", "width": 1000, "height": 486 }, "articleDetail": { "publicurl": "https://storage.googleapis.com/.../articleDetail/thumb.webp", "width": 1300, "height": 530 }, "galleryThumbnail": { "publicurl": "https://storage.googleapis.com/.../galleryThumbnail/thumb.webp", "width": 500, "height": 414 }, "imglistarticles": { "publicurl": "https://storage.googleapis.com/.../imglistarticles/thumb.webp", "width": 1000, "height": 486 }, "imgdetailarticleheader": { "publicurl": "https://storage.googleapis.com/.../imgdetailarticleheader/thumb.webp", "width": 1300, "height": 530 }, "imgdetailarticlepost": { "publicurl": "https://storage.googleapis.com/.../imgdetailarticlepost/thumb.webp", "width": 1920, "height": 1282 }, "imgrecentarticles": { "publicurl": "https://storage.googleapis.com/.../imgrecentarticles/thumb.webp", "width": 339, "height": 165 }, "imgrelatedpost": { "publicurl": "https://storage.googleapis.com/.../imgrelatedpost/thumb.webp", "width": 360, "height": 214 }, "imgctvercovy": { "publicurl": "https://storage.googleapis.com/.../imgctvercovy/thumb.webp", "width": 400, "height": 400 } } } } } ],

    "readalso": [ { "id": 456, "title": "Čtěte také článek 1", "slug": "blog/ctete-take-1", "subtitle": "Podtitul", "shorttext": "Krátký popisek", "snippet": "Snippet článku", "author": { "id": 42, "email": "[email protected]", "details": { "fullname": "John Doe", "bio": "Bio autora", "avatar": { "variants": { "usricosmall": { "publicurl": "https://storage.googleapis.com/.../usricosmall/john.webp", "width": 32, "height": 32 }, "usricobig": { "publicurl": "https://storage.googleapis.com/.../usricobig/john.webp", "width": 64, "height": 64 } } } } }, "tags": [ { "id": 5, "name": "php", "slug": "tag/php" } ], "images": { "articles": { "id": 789, "filename": "article-thumb.webp", "publicurl": "https://storage.googleapis.com/.../article-thumb.webp", "alt": "Název obrázku", "title": "Název obrázku", "variants": { "articles": { "publicurl": "https://storage.googleapis.com/.../articles/thumb.webp", "width": 1000, "height": 486 }, "articleDetail": { "publicurl": "https://storage.googleapis.com/.../articleDetail/thumb.webp", "width": 1300, "height": 530 }, "galleryThumbnail": { "publicurl": "https://storage.googleapis.com/.../galleryThumbnail/thumb.webp", "width": 500, "height": 414 }, "imglistarticles": { "publicurl": "https://storage.googleapis.com/.../imglistarticles/thumb.webp", "width": 1000, "height": 486 }, "imgdetailarticleheader": { "publicurl": "https://storage.googleapis.com/.../imgdetailarticleheader/thumb.webp", "width": 1300, "height": 530 }, "imgdetailarticlepost": { "publicurl": "https://storage.googleapis.com/.../imgdetailarticlepost/thumb.webp", "width": 1920, "height": 1282 }, "imgrecentarticles": { "publicurl": "https://storage.googleapis.com/.../imgrecentarticles/thumb.webp", "width": 339, "height": 165 }, "imgrelatedpost": { "publicurl": "https://storage.googleapis.com/.../imgrelatedpost/thumb.webp", "width": 360, "height": 214 }, "imgctvercovy": { "publicurl": "https://storage.googleapis.com/.../imgctvercovy/thumb.webp", "width": 400, "height": 400 } } } } }, { "id": 457, "title": "Čtěte také článek 2", "slug": "blog/ctete-take-2", "subtitle": null, "shorttext": null, "snippet": "Snippet druhého článku", "author": {...}, "tags": [...], "images": { "articles": {...} } } ] } }

    Odpověď (Chyba - 404 Nenalezeno)

    {

    "success": false, "error": "Not Found", "message": "Článek s ID 866 nebyl nalezen nebo nepatří k tomuto webu" }


    ✏️ 2.1 Aktualizace článku

    Aktualizuje existující článek v systému. Podporuje částečnou aktualizaci (PATCH semantiku) - je možné odeslat pouze pole, která se mají změnit.

    Koncový bod

    PUT /api/v1/articles/{id}

    Oprávnění

    • Token musí mít rozsah:articles:write

    Parametry URL

    ParametrTypPopis
    idcelé čísloID článku k aktualizaci

    Záhlaví požadavků

    Content-Type: application/json

    Authorization: Bearer YOURAPITOKEN

    Tělo požadavku

    Struktura request body je identická jako u POST /api/v1/articles. Všechna pole jsou volitelná - odešlete pouze pole, která chcete aktualizovat.

    {

    "title": "Aktualizovaný název článku", "body": "

    Nový HTML obsah článku

    ", "status": "active", "categories": [15, 23, 45], "images": { "articleDetail": { "url": "https://example.com/images/new-banner.jpg" }, "articles": { "url": "https://example.com/images/new-thumbnail.jpg" }, "gallery": [ { "url": "https://example.com/images/gallery-new-1.jpg" } ] } }

    Popis polí

    Všechna pole mají stejný popis a validační pravidla jako u POST /api/v1/articles (viz sekce 1).

    Důležité poznámky k aktualizaci :
  • Částečná aktualizace : Není nutné zaslat všechna pole. Pole, která nejsou v žádosti, zůstanou beze změny.
  • Poleimages : Při aktualizaci obrázků platí následující pravidla:
  • - článekDetail a články : Nahradí existující obrázek nový (vztah 1:1) - galerie : Přidání nové obrázky do existující galerie (vztah 1:N) - Duplicitní se automaticky přeskakují - Pokud nechcete měnit obrázky, prostě je vynechejte z požadavku fileid

  • Polecategories : Při aktualizaci kategorií se celé pole nahradí novým (ne append)
  • Poletags : Při aktualizaci tagů se celé pole nahradí novým (ne append)
  • Poleshorttext asnippet : Při aktualizaci se ukládají přímo do tabulkyarticles.
  • Pól parameters/ parametervalues:
  • - při aktualizaci se hodnoty ukládají do tabulkyparametervalues - je záložní alias pro starší integraci - nebo prázdný řetězec ( ) smaže existující hodnotu parametru parametervaluesnull""

  • Stahování obrázků z URL : Funguje stejně jako u POST endpointu. Viz detailní dokumentace v sekci 1 "Stahování obrázků z URL" (technické specifikace, validační pravidla, chybové stavy).
  • Příklad požadavku 1: Minimální aktualizace

    PUT /api/v1/articles/866

    Content-Type: application/json Authorization: Bearer YOURAPITOKEN

    { "title": "Nový název článku", "status": "suspend" }

    Příklad požadavku 2: Aktualizace s novými obrázky

    PUT /api/v1/articles/866

    Content-Type: application/json Authorization: Bearer YOURAPITOKEN

    { "title": "Článek s aktualizovanými obrázky", "images": { "articleDetail": { "url": "https://example.com/new-banner.jpg" }, "gallery": [ { "url": "https://example.com/gallery/photo-new-1.jpg" }, { "url": "https://example.com/gallery/photo-new-2.jpg" } ] } }

    Odpověď (Úspěch - 200 OK)

    Struktura response je identická jako u POST /api/v1/articles.

    {

    "success": true, "data": { "id": 866, "title": "Aktualizovaný název článku", "url": "https://domain.cz/clanky/aktualizovany-nazev-clanku", "status": "active", "createdat": "2026-01-10T09:00:00Z", "updatedat": "2026-01-16T15:45:00Z", "articleDetail": { "id": 1302, "url": "https://storage.googleapis.com/.../banner/new-banner.webp", "variants": { "banner": { "url": "https://storage.googleapis.com/.../banner/new-banner.webp", "width": 1300, "height": 530 }, "thumbnail": { "url": "https://storage.googleapis.com/.../thumb/new-banner.webp", "width": 300, "height": 200 } } }, "gallery": [ { "id": 1303, "url": "https://storage.googleapis.com/.../gallery/photo-new-1.webp", "variants": {...} }, { "id": 1304, "url": "https://storage.googleapis.com/.../gallery/photo-new-2.webp", "variants": {...} } ] }, "imagesprocessed": [ { "type": "articleDetail", "status": "success", "url": "https://example.com/new-banner.jpg", "fileid": 1302, "publicurl": "https://storage.googleapis.com/.../banner/new-banner.webp" }, { "type": "gallery[0]", "status": "success", "url": "https://example.com/gallery/photo-new-1.jpg", "fileid": 1303, "publicurl": "https://storage.googleapis.com/.../gallery/photo-new-1.webp" }, { "type": "gallery[1]", "status": "failed", "url": "https://example.com/gallery/photo-new-2.jpg", "error": "Časový limit stahování překročen (30s)" } ] }

    Poznámka k poli imagesprocessed: Zobrazuje se pouze pokud byly zpracovány nějaké obrázky z URL. Obsahuje detailní informace o úspěchu/selhání každého obrázku. Viz detaily v sekci 1 "Pole obrázky zpracovány".

    Odpověď (Chyba - 404 Nenalezeno)

    {

    "success": false, "error": "Not Found", "message": "Článek s ID 866 nebyl nalezen nebo nepatří k tomuto webu" }

    Odpověď (Chyba - 400 Špatný požadavek)

    {

    "success": false, "error": "Validation Error", "message": "Chybná validace dat", "errors": { "title": ["Pole title musí obsahovat alespoň 3 znaky"], "status": ["Neplatná hodnota status. Povolené: active, development, suspend"] } }

    Odpověď (Chyba - 409 Konflikt)

    Pokud se pokusíte změnit URL na hodnotu, která už existuje:

    {

    "success": false, "error": "Conflict", "message": "URL již existuje pro tento web", "details": { "url": "/clanky/jiny-clanek", "existingarticleid": 123 } }


    📚 3. Seznam článků

    Načtete seznam článků s různými filtry a možnostmi.

    3.1 Všechny články

    GET /api/v1/articles

    3.2 Nejnovější X články

    GET /api/v1/articles?limit=10&sort=newest

    3.3 Všechny články v kategorii

    GET /api/v1/articles?categoryid=15

    3.4 X článků v kategorii se stránkováním

    GET /api/v1/articles?categoryid=15&limit=20&page=2

    Parametry dotazu

    ParametrTypVýchodíPopis
    categoryidcelé číslo-Filtrovat podle ID kategorie
    limitcelé číslo20Počet článků na stránce (max 100)
    pagecelé číslo1Číslo stránky
    sortřetězecnejnovějšínewest, oldest, updated, title,popular
    tagřetězec-Filtr podle tagu (slug)
    authoridcelé číslo-Filtr podle autora
    searchřetězec-Fulltextové vyhledávání (viz bod 4)
    Poznámka: API vrací pouze články se stavem active. Parametr statusbyl odstraněn.

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": { "articles": [ { "id": 866, "title": "Název článku", "slug": "/clanky/nazev-clanku", "subtitle": "Podtitul", "snippet": "Krátký úryvek z článku pro seznam...", "publishat": "2026-01-15T10:00:00.000000Z", "manualupdateat": "2026-01-20T14:30:00.000000Z", "categories": [ { "id": 15, "title": "technologie", "displayname": "Technologie", "slug": "/kategorie/technologie", "parentid": null, "order": 1, "image": { "publicurl": "https://storage.googleapis.com/.../category.webp", "imagename": "category.webp", "alt": "Technologie", "title": "Kategorie Technologie", "variants": { "banner": { "publicurl": "https://storage.googleapis.com/.../banner/category.webp", "width": 1500, "height": 400 }, "thumbnail": { "publicurl": "https://storage.googleapis.com/.../thumb/category.webp", "width": 300, "height": 200 } } }, "children": [] } ], "author": { "id": 42, "email": "[email protected]", "details": { "fullname": "John Doe", "signature": "Senior Tech Writer & AI Specialist", "jobtitle": "Senior Content Manager", "companyname": "Tech Media s.r.o.", "website": "https://johndoe.com", "publicemail": "[email protected]", "allowpublicprofile": true, "displaysignature": true, "iscontributor": false, "socials": [ { "platform": "twitter", "url": "https://twitter.com/johndoe", "username": "@johndoe" }, { "platform": "linkedin", "url": "https://linkedin.com/in/johndoe" } ], "links": [ { "title": "Portfolio", "url": "https://johndoe.com/portfolio" } ], "avatar": { "publicurl": "https://storage.googleapis.com/.../avatar.webp", "imagename": "avatar.webp", "alt": "John Doe", "title": "John Doe - Avatar", "variants": { "thumbnail": { "publicurl": "https://storage.googleapis.com/.../thumb/avatar.webp", "width": 150, "height": 150 } } } } }, "tags": [ { "id": 5, "name": "Umělá inteligence", "slug": "tag/umela-inteligence", "articlescount": 42 } ], "images": { "articles": { "publicurl": "https://storage.googleapis.com/.../articles/image.webp", "width": 400, "height": 300, "imagename": "image.webp", "alt": "Článek náhled", "title": "Název článku", "variants": { "banner": { "publicurl": "https://storage.googleapis.com/.../banner/image.webp", "width": 1500, "height": 635 }, "thumbnail": { "publicurl": "https://storage.googleapis.com/.../thumb/image.webp", "width": 300, "height": 200 } } }, "articleDetail": { "publicurl": "https://storage.googleapis.com/.../articleDetail/image.webp", "width": 800, "height": 600, "imagename": "image.webp", "alt": "Článek detail", "title": "Název článku", "variants": {} }, "gallery": [ { "publicurl": "https://storage.googleapis.com/.../gallery/img1.webp", "width": 1200, "height": 800, "imagename": "img1.webp", "alt": "Galerie obrázek 1", "title": "První obrázek galerie", "variants": { "galleryThumbnail": { "publicurl": "https://storage.googleapis.com/.../thumb/img1.webp", "width": 300, "height": 200 } } } ] } }, { "id": 865, "title": "Další článek", "slug": "/clanky/dalsi-clanek", "subtitle": null, "snippet": "Popis dalšího článku...", "publishat": "2026-01-14T08:00:00.000000Z", "manualupdateat": null, "categories": [], "author": { "id": 42, "email": "[email protected]", "details": { "fullname": "John Doe", "signature": null, "jobtitle": null, "companyname": null, "website": null, "publicemail": null, "allowpublicprofile": false, "displaysignature": false, "iscontributor": false, "socials": [], "links": [], "avatar": null } }, "tags": [], "images": { "articles": null, "articleDetail": null, "gallery": [] } } ], "pagination": { "total": 156, "perpage": 20, "currentpage": 2, "lastpage": 8, "from": 21, "to": 40, "prevpageurl": "/api/v1/articles?page=1", "nextpageurl": "/api/v1/articles?page=3" }, "meta": { "totalarticles": 156, "filteredby": { "categoryid": 15 } } } }


    🔍 4. Vyhledávání článků

    Fulltextové vyhledávání v článcích.

    Koncový bod

    GET /api/v1/articles/search

    Parametry dotazu

    ParametrTypPovinnéPopis
    qřetězec✅Hledaný řetězec (min. 3 znaky)
    fieldsřetězecvšetitle, body, shorttext,all
    categoryidcelé číslo❌Omezení na kategorii
    limitcelé číslo❌Počet výsledků (výchozí 20, max. 100)
    pagecelé číslo❌Stránka

    Příklad požadavku

    GET /api/v1/articles/search?q=umělá inteligence&fields=title,body&limit=10

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": { "query": "umělá inteligence", "results": [ { "id": 866, "title": "Umělá inteligence v roce 2026", "url": "https://domain.cz/clanky/umela-inteligence-2026", "excerpt": "...text obsahující umělá inteligence v kontextu...", "relevancescore": 0.95, "publishat": "2026-01-15T10:00:00Z", "image": { "url": "https://.../thumbnail/ai-thumb.webp" } }, { "id": 723, "title": "Budoucnost AI", "url": "https://domain.cz/clanky/budoucnost-ai", "excerpt": "...diskuse o umělé inteligenci a jejích aplikacích...", "relevancescore": 0.78, "publishat": "2026-01-10T14:00:00Z", "image": null } ], "pagination": { "total": 15, "perpage": 10, "currentpage": 1, "lastpage": 2 } } }

    Odpověď (Chyba - 400 Špatný požadavek)

    {

    "success": false, "error": "Validation Error", "message": "Hledaný řetězec musí mít alespoň 3 znaky" }


    🌲 5. Strom všech kategorií

    Načte hierarchickou strukturu všech kategorií webu.

    Koncový bod

    GET /api/v1/categories/tree

    Parametry dotazu

    ParametrTypVýchodíPopis
    navigationidcelé číslonulovéVolitelný filtr na jednu konkrétní navigaci; vrátí pouze kategorie s danýmnavigationid
    activeonlybooleovskávěrnýPokud je true, vrátí se pouze aktivní kategorie; vrátím se falsei neaktivní

    Příklad požadavku

    GET /api/v1/categories/tree?navigationid=1&activeonly=true

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": { "categories": [ { "id": 1, "title": "Technologie", "displayname": "Technologie", "url": "/technologie", "parentid": null, "level": 0, "isactive": "active", "ordercat": 1, "navigationid": 1, "articlecount": 45, "image": { "url": "https://storage.googleapis.com/bucket/images/technologie.webp", "variants": { "banner": { "id": 10508, "originalimageid": 1260, "variantname": "banner", "imagename": "696ab4fbbb108technologie.webp", "width": 1500, "height": 635, "publicurl": "https://storage.googleapis.com/bucket/images/banner/technologie.webp", "createdat": "2026-01-16T22:00:29.000000Z", "updatedat": "2026-01-16T22:00:29.000000Z" }, "articleDetail": { "id": 10510, "originalimageid": 1260, "variantname": "articleDetail", "imagename": "696ab4fdf1b56technologie.webp", "width": 1300, "height": 530, "publicurl": "https://storage.googleapis.com/bucket/images/articleDetail/technologie.webp", "createdat": "2026-01-16T22:00:30.000000Z", "updatedat": "2026-01-16T22:00:30.000000Z" } } }, "children": [ { "id": 15, "title": "Software", "displayname": "Software", "url": "/technologie/software", "parentid": 1, "level": 1, "isactive": "active", "ordercat": 2, "navigationid": 1, "articlecount": 23, "image": { "url": "https://storage.googleapis.com/bucket/images/software.webp", "variants": { "banner": { "id": 10520, "originalimageid": 1262, "variantname": "banner", "imagename": "696ab4fbbb108software.webp", "width": 1500, "height": 635, "publicurl": "https://storage.googleapis.com/bucket/images/banner/software.webp", "createdat": "2026-01-16T22:00:29.000000Z", "updatedat": "2026-01-16T22:00:29.000000Z" }, "articleDetail": { "id": 10522, "originalimageid": 1262, "variantname": "articleDetail", "imagename": "696ab4fdf1b56software.webp", "width": 1300, "height": 530, "publicurl": "https://storage.googleapis.com/bucket/images/articleDetail/software.webp", "createdat": "2026-01-16T22:00:30.000000Z", "updatedat": "2026-01-16T22:00:30.000000Z" } } }, "children": [ { "id": 23, "title": "Web Development", "displayname": "Webový vývoj", "url": "/technologie/software/web-development", "parentid": 15, "level": 2, "isactive": true, "articlecount": 12, "children": [] } ] }, { "id": 16, "title": "Hardware", "displayname": "Hardware", "url": "/technologie/hardware", "parentid": 1, "level": 1, "isactive": true, "articlecount": 18, "children": [] } ] }, { "id": 2, "title": "Lifestyle", "displayname": "Životní styl", "url": "/lifestyle", "parentid": null, "level": 0, "isactive": true, "articlecount": 67, "children": [ { "id": 25, "title": "Zdraví", "displayname": "Zdraví", "url": "/lifestyle/zdravi", "parentid": 2, "level": 1, "isactive": true, "articlecount": 34, "children": [] } ] } ], "meta": { "totalcategories": 8, "maxdepth": 3, "totalarticles": 112 } } }


    🧭 5.1 Seznam navigací

    Vrací seznam navigace aktuálního webu. Slouží externím systémem (např. publikační pipeline) k výběru navigace, strom kategorií se má použít pro zařazování článků — ID navigace lze následně použít jako filtr u . navigationidGET /api/v1/categories/tree

    Koncový bod

    GET /api/v1/navigationsRozsah: categories:read

    Parametry dotazu

    ParametrTypVýchodíPopis
    activeonlybooleovskávěrnýPokud je true, vrátí pouze aktivní navigaci; ignorováno, pokud tabulka nemá sloupecisactive

    Příklad požadavku

    GET /api/v1/navigations

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": [ { "id": 1, "name": "Hlavní menu", "description": "Primární navigace webu", "isactive": true }, { "id": 2, "name": "Patička", "description": null, "isactive": true } ], "meta": { "total": 2 } }


    📡 5.2 Datový kanál

    Pojmenované datové zdroje vrací kanonické seznamy položek pro aktuální web (siteid ).

    Koncový bod

    GET /api/v1/feeds/{key}

    Autorizace

    • Veřejný feed ( ) je dostupný bez tokenu.ispublic=1
    • Neveřejný zdroj (ispublic=0 ) vyžaduje Bearer token a případně feedu.requiredscope

    Parametry dotazu

    ParametrTypVýchodíPopis
    pagecelé číslo1Stránka výsledků
    categoryidcelé číslo—Kategorie volitelného filtru
    tagidcelé číslo—Volitelný filtr tagu

    Příklad požadavku

    GET /api/v1/feeds/homepagearticles?page=1

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": [], "meta": { "pagination": { "currentpage": 1, "perpage": 20, "total": 0, "lastpage": 0 }, "feed": { "key": "homepagearticles", "entitytype": "articles", "ispublic": true } } }


    🌿 6. Strom podkategorií

    Načte hierarchickou strukturu podkategorií konkrétní kategorie.

    Koncový bod

    GET /api/v1/categories/{id}/children

    Parametry URL

    ParametrTypPopis
    idcelé čísloID nadřazené kategorie

    Parametry dotazu

    ParametrTypVýchodíPopis
    recursivebooleovskávěrnýNačíst rekurzivně všechny potomky
    includecountsbooleovskáfalešnýZahrnout počet článků

    Příklad požadavku

    GET /api/v1/categories/1/children?recursive=true&includecounts=true

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": { "parentcategory": { "id": 1, "title": "Technologie", "displayname": "Technologie", "url": "/technologie", "image": { "url": "https://storage.googleapis.com/bucket/images/technologie.webp", "variants": { "banner": { "id": 10508, "originalimageid": 1260, "variantname": "banner", "imagename": "696ab4fbbb108technologie.webp", "width": 1500, "height": 635, "publicurl": "https://storage.googleapis.com/bucket/images/banner/technologie.webp", "createdat": "2026-01-16T22:00:29.000000Z", "updatedat": "2026-01-16T22:00:29.000000Z" }, "articleDetail": { "id": 10510, "originalimageid": 1260, "variantname": "articleDetail", "imagename": "696ab4fbbb108technologie.webp", "width": 1300, "height": 530, "publicurl": "https://storage.googleapis.com/bucket/images/articleDetail/technologie.webp", "createdat": "2026-01-16T22:00:29.000000Z", "updatedat": "2026-01-16T22:00:29.000000Z" } } } }, "children": [ { "id": 15, "title": "Software", "displayname": "Software", "url": "/technologie/software", "parentid": 1, "level": 1, "articlecount": 23, "image": { "url": "https://storage.googleapis.com/bucket/images/software.webp", "variants": { "banner": { "id": 10520, "originalimageid": 1265, "variantname": "banner", "imagename": "696ab4fbbb108software.webp", "width": 1500, "height": 635, "publicurl": "https://storage.googleapis.com/bucket/images/banner/software.webp", "createdat": "2026-01-16T22:01:15.000000Z", "updatedat": "2026-01-16T22:01:15.000000Z" }, "articleDetail": { "id": 10522, "originalimageid": 1265, "variantname": "articleDetail", "imagename": "696ab4fbbb108software.webp", "width": 1300, "height": 530, "publicurl": "https://storage.googleapis.com/bucket/images/articleDetail/software.webp", "createdat": "2026-01-16T22:01:15.000000Z", "updatedat": "2026-01-16T22:01:15.000000Z" } } }, "children": [ { "id": 23, "title": "Web Development", "displayname": "Webový vývoj", "url": "/technologie/software/web-development", "parentid": 15, "level": 2, "articlecount": 12, "children": [] }, { "id": 24, "title": "Mobile Development", "displayname": "Mobilní vývoj", "url": "/technologie/software/mobile-development", "parentid": 15, "level": 2, "articlecount": 8, "children": [] } ] }, { "id": 16, "title": "Hardware", "displayname": "Hardware", "url": "/technologie/hardware", "parentid": 1, "level": 1, "articlecount": 18, "children": [] } ], "meta": { "totalchildren": 4, "maxdepth": 2 } } }

    Odpověď (Chyba - 404 Nenalezeno)

    {

    "success": false, "error": "Not Found", "message": "Kategorie s ID 1 nebyla nalezena" }


    📂 7. Kategorie detailů

    Načtete kompletní informace o konkrétní kategorii.

    Koncový bod

    GET /api/v1/categories/{id}

    Parametry URL

    ParametrTypPopis
    idcelé čísloID kategorie

    Parametry dotazu

    ParametrTypVýchodíPopis
    includearticlesbooleovskáfalešnýZahrnout seznam článků
    articleslimitcelé číslo10Počet článků (pokud obsahují články=true)
    includebreadcrumbbooleovskávěrnýZahrnout strouhanku cestou

    Příklad požadavku

    GET /api/v1/categories/15?includearticles=true&articleslimit=5

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": { "id": 15, "title": "software", "displayname": "Software", "url": "/technologie/software", "parentid": 1, "level": 1, "isactive": "active", "ordercat": 10, "navigationid": 1, "toptext": "

    Úvodní text kategorie

    ", "bottomtext": "

    Závěrečný text kategorie

    ", "css
    cat": ".custom-cat { color: blue; }", "meta": { "title": "Software - Nejnovější zprávy", "description": "Vše o softwarovém vývoji", "keywords": "software, vývoj, programování", "catImg": 123 }, "createdat": "2025-01-10T09:00:00Z", "updatedat": "2026-01-15T10:00:00Z", "image": { "url": "https://storage.googleapis.com/bucket/images/category-banner.webp", "variants": { "banner": { "id": 10508, "originalimageid": 123, "variantname": "banner", "imagename": "696ab4fbbb108category-banner.webp", "width": 1500, "height": 635, "publicurl": "https://storage.googleapis.com/bucket/images/banner/category-banner.webp", "createdat": "2026-01-16T22:00:29.000000Z", "updatedat": "2026-01-16T22:00:29.000000Z" }, "articleDetail": { "id": 10510, "originalimageid": 123, "variantname": "articleDetail", "imagename": "696ab4fdf1b56category-banner.webp", "width": 1300, "height": 530, "publicurl": "https://storage.googleapis.com/bucket/images/articleDetail/category-banner.webp", "createdat": "2026-01-16T22:00:30.000000Z", "updatedat": "2026-01-16T22:00:30.000000Z" } } }, "breadcrumb": [ { "id": 1, "title": "Technologie", "url": "/technologie" }, { "id": 15, "title": "Software", "url": "/technologie/software" } ], "children": [ { "id": 23, "title": "Web Development", "displayname": "Webový vývoj", "url": "/technologie/software/web-development" }, { "id": 24, "title": "Mobile Development", "displayname": "Mobilní vývoj", "url": "/technologie/software/mobile-development" } ], "articles": [ { "id": 866, "title": "Název článku", "url": "/clanky/nazev-clanku", "publishat": "2026-01-15T10:00:00Z", "image": { "url": "https://.../thumbnail/article-thumb.webp" } } ], "stats": { "totalarticles": 23, "directarticles": 5, "childrencount": 2 } } }


    🏷️ 8. Seznam tagů

    Načtete všechny značky pro daný web. Podporuje filtrování podle kategorie štítku.

    Koncový bod

    GET /api/v1/tags

    Parametry dotazu

    ParametrTypVýchodíPopis
    sortřetězectitleŘazení: title, popular,newest
    categoryidcelé číslo—Nové v 1.6: Filtrovat tagy podle ID kategorie štítku
    categoryslugřetězec—Filtrovat tagy podle slugu kategorie štítku

    Příklad požadavku

    GET /api/v1/tags?sort=popular

    Authorization: Bearer YOURAPITOKEN

    GET /api/v1/tags?categoryid=5

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": [ { "id": 15, "name": "Umělá inteligence", "slug": "tag/umela-inteligence", "status": "active", "headline": "Vše o umělé inteligenci", "metatitle": "Umělá inteligence — články a novinky", "metadescription": "Přehled článků a produktů označených štítkem umělá inteligence.", "articlescount": 47, "productscount": 12, "itemscount": 59, "categories": [ { "id": 3, "title": "Technologie", "slug": "technologie", "isdefault": true } ] } ], "meta": { "total": 1 } }


    🏷️ 8a. Kategorie tagů (nové v 1.6)

    Načtete všechny kategorie štítků pro daný web. Slouží pro filtrování tagů a správu organizací.

    Koncový bod

    GET /api/v1/tag-categories

    Autorizace

    Vyžaduje rozsah tags:read.

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": [ { "id": 1, "title": "Obecné", "slug": "obecne", "isdefault": true, "order": 0, "tagscount": 24 }, { "id": 2, "title": "Technologie", "slug": "technologie", "isdefault": false, "order": 1, "tagscount": 15 } ], "meta": { "total": 2 } }

    | sort| řetězec | title| Řazení: title,articlescount , | | | řetězec | | Směr řazení: , | | | celé číslo | 50 | Počet tagů (max 200) | | | booleovský | pravda | Zahrnout počty článků | createdatorderascascdesclimitwithcounts

    Příklad požadavku

    GET /api/v1/tags?sort=articlescount&order=desc&limit=20

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": { "tags": [ { "id": 15, "name": "Umělá inteligence", "slug": "umela-inteligence", "articlescount": 47, "createdat": "2025-01-10T09:00:00Z", "updatedat": "2026-01-15T10:00:00Z" }, { "id": 23, "name": "Machine Learning", "slug": "machine-learning", "articlescount": 34, "createdat": "2025-02-15T10:00:00Z", "updatedat": "2026-01-14T08:00:00Z" }, { "id": 8, "title": "Python", "slug": "python", "articlescount": 28, "createdat": "2024-12-01T12:00:00Z", "updatedat": "2026-01-13T14:00:00Z" } ], "meta": { "total": 156, "returned": 20 } } }


    🏷️ 9. Detail tagu s články

    Načtete detail konkrétního tagu včetně seznamu článků.

    Koncový bod

    GET /api/v1/tags/{idorslug}

    Parametry URL

    ParametrTypPopis
    idorslugřetězecID nebo slug tagu (např. 15nebo umela-inteligence)

    Parametry dotazu

    ParametrTypVýchodíPopis
    includearticlesbooleovskávěrnýZahrnout seznam článků
    articleslimitcelé číslo20Počet článků (max. 100)
    articlespagecelé číslo1Články stránky
    sortarticlesřetězecpublishatŘazení článků: publishat, title,views

    Příklad požadavku

    GET /api/v1/tags/umela-inteligence?articleslimit=10

    Authorization: Bearer YOURAPITOKEN

    Odpověď (Úspěch - 200 OK)

    {

    "success": true, "data": { "tag": { "id": 15, "name": "Umělá inteligence", "slug": "umela-inteligence", "articlescount": 47, "createdat": "2025-01-10T09:00:00Z", "updatedat": "2026-01-15T10:00:00Z" }, "articles": [ { "id": 866, "title": "Nový článek o AI", "subtitle": "Podtitulek článku", "url": "https://domain.cz/clanky/novy-clanek-o-ai", "shorttext": "Krátký popis článku", "status": "active", "publishat": "2026-01-15T10:00:00Z", "image": { "url": "https://.../thumbnail/article-thumb.webp", "alt": "Alt text obrázku" }, "author": { "id": 42, "name": "John Doe", "avatar": "https://.../avatar/john-doe.webp" }, "categories": [ { "id": 15, "name": "Technologie", "url": "/technologie" } ] } ], "pagination": { "total": 47, "perpage": 10, "currentpage": 1, "lastpage": 5 } } }

    Odpověď (Chyba - 404 Nenalezeno)

    {

    "success": false, "error": "Not Found", "message": "Tag 'umela-inteligence' nebyl nalezen" }


    👤 10. Detail autora/uživatele

    Načte detail autora včetně jeho článků, bio, sociálních sítí a dalších informací.

    Koncový bod

    GET /api/v1/users/{id}

    Parametry URL

    ParametrTypPopis
    idcelé čísloID uživatele

    Parametry dotazu

    ParametrTypVýchodíPopis
    includearticlesbooleovskávěrnýZahrnout seznam článků autora
    articleslimitinteger10Počet článků (max 50)
    articlespageinteger1Stránka článků

    Request Example

    GET /api/v1/users/42?includearticles=true&articleslimit=5

    Authorization: Bearer YOURAPITOKEN

    Response (Success - 200 OK)

    {

    "success": true, "data": { "user": { "id": 42, "login": "johndoe", "name": "John Doe", "email": "[email protected]", "phone": "+420 123 456 789", "createdat": "2024-01-10T09:00:00Z", "updatedat": "2026-01-15T10:00:00Z" }, "details": { "fullname": "John Doe", "jobtitle": "Senior Developer", "companyname": "Tech Corp s.r.o.", "website": "https://johndoe.dev", "publicemail": "[email protected]", "biohtml": "

    Jsem vývojář s 10 lety zkušeností...

    ", "signature": "

    John Doe - Senior Developer

    ", "avatar": { "id": 456, "url": "https://.../avatar/john-doe.webp", "variants": { "usricosmall": { "url": "https://.../usricosmall/john-doe.webp", "width": 64, "height": 64 }, "usricomedium": { "url": "https://.../usricomedium/john-doe.webp", "width": 128, "height": 128 } } }, "cover": { "id": 789, "url": "https://.../cover/profile-banner.webp" }, "socials": [ { "platform": "twitter", "url": "https://twitter.com/johndoe", "handle": "@johndoe" }, { "platform": "linkedin", "url": "https://linkedin.com/in/johndoe" }, { "platform": "github", "url": "https://github.com/johndoe", "handle": "johndoe" } ], "links": [ { "title": "Osobní blog", "url": "https://blog.johndoe.dev" }, { "title": "Portfolio", "url": "https://portfolio.johndoe.dev" } ], "expertisetopics": [ "JavaScript", "React", "Node.js", "TypeScript" ], "extras": { "achievements": [ "Publikoval 50+ článků", "Speaker na 10 konferencích" ], "certifications": [ "AWS Certified Developer", "Google Cloud Professional" ] }, "allowpublicprofile": true, "displaysignature": true, "iscontributor": true, "preferredlanguage": "cs" }, "stats": { "totalarticles": 127, "totalviews": 45678, "articlesthismonth": 4, "membersince": "2024-01-10T09:00:00Z" }, "articles": [ { "id": 866, "title": "Nový článek", "url": "https://domain.cz/clanky/novy-clanek", "publishat": "2026-01-15T10:00:00Z", "image": { "url": "https://.../thumbnail/article-thumb.webp" }, "categories": [ { "id": 15, "name": "Technologie" } ] } ], "pagination": { "total": 127, "perpage": 5, "currentpage": 1, "lastpage": 26 } } }

    Response (Error - 404 Not Found)

    {

    "success": false, "error": "Not Found", "message": "Uživatel s ID 42 nebyl nalezen" }


    ⚙️ 11. Seznam parametrů pro content type

    Načte definice parametrů pro daný typ obsahu (articles, products, atd.).

    Endpoint

    GET /api/v1/parameters

    Query Parameters

    ParametrTypPovinnéPopis
    contenttypestring✅Typ obsahu: article, catalogue, atd.

    Request Example

    GET /api/v1/parameters?contenttype=article

    Authorization: Bearer YOURAPITOKEN

    Response (Success - 200 OK)

    {

    "success": true, "data": { "parameters": [ { "id": 1, "name": "difficulty", "datatype": "enum", "settings": { "label": "Obtížnost", "options": ["začátečník", "středně pokročilý", "pokročilý", "expert"], "default": "středně pokročilý" }, "isrequired": false, "isfilterable": true, "issearchable": false, "issortable": true, "sortorder": 1 }, { "id": 2, "name": "readingtime", "datatype": "integer", "settings": { "label": "Doba čtení (minuty)", "min": 1, "max": 120, "unit": "min" }, "isrequired": false, "isfilterable": true, "issearchable": false, "issortable": true, "sortorder": 2 }, { "id": 3, "name": "videourl", "datatype": "youtubeurl", "settings": { "label": "YouTube video", "placeholder": "https://youtube.com/watch?v=..." }, "isrequired": false, "isfilterable": false, "issearchable": false, "issortable": false, "sortorder": 3 }, { "id": 4, "name": "maplocation", "datatype": "mapurl", "settings": { "label": "Mapa lokace", "placeholder": "https://maps.google.com/..." }, "isrequired": false, "isfilterable": false, "issearchable": false, "issortable": false, "sortorder": 4 }, { "id": 5, "name": "attachment", "datatype": "file", "settings": { "label": "Příloha ke stažení", "allowedtypes": ["pdf", "docx", "zip"], "maxsizemb": 10 }, "isrequired": false, "isfilterable": false, "issearchable": false, "issortable": false, "sortorder": 5 }, { "id": 6, "name": "featuredimage", "datatype": "image", "settings": { "label": "Featured obrázek", "allowedtypes": ["jpg", "png", "webp"], "maxsizemb": 5 }, "isrequired": false, "isfilterable": false, "issearchable": false, "issortable": false, "sortorder": 6 } ], "meta": { "contenttype": "article", "totalparameters": 6 } } }

    Podporované datatype hodnoty

    • text - Dlouhý text (WYSIWYG editor)
    • string - Krátký text (1 řádek)
    • htmltext - HTML text
    • integer - Celé číslo
    • float - Desetinné číslo
    • boolean - Ano/Ne
    • date - Datum
    • time - Čas
    • datetime - Datum a čas
    • enum - Výběr z možností
    • file - Soubor
    • image - Obrázek
    • url - URL adresa
    • youtubeurl - YouTube URL
    • mapurl - URL mapy (Google Maps, atd.)

    🔒 Bezpečnostní mechanismy

    1. Token-based autentizace

    Každý token je vázán na konkrétní siteid:

    -- Příklad ověření při každém requestu

    SELECT siteid, permissions FROM apitokens WHERE token = :token AND isactive = 1 AND (expiresat IS NULL OR expiresat > NOW())

    2. Automatická filtrace dat podle siteid

    Všechny databázové dotazy musí obsahovat filtr podle siteid:

    // ❌ ŠPATNĚ - bez filtrace

    $articles = Article::all();

    // ✅ SPRÁVNĚ - s filtrací podle siteid z tokenu $articles = Article::where('siteid', $SERVER['APISITEID'])->get();

    3. Validace oprávnění

    Před každou operací se kontrolují oprávnění tokenu:

    function requirePermission($token, $permission) {

    $tokenRecord = ApiToken::where('token', $token)->first(); $permissions = jsondecode($tokenRecord->permissions, true);

    if (!inarray($permission, $permissions)) { return ['error' => 'Insufficient permissions', 'code' => 403]; }

    return true; }

    4. Rate limiting

    Omezení počtu requestů z jednoho tokenu:

    • Read operace: 1000 requestů / hodinu
    • Write operace: 100 requestů / hodinu
    HTTP/1.1 429 Too Many Requests

    X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1642248000

    { "error": "Rate Limit Exceeded", "message": "Překročen limit 1000 requestů za hodinu", "retryafter": 3600 }

    5. HTTPS Only

    API musí běžet pouze přes HTTPS.

    6. Logging všech přístupů

    Každý API request se loguje:

    CREATE TABLE apilogs (

    id INT PRIMARY KEY AUTOINCREMENT, tokenid INT, siteid INT, method VARCHAR(10), endpoint VARCHAR(255), ipaddress VARCHAR(45), useragent TEXT, responsecode INT, responsetimems INT, createdat TIMESTAMP );


    📊 Databázové migrace

    Vytvoření tabulky apitokens

    CREATE TABLE apitokens (

    id INT(11) UNSIGNED NOT NULL AUTOINCREMENT, siteid INT(11) UNSIGNED NOT NULL, token VARCHAR(64) NOT NULL, name VARCHAR(255) NOT NULL, permissions JSON NOT NULL COMMENT '["read","write","delete"]', expiresat DATETIME NULL DEFAULT NULL, lastusedat DATETIME NULL DEFAULT NULL, isactive TINYINT(1) NOT NULL DEFAULT 1, createdat TIMESTAMP NOT NULL DEFAULT CURRENTTIMESTAMP, updatedat TIMESTAMP NOT NULL DEFAULT CURRENTTIMESTAMP ON UPDATE CURRENTTIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uniquetoken (token), KEY idxsiteid (siteid), KEY idxisactive (isactive), KEY idxexpiresat (expiresat) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4unicodeci;

    ⚠️ ZASTARALÉ: Rozšíření ENUM pro status (NEIMPLEMENTOVÁNO)

    VAROVÁNÍ: Tato sekce je zastaralá a neodpovídá skutečné implementaci. Aktuální kód (Article.php, Product.php, crony) používá pouze tyto statusy: development, active, suspend.-- ❌ NEPOUŽÍVAT - Tato migrace není implementována v kódu!

    -- Hodnoty 'hidden' a 'deleted' NEJSOU podporovány v současné implementaci ALTER TABLE articles MODIFY status ENUM('development','active','suspend','hidden','deleted') NOT NULL DEFAULT 'development';

    -- Poznámka: Soft delete je preferováno přes deletedat timestamp, -- nikoli přes status='deleted'

    Vytvoření tabulky apilogs

    CREATE TABLE apilogs (

    id BIGINT(20) UNSIGNED NOT NULL AUTOINCREMENT, tokenid INT(11) UNSIGNED NULL DEFAULT NULL, siteid INT(11) UNSIGNED NOT NULL, method VARCHAR(10) NOT NULL, endpoint VARCHAR(255) NOT NULL, queryparams TEXT NULL, requestbody TEXT NULL, ipaddress VARCHAR(45) NOT NULL, useragent TEXT NULL, responsecode INT(3) NOT NULL, responsetimems INT(11) NOT NULL, errormessage TEXT NULL, createdat TIMESTAMP NOT NULL DEFAULT CURRENTTIMESTAMP, PRIMARY KEY (id), KEY idxtokenid (tokenid), KEY idxsiteid (siteid), KEY idxcreatedat (createdat), KEY idxresponsecode (responsecode) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4unicodeci;


    🧪 Testování API

    cURL Příklady

    Získání detailu článku

    curl -X GET "https://yourdomain.com/api/v1/articles/866" \

    -H "Authorization: Bearer YOURAPITOKEN" \ -H "Accept: application/json"

    Vytvoření nového článku

    curl -X POST "https://yourdomain.com/api/v1/articles" \

    -H "Authorization: Bearer YOURAPITOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Test článek", "body": "

    Obsah článku

    ", "status": "active", "userid": 42, "url": "/test-clanek", "categories": [15] }'

    Vyhledávání

    curl -X GET "https://yourdomain.com/api/v1/articles/search?q=umělá+inteligence&limit=5" \

    -H "Authorization: Bearer YOURAPITOKEN" \ -H "Accept: application/json"

    Postman Collection

    Doporučujeme vytvořit Postman Collection s příklady všech endpointů.


    🚀 Implementační kontrolní seznam

    Historický checklist byl archivován. Tento dokument je kanonická specifikace implementovaných endpointů a kontraktů.


    📖 Souhrn všech 32 hlavních API endpointů

    #MetodaEndpointPopisOprávnění
    ČLÁNKY
    1POST/api/v1/articlesVytvoření nového článkuarticles:write
    2GET/api/v1/articles/{id}Detail konkrétního článkuarticles:read
    3GET/api/v1/articlesSeznam článků s filtrováním a paginacíarticles:read
    4GET/api/v1/articles/searchFulltextové vyhledávání v článcícharticles:read
    KATEGORIE
    5GET/api/v1/categories/treeHierarchický strom všech kategoriícategories:read
    6GET/api/v1/categories/{id}/childrenPodkategorie konkrétní kategoriecategories:read
    7GET/api/v1/categories/{id}Detail kategorie s článkycategories:read
    TAGY
    8GET/api/v1/tagsSeznam všech tagůtags:read
    9GET/api/v1/tags/{idorslug}Detail tagu s článkytags:read
    UŽIVATELÉ
    10GET/api/v1/users/{id}Profil autora s články a sociálními sítěmiusers:read
    DATA FEEDY
    10aGET/api/v1/feeds/{key}Pojmenovaný feed položek pro aktuální webper-feed
    PRODUKTY (READ & WRITE)
    11GET/api/v1/productsSeznam produktů s pokročilým filtrovánímproducts:read
    12GET/api/v1/products/{id}Detail produktu s kompletními datyproducts:read
    13POST/api/v1/productsVytvoření produktuproducts:write
    14PUT/api/v1/products/{id}Aktualizace produktuproducts:write
    15DELETE/api/v1/products/{id}Soft-delete produktuproducts:write
    E-SHOP (PRODUKTY)
    16GET/api/v1/eshop/productsSeznam prodejných produktůeshop:read
    17GET/api/v1/eshop/products/{id}Detail prodejného produktueshop:read
    18GET/api/v1/eshop/products/{id}/variantsVarianty produktueshop:read
    19GET/api/v1/eshop/products/{id}/availabilityDostupnost produktueshop:read
    E-SHOP (KOŠÍK)
    20GET/api/v1/eshop/cartNačtení aktivního košíkueshop:read
    21POST/api/v1/eshop/cart/itemsPřidání položky do košíkueshop:write
    22PATCH/api/v1/eshop/cart/items/{id}Změna množství položkyeshop:write
    23DELETE/api/v1/eshop/cart/items/{id}Odebrání položky z košíkueshop:write
    E-SHOP (KUPÓNY)
    24POST/api/v1/eshop/cart/couponAplikace kupónu na košíkeshop:write
    25DELETE/api/v1/eshop/cart/couponOdebrání kupónu z košíkueshop:write
    E-SHOP (CHECKOUT A OBJEDNÁVKY)
    26POST/api/v1/eshop/checkoutVytvoření objednávky z aktivního košíkueshop:write
    27GET/api/v1/eshop/orders/{id}Detail objednávky v rámci siteideshop:read
    E-SHOP (DOPRAVA A PLATBY)
    28GET/api/v1/eshop/shipping-methodsDostupné dopravy pro site/carteshop:read
    29GET/api/v1/eshop/payment-methodsDostupné platby (volitelně dle dopravy)eshop:read
    E-SHOP (TRANSAKCE A WEBHOOKY)
    30GET/api/v1/eshop/orders/{id}/transactionsVýpis transakcí k objednávceeshop:read
    31POST/api/v1/eshop/orders/{id}/transactionsZahájení platební transakceeshop:write
    32GET/api/v1/eshop/transactions/{id}Detail transakceeshop:read
    33POST/api/v1/eshop/webhooks/{provider}Webhook od platební brányveřejný
    34POST/api/v1/eshop/exportSpuštění exportního běhueshop:write
    PARAMETRY
    35GET/api/v1/parametersDefinice parametrů pro content typeparameters:read

    Struktura response pro všechny endpointy

    Všechny endpointy vrací jednotný formát:

    Success (200 OK):{

    "success": true, "data": { / data specifická pro endpoint / }, "meta": { / metadata jako pagination, total counts atd. / } }

    Error (4xx/5xx):{

    "success": false, "error": "Error Type", "message": "Popis chyby", "code": 400 }

    Poznámky k bezpečnosti

    Všechny endpointy:
    • Vyžadují platný API token v hlavičce
    • Automaticky filtrují data podle siteid z tokenu
    • Logují všechny requesty do apilogs
    • Respektují rate limity
    • Vrací pouze veřejná data nebo data dle oprávnění
    Chráněné informace:
    • userdetails.phone - viditelné pouze pro admin
    • userdetails.address - viditelné pouze pro admin
    • articles.status='development' - viditelné pouze pro admin
    • categories.isactive='development' - viditelné pouze pro admin

    � 10. Products API (READ & WRITE)

    API pro produkty poskytuje čtení i zápis dat. K dispozici jsou endpointy pro seznam, detail, vytvoření, aktualizaci a soft-delete produktu.

    🎯 Klíčové vlastnosti Products API

    ✅ Kompletní data produktů včetně:

    • Dynamických produktových polí (product fields)
    • Hodnot parametrů (parameters)
    • Obrázků s variantami
    • Tagů a autorských informací
    • Kategorií

    ✅ Flexibilní filtrování:

    • Podle parametrů s operátory (eq, gte, lte, like)
    • Podle produktových polí s operátory
    • Podle kategorií a statusu
    • Fulltextové vyhledávání

    ✅ Optimalizované dotazy:

    • Eager loading všech vztahů
    • Automatické mapování datových typů
    • Podpora 15 různých value sloupců

    10.1 GET /products

    Popis: Načte seznam produktů s možností filtrování. Autorizace: Vyžaduje Authorization: Bearer {token} s oprávněním products:read nebo
    Query parametry:
    ParametrTypPovinnýPopisPříklad
    statusstringNeFiltr podle statusuactive, development, suspend
    categoryidintegerNeID kategorie5
    producttypeidintegerNeID typu produktu3
    searchstringNeFulltextové vyhledávání v title, subtitle, bodynotebook
    pageintegerNeČíslo stránky pro paginaci2
    perpageintegerNePočet produktů na stránku (max 100)20
    filters[parameter{id}][{operator}]mixedNeFiltrování podle parametrufilters[parameter5][eq]=red
    filters[field{id}][{operator}]mixedNeFiltrování podle produktového polefilters[field8][gte]=100
    Podporované operátory pro filtry:
    • eq - Rovná se (=)
    • gte - Větší nebo rovno (>=)
    • lte - Menší nebo rovno (<=)
    • like - Obsahuje (LIKE %value%)
    Příklady filtrování:

    Produkty s barvou "červená" (parameterid=5)

    GET /api/v1/products?filters[parameter5][eq]=červená

    Produkty s hmotností >= 100g (parameterid=12)

    GET /api/v1/products?filters[parameter12][gte]=100

    Produkty s cenou <= 999 Kč (fieldid=8)

    GET /api/v1/products?filters[field8][lte]=999

    Produkty obsahující "nike" v poli brand (fieldid=3)

    GET /api/v1/products?filters[field3][like]=nike

    Kombinace filtrů

    GET /api/v1/products?filters[parameter5][eq]=červená&filters[field8][lte]=999&categoryid=10

    Response 200 OK:{

    "success": true, "data": [ { "id": 123, "title": "Běžecké boty Nike Air Zoom", "subtitle": "Profesionální boty pro běžce", "body": "

    Detailní popis produktu...

    ", "status": "active", "publishat": "2025-12-01T10:00:00Z", "publishendat": null, "manualupdateat": "2026-01-10T14:30:00Z", "createdat": "2025-11-20T09:00:00Z", "updatedat": "2026-01-15T16:45:00Z", "siteid": 7, "producttypeid": 5, "userid": 42, "url": "http://example.com/katalog/boty/nike-air-zoom.html",

    "producttype": { "id": 5, "name": "Sportovní obuv", "description": "Šablona pro sportovní obuv" },

    "typefields": [ { "id": 1, "fieldname": "material", "datatype": "string", "settings": { "label": "Materiál", "helptext": "Hlavní materiál svršku" }, "isrequired": true, "sortorder": 1 }, { "id": 2, "fieldname": "size", "datatype": "enum", "settings": { "label": "Velikost", "options": ["39", "40", "41", "42", "43", "44", "45"] }, "isrequired": true, "sortorder": 2 } ],

    "fieldvalues": [ { "fieldid": 1, "value": "Syntetická kůže" }, { "fieldid": 2, "value": "42" } ],

    "parameters": [ { "id": 5, "name": "color", "datatype": "string", "value": "červená", "isfilterable": true, "issearchable": false }, { "id": 12, "name": "weight", "datatype": "integer", "value": 250, "isfilterable": true, "issearchable": false } ],

    "articleDetail": { "id": 456, "filename": "nike-air-zoom-main.webp", "publicurl": "https://storage.googleapis.com/.../nike-air-zoom-main.webp", "alt": "Nike Air Zoom - hlavní foto", "title": "Nike Air Zoom", "order": 0, "variants": { "banner": { "id": 457, "publicurl": "https://storage.googleapis.com/.../banner/nike-air-zoom-main.webp", "width": 1500, "height": 635 }, "articleDetail": { "id": 458, "publicurl": "https://storage.googleapis.com/.../articleDetail/nike-air-zoom-main.webp", "width": 800, "height": 600 }, "galleryThumbnail": { "id": 459, "publicurl": "https://storage.googleapis.com/.../galleryThumbnail/nike-air-zoom-main.webp", "width": 300, "height": 200 } } },

    "gallery": [ { "id": 789, "filename": "nike-air-zoom-side.webp", "publicurl": "https://storage.googleapis.com/.../nike-air-zoom-side.webp", "order": 1, "variants": { "gallery": { "id": 790, "publicurl": "https://storage.googleapis.com/.../gallery/nike-air-zoom-side.webp", "width": 800, "height": 600 } } } ],

    "categories": [ { "id": 15, "title": "boty", "displayname": "Obuv", "url": "/katalog/boty" }, { "id": 23, "title": "bezecke-boty", "displayname": "Běžecké boty", "url": "/katalog/boty/bezecke" } ],

    "tags": [ { "id": 8, "name": "běh", "slug": "beh" }, { "id": 15, "name": "nike", "slug": "nike" } ],

    "author": { "id": 42, "email": "[email protected]", "details": { "fullname": "John Doe", "avatar": { "id": 123, "publicurl": "https://storage.googleapis.com/.../avatars/john.webp", "alt": "John Doe", "title": "John Doe" } } } } ], "meta": { "currentpage": 1, "perpage": 20, "total": 156, "lastpage": 8 } }

    Chybové odpovědi: 401 Unauthorized - Chybí nebo je neplatný API token:{

    "success": false, "error": "Unauthorized", "message": "API token je neplatný nebo chybí", "code": 401 }

    400 Bad Request - Neplatné parametry:{

    "success": false, "error": "Bad Request", "message": "Neplatný formát filtru", "code": 400 }

    429 Too Many Requests - Překročen rate limit:{

    "success": false, "error": "Too Many Requests", "message": "Překročen limit požadavků. Zkuste to znovu za 3600 sekund", "code": 429 }

    cURL příklad:

    Základní request

    curl -X GET "https://example.com/api/v1/products?categoryid=5&status=active" \ -H "Authorization: Bearer YOURAPITOKEN"

    S filtrováním podle parametrů

    curl -X GET "https://example.com/api/v1/products?filters[parameter5][eq]=červená&filters[parameter12][gte]=100" \ -H "Authorization: Bearer YOURAPITOKEN"

    S fulltextovým vyhledáváním

    curl -X GET "https://example.com/api/v1/products?search=nike&categoryid=10" \ -H "Authorization: Bearer YOURAPITOKEN"

    S paginací

    curl -X GET "https://example.com/api/v1/products?page=2&perpage=50" \ -H "Authorization: Bearer YOURAPITOKEN"


    10.2 GET /products/{id}

    Popis: Načte detail konkrétního produktu. Autorizace: Vyžaduje Authorization: Bearer {token} s oprávněním products:read nebo Path parametry:
    ParametrTypPovinnýPopis
    idintegerAnoID produktu
    Response 200 OK:
    {

    "success": true, "data": { "id": 123, "title": "Běžecké boty Nike Air Zoom", "subtitle": "Profesionální boty pro běžce", "body": "

    Detailní popis produktu s HTML formátováním...

    ", "status": "active", "publishat": "2025-12-01T10:00:00Z", "publishendat": null, "manualupdateat": "2026-01-10T14:30:00Z", "createdat": "2025-11-20T09:00:00Z", "updatedat": "2026-01-15T16:45:00Z", "siteid": 7, "producttypeid": 5, "userid": 42, "url": "http://example.com/katalog/boty/nike-air-zoom.html",

    "producttype": { "id": 5, "name": "Sportovní obuv", "description": "Šablona pro sportovní obuv" },

    "typefields": [ { "id": 1, "fieldname": "material", "datatype": "string", "settings": { "label": "Materiál", "helptext": "Hlavní materiál svršku", "maxlength": 255 }, "isrequired": true, "sortorder": 1 }, { "id": 2, "fieldname": "size", "datatype": "enum", "settings": { "label": "Velikost", "options": ["39", "40", "41", "42", "43", "44", "45"] }, "isrequired": true, "sortorder": 2 }, { "id": 3, "fieldname": "brand", "datatype": "string", "settings": { "label": "Značka", "helptext": "Výrobce produktu" }, "isrequired": false, "sortorder": 3 } ],

    "fieldvalues": [ { "fieldid": 1, "value": "Syntetická kůže" }, { "fieldid": 2, "value": "42" }, { "fieldid": 3, "value": "Nike" } ],

    "parameters": [ { "id": 5, "name": "color", "datatype": "string", "value": "červená", "isfilterable": true, "issearchable": false }, { "id": 12, "name": "weight", "datatype": "integer", "value": 250, "isfilterable": true, "issearchable": false }, { "id": 18, "name": "waterproof", "datatype": "boolean", "value": true, "isfilterable": true, "issearchable": false } ],

    "images": { "articles": { "publicurl": "https://storage.googleapis.com/.../nike-air-zoom-main.webp", "width": 1000, "height": 486, "imagename": "nike-air-zoom-main.webp", "alt": "Nike Air Zoom - hlavní foto", "title": "Nike Air Zoom", "variants": { "articles": { "publicurl": "https://storage.googleapis.com/.../articles/nike-air-zoom-main.webp", "width": 1000, "height": 486 }, "banner": { "publicurl": "https://storage.googleapis.com/.../banner/nike-air-zoom-main.webp", "width": 1500, "height": 635 } } }, "articleDetail": { "publicurl": "https://storage.googleapis.com/.../nike-air-zoom-detail.webp", "width": 1300, "height": 530, "imagename": "nike-air-zoom-detail.webp", "alt": "Nike Air Zoom - detail produktu", "title": "Nike Air Zoom Detail", "variants": { "articleDetail": { "publicurl": "https://storage.googleapis.com/.../articleDetail/nike-air-zoom-detail.webp", "width": 1300, "height": 530 } } }, "gallery": [ { "publicurl": "https://storage.googleapis.com/.../nike-air-zoom-side.webp", "width": 500, "height": 414, "imagename": "nike-air-zoom-side.webp", "alt": "Nike Air Zoom - pohled ze strany", "title": "Pohled ze strany", "order": 1, "variants": { "gallery": { "publicurl": "https://storage.googleapis.com/.../gallery/nike-air-zoom-side.webp", "width": 1200, "height": 800 }, "galleryThumbnail": { "publicurl": "https://storage.googleapis.com/.../galleryThumbnail/nike-air-zoom-side.webp", "width": 500, "height": 414 } } }, { "publicurl": "https://storage.googleapis.com/.../nike-air-zoom-sole.webp", "width": 500, "height": 414, "imagename": "nike-air-zoom-sole.webp", "alt": "Nike Air Zoom - detail podrážky", "title": "Detail podrážky", "order": 2, "variants": { "gallery": { "publicurl": "https://storage.googleapis.com/.../gallery/nike-air-zoom-sole.webp", "width": 1200, "height": 800 }, "galleryThumbnail": { "publicurl": "https://storage.googleapis.com/.../galleryThumbnail/nike-air-zoom-sole.webp", "width": 500, "height": 414 } } } ] },

    "categories": [ { "id": 15, "title": "boty", "displayname": "Obuv", "url": "/katalog/boty" }, { "id": 23, "title": "bezecke-boty", "displayname": "Běžecké boty", "url": "/katalog/boty/bezecke" } ],

    "tags": [ { "id": 8, "name": "běh", "slug": "beh" }, { "id": 15, "name": "nike", "slug": "nike" }, { "id": 22, "name": "sport", "slug": "sport" } ],

    "author": { "id": 42, "email": "[email protected]", "details": { "fullname": "John Doe", "bio": "Produktový manažer se zaměřením na sportovní výbavu", "avatar": { "id": 123, "publicurl": "https://storage.googleapis.com/.../avatars/john.webp", "alt": "John Doe", "title": "John Doe" } } } } }

    Poznámka k struktuře images:
    • images.articles: Hlavní obrázek pro seznam produktů (pochází z pivot variant: articles nebo banner)
    • images.articleDetail: Obrázek pro detail produktu (pochází z pivot variant: articleDetail)
    • images.gallery: Pole galerie obrázků s polem order (pochází z pivot variant: gallery)
    • Varianty obsahují pouze: publicurl, width, height (bez id, variantname, atd.)
    • Vynechány jsou uživatelské ikony: usricosmall, usricobig
    Chybové odpovědi: 404 Not Found - Produkt neexistuje:{

    "success": false, "error": "Not Found", "message": "Produkt s ID 123 nebyl nalezen", "code": 404 }

    401 Unauthorized - Chybí nebo je neplatný API token:{

    "success": false, "error": "Unauthorized", "message": "API token je neplatný nebo chybí", "code": 401 }

    403 Forbidden - Produkt patří k jinému webu:{

    "success": false, "error": "Forbidden", "message": "Nemáte oprávnění přistupovat k tomuto produktu", "code": 403 }

    cURL příklad:curl -X GET "https://example.com/api/v1/products/123" \

    -H "Authorization: Bearer YOURAPITOKEN"


    10.2A POST /products

    Popis: Vytvoří nový produkt pro aktuální siteid. Autorizace: Vyžaduje Authorization: Bearer {token} s oprávněním products:write nebo Povinná pole v request body:
    • title (string)
    • producttypeid (integer)
    Volitelná pole:
    • subtitle, body, status, meta, categoryids
    Response 201 Created:
    • Vrací vytvořený produkt ve stejné struktuře jako detail (GET /api/v1/products/{id}).

    10.2B PUT /products/{id}

    Popis: Aktualizuje existující produkt. Autorizace: Vyžaduje Authorization: Bearer {token} s oprávněním products:write nebo Response 200 OK:
    • Vrací aktualizovaný produkt ve stejné struktuře jako detail (GET /api/v1/products/{id}).

    10.2C DELETE /products/{id}

    Popis: Provede soft-delete produktu (status=deleted, deletedat). Autorizace: Vyžaduje Authorization: Bearer {token} s oprávněním products:write nebo Response 200 OK:
    • Vrací potvrzení s message a deletedat.

    10.3 Datové typy produktových polí a parametrů

    Products API podporuje 15 datových typů pro typefields (produktová pole) a parameters:

    Data TypePopisPříklad hodnotySloupec v DB
    stringKrátký text"Nike"valuestring
    textDelší text"Popis produktu..."valuetext
    htmltextHTML formátovaný text"

    Text

    "
    valuehtmltext / valuetext
    integerCelé číslo42valueinteger
    floatDesetinné číslo99.99valuefloat
    booleanAno/Ne hodnotatrue nebo falsevalueboolean
    dateDatum"2026-01-15"valuedate
    timeČas"14:30:00"valuetime
    datetimeDatum a čas"2026-01-15T14:30:00Z"valuedatetime
    enumVýběr z možností"42" (z options: ["39","40","41","42"])valueenum
    urlURL adresa"https://example.com"valueurl
    youtubeurlURL na YouTube"https://youtu.be/..."valueyoutubeurl / valueurl
    mapurlURL mapy"https://maps.google.com/..."valuemapurl / valueurl
    fileSoubor (PDF, DOCX atd.){"fileid": 456}valuefileid
    imageObrázek{"imageid": 789}valueimageid
    Poznámky k datovým typům:
    • enum: Možnosti jsou definovány v settings.options pole typu
    • file: Vrací objekt s fileid, kompletní data souboru lze získat přes Files API
    • image: Vrací objekt s imageid, kompletní data obrázku jsou dostupná přes image resource
    • boolean: Vrací true/false, v DB uloženo jako 1/0
    • date/time/datetime: Formát ISO 8601

    10.4 Implementační poznámky

    Optimalizace dotazů:
    • Používá eager loading všech vztahů (->with([...]))
    • Automatické mapování datatype na správný value sloupec
    • Podpora whereHas() pro filtrování podle vztahů
    Filtrování:
    • Filtr filters[parameter{id}][{operator}] pro parametry
    • Filtr filters[field{id}][{operator}] pro produktová pole
    • Operátory: eq (=), gte (>=), lte (<=), like (LIKE %value%)
    Bezpečnost:
    • Automatická filtrace podle siteid z API tokenu
    • Pouze produkty se statusem active jsou veřejné (ostatní jen pro admin)
    • Rate limiting: 1000 requestů/hodina pro read operace
    Výkon:
    • Doporučeno vytvořit composite indexy na:

    - parametervalues(parameterid, contenttype, contentid) - productfieldvalues(productid, fieldid)

    • Eager loading zabraňuje N+1 dotazům

    10.5 Datové struktury a schémata

    SimplifiedArticleResource

    Zjednodušená struktura článku používaná v poli readalso v detailu článku. Obsahuje pouze základní informace potřebné pro zobrazení náhledu článku.

    Pole:{

    "id": 456, "title": "Název článku", "slug": "blog/nazev-clanku", "subtitle": "Podtitul článku", "shorttext": "Krátký popisek článku", "snippet": "Snippet článku", "author": { "id": 42, "email": "[email protected]", "fullname": "John Doe", "bio": "Bio autora", "jobtitle": "Senior Developer", "company": "Tech Corp", "avatar": { "usricosmall": { "id": 123, "publicurl": "https://storage.googleapis.com/.../usricosmall/avatar.webp", "width": 48, "height": 48 }, "usricobig": { "id": 124, "publicurl": "https://storage.googleapis.com/.../usricobig/avatar.webp", "width": 200, "height": 200 } }, "sociallinks": [...], "professionallinks": [...] }, "tags": [ { "id": 5, "name": "php", "slug": "tag/php" } ], "images": { "articles": { "id": 789, "filename": "article-thumb.webp", "publicurl": "https://storage.googleapis.com/.../article-thumb.webp", "alt": "Název obrázku", "title": "Název obrázku", "variants": { "articles": { "publicurl": "https://storage.googleapis.com/.../articles/thumb.webp", "width": 1000, "height": 486 }, "articleDetail": { "publicurl": "https://storage.googleapis.com/.../articleDetail/thumb.webp", "width": 1300, "height": 530 }, "galleryThumbnail": { "publicurl": "https://storage.googleapis.com/.../galleryThumbnail/thumb.webp", "width": 500, "height": 414 }, "imglistarticles": { "publicurl": "https://storage.googleapis.com/.../imglistarticles/thumb.webp", "width": 1000, "height": 486 }, "imgdetailarticleheader": { "publicurl": "https://storage.googleapis.com/.../imgdetailarticleheader/thumb.webp", "width": 1300, "height": 530 }, "imgdetailarticlepost": { "publicurl": "https://storage.googleapis.com/.../imgdetailarticlepost/thumb.webp", "width": 1920, "height": 1282 }, "imgrecentarticles": { "publicurl": "https://storage.googleapis.com/.../imgrecentarticles/thumb.webp", "width": 339, "height": 165 }, "imgrelatedpost": { "publicurl": "https://storage.googleapis.com/.../imgrelatedpost/thumb.webp", "width": 360, "height": 214 }, "imgctvercovy": { "publicurl": "https://storage.googleapis.com/.../imgctvercovy/thumb.webp", "width": 400, "height": 400 } } } } }

    Rozdíly oproti plné ArticleResource:
    PoleSimplifiedArticleArticleResource
    body❌ Není✅ HTML obsah
    meta❌ Není✅ SEO + mainCatid
    files❌ Není✅ Přílohy
    parameters❌ Není✅ Custom parametry
    categories❌ Není✅ Kategorie
    images.articleDetail❌ Není✅ Detail variant
    images.gallery❌ Není✅ Galerie
    images.articles✅ S 9 variantami✅ Se všemi 17 variantami
    author✅ Kompletní UserDetails✅ Kompletní
    tags✅ Kompletní✅ Kompletní
    ⚠️ DŮLEŽITÉ - Filtrované varianty obrázků v SimplifiedArticle:

    Z výkonnostních důvodů jsou z readalso a relatedarticles odstraněny následující varianty:

    • ❌ banner, bannerl, banner2l, banner2m, banner2s (bannery pro různé rozlišení)
    • ❌ usricosmall, usricobig (ikony uživatelů - jsou pouze v author.avatar)
    • ❌ imghomebanner (homepage banner)
    ✅ Dostupné varianty obrázků (9 typů):
  • articles (1000×486 px) - seznam článků
  • articleDetail (1300×530 px) - detail článku header
  • galleryThumbnail (500×414 px) - miniatury galerie
  • imglistarticles (1000×486 px) - seznam článků
  • imgdetailarticleheader (1300×530 px) - detail header
  • imgdetailarticlepost (1920×1282 px) - detail velký obrázek
  • imgrecentarticles (339×165 px) - nedávné články
  • imgrelatedpost (360×214 px) - související příspěvky
  • imgctvercovy (400×400 px) - čtvercový formát
  • Použití:
    • Pole readalso v detailu článku (GET /api/v1/articles/{idOrSlug})
    • Pole relatedarticles v detailu článku (GET /api/v1/articles/{idOrSlug})
    • Maximálně 6 náhodných článků ze stejné kategorie (mainCatid) pro readalso
    • Varianty optimalizovány pro rychlejší response a menší payload (~47% redukce)

    �📞 Podpora

    Pro dotazy k API kontaktujte:

    • Email: [email protected]
    • Dokumentace: https://docs.yourdomain.com/api
    • Status: https://status.yourdomain.com
    • GitHub Issues: https://github.com/yourdomain/api-issues

    Změnový log

    Verze 1.8 (23. března 2026) - E-shop Fáze 3 (kupóny)
    • ✅ Přidány e-shop coupon endpointy: POST /api/v1/eshop/cart/coupon, DELETE /api/v1/eshop/cart/coupon
    • ✅ Přidána coupon vrstva pro košík (eshopcoupons, eshopcouponproducts, eshopcouponredemptions)
    • ✅ Rozšířena cart response o coupon, discounttotalwithvat, grandtotalwithvat
    • ✅ Aktualizován počet endpointů na 64 (včetně 10 e-shop endpointů)
    Verze 1.7 (23. března 2026) - E-shop Fáze 2 (košík a rezervace)
    • ✅ Přidány e-shop cart endpointy: GET /api/v1/eshop/cart, POST /api/v1/eshop/cart/items, PATCH /api/v1/eshop/cart/items/{id}, DELETE /api/v1/eshop/cart/items/{id}
    • ✅ Dokumentace rozšířena o požadované scope pro košík (eshop:read, eshop:write)
    • ✅ Aktualizován počet endpointů na 62 (včetně 8 e-shop endpointů)
    Verze 1.5 (15. února 2026) - Kompletní systematická verifikace
    • ✅ KRITICKÁ OPRAVA #1: body je VŽDY povinné (verifikováno v ArticleController.php:242) - odstraněna zavádějící poznámka o podmíněné povinnosti
    • ✅ KRITICKÁ OPRAVA #2: categoryid/categoryids fallback logika správně zdokumentována - backend vezme categoryids[0] pokud categoryid chybí (ArticleController.php:247-254)
    • ✅ KRITICKÁ OPRAVA #3: Odstraněn neexistující status hidden ze všech příkladů a dokumentace - skutečné statusy jsou development, active, suspend, schedule (verifikováno v Article.php, Product.php, cronech)
    • ✅ KRITICKÁ OPRAVA #4: Tabulka všech endpointů (řádek 3889) opravena na správnou scope terminologii (articles:write místo write)
    • ✅ KRITICKÁ OPRAVA #5: Opraven počet endpointů v přehledu - tehdejší aktuální počet byl 58 endpointů po přidání e-shop read-only API (verifikováno v index.php)
    • ✅ KRITICKÁ OPRAVA #6: ProductResource používá stejné klíče obrázků jako články: images.articleDetail, images.articles, images.gallery (verifikováno v ProductResource.php:293-333)
    • ✅ Přidána poznámka o 28 WebBuilder endpointech, které nejsou v této dokumentaci detailně popsány
    • ✅ Označena zastaralá sekce s ENUM rozšířením pro hidden/deleted statusy (NEIMPLEMENTOVÁNO)
    Verze 1.4 (15. února 2026)
    • ✅ Opravena dokumentace oprávnění (scopes) - dokumentace nově správně popisuje hierarchický scope systém {resource}:{action} dle skutečné implementace v Scopes.php
    • ✅ Přidán kompletní přehled scope hierarchie s příklady (ADMINALL , articles:write, articles:read, atd.)
    • ✅ Vysvětlena logika kdy :write scope automaticky zahrnuje :read přístup
    • ✅ Aktualizovány všechny endpointy na správnou scope terminologii (např. articles:write místo původního nesprávného write)
    • ✅ Dokumentace ověřena proti skutečnému kódu v ArticleController.php a Scopes.php
    Verze 1.3 (15. února 2026)
    • ✅ Opraveny klíče pro obrázky - dokumentace nově správně používá articleDetail, articles, gallery místo neexistujících bannerimage, listimage, galleryimages
    • ✅ Aktualizovány všechny příklady kódu s realitou z ArticleController.php (řádky 386-665)
    • ✅ Ověřeny skutečné názvy variant obrázků proti produkční implementaci
    Verze 1.2 (16. ledna 2026)
    • ✅ Přidány endpointy pro produkty (seznam, detail, create, update, delete) - READ & WRITE
    • ✅ Implementováno pokročilé filtrování produktů s operátory (eq, gte, lte, like)
    • ✅ Filtrování podle parametrů (12 datových typů)
    • ✅ Filtrování podle produktových polí (dynamická pole podle typu produktu)
    • ✅ Přidána podpora pro typefields (definice polí pro typ produktu)
    • ✅ Přidána podpora pro fieldvalues (hodnoty produktových polí)
    • ✅ Optimalizované eager loading s podporou morfmorfních vztahů
    • ✅ Automatické mapování datových typů na správné value sloupce
    • ✅ Aktualizován souhrn na 13 endpointů
    • ✅ Dokumentace filtrovacích operátorů s příklady
    Verze 1.1 (15. ledna 2026)
    • ✅ Přidány endpointy pro tagy (seznam, detail s články)
    • ✅ Přidán endpoint pro profily uživatelů/autorů s bio, sociálními sítěmi a statistikami
    • ✅ Přidán endpoint pro parametry content typů
    • ✅ Rozšířena response kategorií o všechna DB pole (ordercat, csscat, navigationid, createdat, updatedat)
    • ✅ Přidána podpora pro custom parametry v článcích (14 datových typů)
    • ✅ Aktualizován implementační checklist
    • ✅ Přidán souhrn všech 11 endpointů
    • ✅ Ověřena kompatibilita s aktuální DB strukturou
    Verze 1.0 (14. ledna 2026)
    • 🎉 Iniciální release
    • ✅ 4 endpointy pro články
    • ✅ 3 endpointy pro kategorie
    • ✅ Autentizace pomocí API tokenů
    • ✅ Multi-site podpora s siteid izolací
    • ✅ Rate limiting
    • ✅ Kompletní dokumentace

    Plánované funkce (v2.0)

    • [ ] POST /api/v1/categories - Vytvoření kategorie
    • [ ] PUT /api/v1/categories/{id} - Aktualizace kategorie
    • [ ] POST /api/v1/tags - Vytvoření tagu
    • [ ] GET /api/v1/articles/{id}/related - Související články
    • [ ] GET /api/v1/stats - Statistiky API použití
    • [ ] WebHooks - Notifikace o změnách obsahu
    • [ ] GraphQL endpoint - Alternativa k REST
    • [ ] Bulk operations - Hromadné operace

    11. 📝 Formuláře a Zprávy (Forms & Submissions API)

    API pro práci s webovými formuláři umožňuje získat konfiguraci formuláře a zpracovat odeslání. Kombinuje veřejný endpoint pro odeslání (bez autentizace) s pokročilou anti-spam ochranou a volitelnými backend side-effecty.

    11.1 Získání konfigurace formuláře

    🛒 10A. E-shop Products API (READ ONLY)

    Read-only e-shop API staví nad existujícím katalogem produktů a vrací pouze produkty, které jsou skutečně prodejné pro aktuální siteid.

    Pravidla pro zařazení produktu do e-shop API

    Produkt se vrátí pouze pokud platí vše níže:

    • produkt patří do aktuálního siteid
    • producttypes.config.iseshopenabled = true
    • produkt má status = active
    • publishat není v budoucnosti
    • publishendat není v minulosti
    • existuje aktivní záznam v eshopproductprices
    • produkt je nákupně dostupný podle eshopproductinventory

    10A.1 GET /eshop/products

    GET /api/v1/eshop/productsAutorizace: Vyžaduje Authorization: Bearer {token} s oprávněním products:read nebo

    Query parametry

    ParametrTypPovinnýPopis
    pageintegerNeČíslo stránky (default 1)
    perpageintegerNePočet položek na stránku (default 20, max 100)
    producttypeidintegerNeFiltr podle typu produktu
    categoryidintegerNeFiltr podle kategorie
    searchstringNeVyhledávání v title, subtitle, sku

    Response 200 OK

    {

    "success": true, "data": [ { "id": 458, "siteid": 7, "title": "Úhlová bruska BOSCH GWS 1000", "subtitle": "Professional 125mm", "sku": "BOSCH-GWS-1000", "status": "active", "url": "https://example.com/katalog/naradi/uhlova-bruska-bosch-gws-1000.html", "createdat": "2025-01-10T09:00:00+00:00", "updatedat": "2025-01-22T16:45:00+00:00", "producttypeid": 5, "producttype": { "id": 5, "name": "Elektrické nářadí" }, "productkind": "physical", "categories": [ { "id": 11, "title": "naradi", "displayname": "Nářadí", "url": "/katalog/naradi" } ], "pricing": { "currency": "CZK", "basepricewithoutvat": 2478.51, "basepricewithvat": 2999.00, "finalpricewithoutvat": 2313.22, "finalpricewithvat": 2799.00, "vatrate": 21, "issaleactive": true, "salefrom": "2026-03-01T00:00:00+00:00", "saleto": "2026-03-31T23:59:59+00:00" }, "inventory": { "availabilitystatus": "instock", "availablequantity": 12, "reservedquantity": 1, "minsalequantity": 1, "allowbackorder": false, "ispurchasable": true }, "variantgroupid": 3 } ], "meta": { "currentpage": 1, "perpage": 20, "total": 1, "lastpage": 1, "totalpages": 1, "from": 1, "to": 1 } }

    10A.2 GET /eshop/products/{id}

    GET /api/v1/eshop/products/{id}Autorizace: Vyžaduje eshop:read (kompatibilně i products:read)

    Parametry cesty

    • id (povinné, integer): ID produktu

    Response 200 OK

    Vrací stejnou strukturu jako list endpoint pro jednu položku.

    10A.3 GET /eshop/products/{id}/variants

    GET /api/v1/eshop/products/{id}/variantsAutorizace: Vyžaduje eshop:read (kompatibilně i products:read)

    Response 200 OK

    {

    "success": true, "data": { "productid": 458, "groupid": 3, "primaryproductid": 458, "variantaxes": ["color", "size"], "variants": [ { "id": 458, "sku": "BOSCH-GWS-1000-BLACK", "title": "Úhlová bruska BOSCH GWS 1000 - Black", "pricing": { "currency": "CZK", "finalpricewithvat": 2799.00 }, "inventory": { "availabilitystatus": "instock", "availablequantity": 12, "ispurchasable": true } } ] } }

    Pokud produkt nepatří do variantové skupiny, endpoint vrátí groupid = null, variantaxes = [] a prázdné pole variants.

    10A.4 GET /eshop/products/{id}/availability

    GET /api/v1/eshop/products/{id}/availabilityAutorizace: Vyžaduje eshop:read (kompatibilně i products:read)

    Response 200 OK

    {

    "success": true, "data": { "productid": 458, "sku": "BOSCH-GWS-1000", "availabilitystatus": "instock", "availablequantity": 12, "reservedquantity": 1, "minsalequantity": 1, "allowbackorder": false, "ispurchasable": true } }

    Chybové stavy

    • 404 Not Found: produkt není prodejný pro aktuální web nebo neexistuje
    • 403 Forbidden: token nemá eshop:read ani kompatibilní products:read
    GET /api/v1/forms/{id}/configAutentizace: Bearer token required Oprávnění: forms:read

    Parametry cesty

    • id (povinné, integer): ID formuláře

    Response 200 OK

    {

    "id": 1, "formname": "Kontaktní formulář", "formfields": { "fields": [ { "slug": "fullname", "label": "Celé jméno", "type": "text", "required": true, "placeholder": "Jan Novák" }, { "slug": "email", "label": "E-mail", "type": "email", "required": true, "placeholder": "[email protected]" }, { "slug": "message", "label": "Zpráva", "type": "textarea", "required": true, "placeholder": "Vaše zpráva..." } ] }, "successmessage": "Děkujeme za vaši zprávu.", "formconfig": { "sendtouser": true, "requirerecaptcha": true, "enablehmac": false } }

    Poznámka: Endpoint vrací pouze veřejnou konfiguraci formuláře. Admin-only metadata jako mapování ecomailfield u jednotlivých polí a formconfig.ecomailtag se v response nikdy neposílají.

    Caching pomocí ETag

    GET /api/v1/forms/1/config

    If-None-Match: "a1b2c3d4"

    Response 304 Not Modified (pokud se konfigurace nezměnila)

    11.2 Odeslání formuláře

    POST /api/v1/forms/{id}/submitAutentizace: Veřejný endpoint (bez tokenu) Security: 5vrstvová anti-spam ochrana

    Backend side-effects

    • Zpráva se nejdřív uloží lokálně do databáze.
    • Poté proběhne obsahová spam klasifikace (EmailWhitelist + SpamDetector).
    • Pokud je ve $SERVER['domain'] vyplněno ecomailapikey i ecomaillistid, backend se pokusí synchronizovat kontakt do Ecomailu.
    • Synchronizace do Ecomailu je čistě backendová, fail-safe a nikdy nemění HTTP response submitu.
    • Submit označený jako spam neodesílá admin/user emaily a nespouští Ecomail sync.

    Request Body

    {

    "hpfield": "", "formopenedat": 1705320000, "g-recaptcha-response": "03AGdBq25...", "fullname": "Jan Novák", "email": "[email protected]", "message": "Dobrý den..." }

    Povinná bezpečnostní pole

    PoleTypPopis
    hpfieldstringHoneypot - musí být prázdný
    formopenedatintegerUnix timestamp (1-3600s před odesláním)
    g-recaptcha-responsestringreCAPTCHA token

    5vrstvá ochrana

    VrstvaMechanismusChyba
    1️⃣Rate Limiting (10/min/IP)429
    2️⃣Honeypot (hpfield="")400
    3️⃣Timestamp (1-3600s)400
    4️⃣reCAPTCHA (score > 0.5)400
    5️⃣HMAC (optional)403

    Příklad JavaScript

    import { useState } from 'react';

    function ContactForm() { const [formOpenedAt] = useState(Math.floor(Date.now() / 1000));

    const handleSubmit = async (e) => { e.preventDefault();

    const formData = { hpfield: '', formopenedat: formOpenedAt, 'g-recaptcha-response': await getRecaptchaToken(), fullname: e.target.fullname.value, email: e.target.email.value, message: e.target.message.value };

    const response = await fetch('/api/v1/forms/1/submit', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) });

    if (response.ok) { const result = await response.json(); alert(result.message); } };

    return (

    ); }

    11.3 CORS konfigurace

    Whitelist v site
    configurations:{

    "formallowedorigins": { "value": ["https://lovable.app", "https://app.lovable.dev"] } }

    Chování:
    • Whitelist nakonfigurován + origin match → Allow
    • Whitelist nakonfigurován + origin mismatch → 403 Forbidden
    • Whitelist nenakonfigurován → Allow all (fallback)

    11.4 Implementační checklist

    Frontend:
    • [ ] Získat API token s forms:read
    • [ ] Načíst konfiguraci GET /forms/{id}/config
    • [ ] Implementovat honeypot (skrytý, prázdný)
    • [ ] Uložit timestamp při načtení
    • [ ] Integrovat reCAPTCHA
    • [ ] Zpracovat RFC 7807 chyby
    Backend:
    • [ ] Vytvořit formulář v admin panelu
    • [ ] Nastavit adminemails
    • [ ] Nakonfigurovat reCAPTCHA klíče
    • [ ] Volitelně: formallowedorigins whitelist
    • [ ] Otestovat rate limiting

    Konec specifikace API v2.2

    12. User Portal API (v2.3)

    12.1 Přehled

    User portál je samostatná vrstva oddělená od admin API. Umožňuje přihlášeným uživatelům spravovat vlastní obsah (články, produkty, média), komunikovat (žádosti, komentáře, recenze) a sledovat historii akcí.

    Autentizace: stejná jako ostatní endpointy – Bearer token nebo session cookie. Module gating: každý endpoint vyžaduje, aby byl příslušný modul na daném webu povolen

    (sitemodules tabulka). Pokud modul chybí, API vrátí 403 Forbidden (RFC 7807).

    Ownership: uživatel smí číst/editovat pouze záznamy, jejichž userid == jeho UID

    (výjimka: admin scope ).


    12.2 Scopes (oprávnění)

    ScopePopis
    user:profile:readČtení vlastního profilu
    user:profile:writeZápis profilu, změna hesla
    user:media:readČtení vlastních médií
    user:media:writeNahrávání / smazání médií
    user:notifications:readČtení notifikací
    user:notifications:writeOznačení přečteno
    user:requests:readČtení žádostí
    user:requests:writeVytvoření žádosti
    user:comments:readČtení komentářů
    user:comments:writeVytvoření / smazání komentáře
    user:reviews:readČtení recenzí
    user:reviews:writeVytvoření / editace / smazání recenze
    user:articles:readČtení vlastních článků
    user:articles:writeCRUD vlastních článků
    user:products:readČtení vlastních produktů
    user:products:writeCRUD vlastních produktů
    user:eshop:readMoje objednávky (zákazník)
    user:seller:readObjednávky k mým produktům (prodejce)
    user:history:readPřehled auditního logu

    12.3 Endpointy – Profil a účet

    GET /api/v1/user/me

    Vrátí profil přihlášeného uživatele.

    Scopes: user:profile:read

    PUT /api/v1/user/me

    Aktualizace profilu (firstname, lastname, phone, bio, website).

    Scopes: user:profile:write

    PUT /api/v1/user/me/password

    Změna hesla. Vyžaduje currentpassword, newpassword, newpasswordconfirm.

    Scopes: user:profile:write

    GET /api/v1/user/me/roles

    Role a oprávnění uživatele na aktuálním webu.

    Scopes: user:profile:read

    GET /api/v1/user/me/modules

    Povolené moduly pro daný web + tarif.

    Scopes: user:profile:read

    12.4 Endpointy – Notifikace

    GET /api/v1/user/notifications

    Seznam notifikací. Filtry: ?type=system|content|eshop, ?unread=1, ?page, ?limit.

    Scopes: user:notifications:read | Modul: notifications

    POST /api/v1/user/notifications/read

    Označení notifikací jako přečtené. Body: {"id": 5} nebo {"all": true}.

    Scopes: user:notifications:write | Modul: notifications

    12.5 Endpointy – Články

    Modul: articles
    MetodaEndpointPopis
    GET/api/v1/user/articlesSeznam vlastních článků
    POST/api/v1/user/articlesVytvoření článku (status: development)
    GET/api/v1/user/articles/{id}Detail vlastního článku
    PUT/api/v1/user/articles/{id}Editace vlastního článku
    DELETE/api/v1/user/articles/{id}Soft-delete článku
    Parametry GET /user/articles: ?page, ?limit, ?status, ?sort, ?dir POST/PUT fields: title (povinný), body, subtitle, short
    text, snippet, categoryid nebo categoryids, volitelně status (development/active/schedule/suspend), tagids, parameters nebo parametervalues

    12.6 Endpointy – Produkty

    Modul: catalogproducts
    MetodaEndpointPopis
    GET/api/v1/user/productsSeznam vlastních produktů
    POST/api/v1/user/productsVytvoření produktu
    GET/api/v1/user/products/{id}Detail produktu
    PUT/api/v1/user/products/{id}Editace produktu
    DELETE/api/v1/user/products/{id}Soft-delete produktu
    POST/PUT required: title, product
    typeid, categoryid nebo categoryids

    12.7 Endpointy – Média

    Modul: media
    MetodaEndpointPopis
    GET/api/v1/user/mediaSeznam vlastních souborů
    POST/api/v1/user/mediaUpload souboru (přes UploadedFile pipeline)
    GET/api/v1/user/media/{id}Detail souboru
    DELETE/api/v1/user/media/{id}Soft-delete souboru

    12.8 Endpointy – Žádosti, komentáře, recenze

    Modul: requests
    messages (žádosti) / commentsreviews (komentáře, recenze)
    MetodaEndpointPopis
    GET/api/v1/user/requestsMoje žádosti
    POST/api/v1/user/requestsNová žádost (subject + body)
    GET/api/v1/user/requests/{id}Detail žádosti
    GET/api/v1/user/commentsMoje komentáře
    POST/api/v1/user/commentsNový komentář (entitytype, entityid, body)
    DELETE/api/v1/user/comments/{id}Smazání komentáře
    GET/api/v1/user/reviewsMoje recenze
    POST/api/v1/user/reviewsNová recenze (entitytype, entityid, rating 1–5)
    PUT/api/v1/user/reviews/{id}Editace recenze
    DELETE/api/v1/user/reviews/{id}Smazání recenze

    12.9 Endpointy – E-shop BASIC

    Modul customer: eshop
    customer | Modul seller: eshopseller
    MetodaEndpointPopis
    GET/api/v1/user/ordersMoje objednávky jako zákazník
    GET/api/v1/user/orders/{id}Detail objednávky (items + addresses + events)
    GET/api/v1/user/seller/ordersObjednávky s mými produkty (prodejce)
    GET/api/v1/user/seller/orders/{id}Detail – pouze moje items v dané objednávce

    12.9.1 Endpointy – E-shop ADVANCED

    Modul customer: eshop
    customer (LEVEL ADVANCED) | Modul seller: eshopseller (LEVEL ADVANCED)
    MetodaEndpointPopis
    GET/api/v1/user/orders/{id}/trackingTimeline trackingu objednávky (status + shipment události)
    POST/api/v1/user/orders/{id}/repeatZopakování objednávky do aktivního košíku
    GET/api/v1/user/seller/orders/{id}/eventsKompletní timeline událostí objednávky pro prodejce
    POST/api/v1/user/seller/orders/{id}/statusZměna stavu objednávky prodejcem
    POST/api/v1/user/seller/orders/{id}/trackingPřidání tracking události pro zásilku
    POST/api/v1/user/seller/orders/{id}/messageInterní/customer message pro objednávku
    GET/api/v1/user/seller/complaintsReklamace k produktům přihlášeného prodejce
    GET/api/v1/user/seller/complaints/{id}Detail reklamace k produktu prodejce
    POST/api/v1/user/seller/complaints/{id}/statusAktualizace stavu reklamace prodejcem

    12.10 Endpointy – Historie akcí

    GET /api/v1/user/history

    Auditní log vlastních akcí. Filtry: ?page, ?limit, ?sort, ?dir.

    Scopes: user:history:read | Modul: audit
    history

    12.11 Module gating – tabulka modulů

    Klíč moduluOvlivněné endpointy
    coreaccount/user/me, /user/me/password, /user/me/roles, /user/me/modules
    notifications/user/notifications
    articles/user/articles/
    catalogproducts/user/products/
    media/user/media/
    eshopcustomer/user/orders, /user/orders/{id}/tracking, /user/orders/{id}/repeat
    eshopseller/user/seller/orders, /user/seller/orders/{id}/events, /user/seller/orders/{id}/status, /user/seller/orders/{id}/tracking, /user/seller/orders/{id}/message, /user/seller/complaints
    commentsreviews/user/comments/, /user/reviews/
    requestsmessages/user/requests/
    audithistory/user/history
    Úrovně: OFF (zakázáno, vrátí 403) / BASIC / ADVANCED

    12.12 Web rozhraní (server-side Twig)

    URLŠablonaPopis
    GET /user/dashboarduser-portal/dashboard.twigPřehled účtu
    GET /user/profileuser-portal/profile.twigEditace profilu + hesla
    GET /user/rolesuser-portal/roles.twigPřehled rolí a modulů
    GET /user/notificationsuser-portal/notifications.twigNotifikace
    GET /user/articlesuser-portal/articles.twigSeznam článků
    GET /user/articles/newuser-portal/article-edit.twigNový článek
    GET /user/articles/{id}/edituser-portal/article-edit.twigEditace článku
    GET /user/productsuser-portal/products.twigSeznam produktů
    GET /user/products/{id}/edituser-portal/product-edit.twigEditace produktu
    GET /user/mediauser-portal/media.twigGalerie médií
    GET /user/requestsuser-portal/requests.twigŽádosti + formulář
    GET /user/commentsuser-portal/comments.twigKomentáře
    GET /user/reviewsuser-portal/reviews.twigRecenze
    GET /user/historyuser-portal/history.twigAuditní log

    Všechny web stránky vyžadují aktivní session (redirect na /admin/login/ pokud nepřihlášen).


    12.13 AJAX endpointy – User Portal (v2.3)

    Tyto endpointy jsou volány JavaScriptem z user portal stránek pro dynamické funkce. Všechny vyžadují aktivní session a vrací JSON odpovědi.

    GET /user/articles/search-tags

    Vyhledávání štítků pro tagování článků.

    Parametry: ?q=search
    query (minimálně 2 znaky) Odpověď: {"success": true, "tags": [{"id": 1, "title": "název štítku", "displayname": "Zobrazovaný název"}]} Cache: File-based cache s TTL 5 minut, automatická invalidace při změně štítků Rate limit: 10 požadavků/minutu na uživatele

    GET /user/articles/search-content

    Vyhledávání souvisejícího obsahu (články + produkty) pro propojení.

    Parametry: ?q=search
    query (minimálně 2 znaky) Odpověď: {"success": true, "results": [{"id": 1, "title": "název", "type": "article|product", "url": "/url"}]} Cache: File-based cache s TTL 5 minut Rate limit: 10 požadavků/minutu na uživatele Cache chování:
    • Cache soubory jsou uloženy v www/cache/search.json
    • Klíč cache: tagssearch{siteId}{md5(query)} nebo contentsearch{siteId}{md5(query)}
    • TTL: 300 sekund (5 minut)
    • Invalidace: Automatická při Tag::syncTags() nebo ruční při změně obsahu
    • Fallback: Pokud cache selže, endpoint funguje bez cache

    Konec specifikace API v2.3 – User Portal rozšíření

    QR Payment Support v Articles API

    Přehled

    QR payment je volitelná funkce, která umožňuje zobrazit QR kód pro snadnou platbu na články. QR payment data jsou součástí detailu článku jako volitelný blok qrpayment.

    QR Payment Response

    V odpovědi GET /api/v1/articles/{id} je přidán blok qrpayment:

    {

    "success": true, "data": { "id": 132, "title": "Název článku", ... "qrpayment": { "mode": "author|general|none", "vs": "1234567890", "text": "Přispěj na podporu", "amounts": [50, 100, 200, 500], "qrurl": "/qr-payment?articleid=132&amount=100" } } }

    QR Payment Pole

    PoleTypPopis
    modestringRežim platby: "author" (IBAN autora), "general" (redakční IBAN), "none" (vypnuto)
    vsstringVariabilní symbol pro platbu (generován při každé aktivaci)
    textstringText z konfigurace (qrpaymenttextauthor nebo qrpaymenttexteditorial)
    amountsarrayPole doporučených částek [50, 100, 200, 500]
    qrurlstringURL adresa QR obrázku pro dané množství

    Konfigurace

    QR payment se konfiguruje v siteconfigurations:

    KlíčTypPříkladPopis
    qrpaymentdefaultmodestringauthorVýchozí režim: author, general, none
    qrpaymentibanstringCZ1234567890IBAN pro redakční platby (general režim)
    qrpaymentamountsjson[50,100,200,500]Pole doporučených částek
    qrpaymentmsgstringPříspěvek na provozZpráva pro QR (volitelný text)
    qrpaymenttextauthorstringPodpořte autoraText pro režim author
    qrpaymenttexteditorialstringPřispěj na provozText pro režim general

    Podmínky

    • QR payment se vrací jen když je web na tarifi ENTERPRISE s modulem CMS Articles úrovně ADVANCED
    • Pokud mode = "none", blok qrpayment vrací null
    • Pokud se služba nezdaří, vrací se null (silent fail)
    • QR payment je vždy READ ONLY ve veřejném API

    Tags API Specification

    Přehled

    Tags API poskytuje přístup k systému štítků (tagů) v CMS s podporou kategorií, obrázků a filtrování.

    GET /api/v1/tags

    Vrátí seznam všech štítků s paginací a filtrováním.

    Oprávnění

    • Token musí mít scope: tags:read

    Query Parameters

    ParametrTypVýchozíPopis
    pageinteger1Číslo stránky pro paginaci
    perpageinteger20Počet štítků na stránku (max. 100)
    searchstring-Hledaný text v názvu štítku
    categoryidinteger-Filtrování podle ID kategorie štítku
    sortstringtitleŘazení: title, popular, newest

    Request Example

    GET /api/v1/tags?page=1&perpage=10&categoryid=5&search=web

    Authorization: Bearer YOURAPITOKEN

    Response (Success - 200 OK)

    {

    "success": true, "data": [ { "id": 13, "name": "PHP", "slug": "tag/php", "status": "active", "categories": [], "articlescount": 42, "productscount": 8 }, { "id": 14, "name": "Web Development", "slug": "tag/web-development", "status": "active", "categories": [ { "id": 1, "title": "Technology", "displayname": "Technologie" } ], "articlescount": 125, "productscount": 15 } ], "meta": { "pagination": { "currentpage": 1, "perpage": 10, "totalpages": 5, "totalcount": 42 } } }

    GET /api/v1/tags/{id}

    Vrátí detail konkrétního štítku.

    Oprávnění

    • Token musí mít scope: tags:read

    URL Parameters

    ParametrTypPopis
    idintegerID štítku

    Request Example

    GET /api/v1/tags/13

    Authorization: Bearer YOURAPITOKEN

    Response (Success - 200 OK)

    {

    "success": true, "data": { "id": 13, "name": "PHP", "slug": "tag/php", "status": "active", "categories": [ { "id": 1, "title": "Technology", "displayname": "Technologie" } ], "articlescount": 42, "productscount": 8, "relatedtags": [ { "id": 5, "name": "Laravel", "slug": "tag/laravel" }, { "id": 12, "name": "Symfony", "slug": "tag/symfony" } ] } }

    GET /api/v1/tag-categories

    Vrátí seznam všech kategorií štítků.

    Oprávnění

    • Token musí mít scope: tags:read

    Request Example

    GET /api/v1/tag-categories

    Authorization: Bearer YOURAPITOKEN

    Response (Success - 200 OK)

    {

    "success": true, "data": [ { "id": 1, "title": "Technology", "displayname": "Technologie", "isdefault": true, "order": 1 }, { "id": 2, "title": "Business", "displayname": "Byznys", "is_default": false, "order": 2 } ], "meta": { "total": 2 } }

    Chybové kódy

    KódPopis
    401Chybný token, vypršel nebo nemá patřičný scope tags:read
    403Nedostatečná oprávnění
    404Štítek nebo kategorie neexistuje

    Konec specifikace API v3.2 – Tags & QR Payment rozšíření*

    Další čtení

    Mohlo by vás zajímat

    Postavte web, který se dobře indexuje

    Čisté HTML, serverový rendering a plná kontrola nad meta daty. Vyzkoušejte Mini-web zdarma.

    Vyzkoušet zdarma Zpět na api reference