Сайти
Сайт — це статика, яку ви завантажили: зібраний React, лендинг, документація. Вона віддається зі сховища на власній адресі, а не з домену застосунку.
Адреса сайту
У кожного сайту власний піддомен:
app.oxta.io застосунок
myapp.sites.oxta.io ваш сайт
Це інше походження, ніж застосунок: на домені застосунку в браузері лежить ваш токен, і якби HTML сайту віддавався звідти, його скрипт міг би цей токен прочитати. Через це ж HTML і SVG у звичайних файлах віддаються як завантаження, а не як сторінка — там окремого походження немає.
Оскільки піддомени різні, сайти ізольовані й один від одного: скрипт одного сайту не бачить сховища іншого.
Застосунок на цих адресах не відповідає взагалі: myapp.sites.oxta.io/api/…, /flows, будь-що — 404. Тож токен там не з'явиться навіть теоретично.
Раніше сайт віддавався ще й за адресою sites.oxta.io/myapp/. Так робити перестали: у тій формі всі сайти всіх користувачів мали одне походження, тобто скрипт одного сайту читав сховище іншого й міг звертатися до його /_api/… від імені відвідувача. Старі посилання не загубились — вони назавжди перенаправляються на піддомен (301, а для запитів із тілом 308, щоб метод не змінився).
Збірка
На піддомені сайт лежить у корені, тож звичайна збірка з абсолютними шляхами (/assets/app.js) працює як є — нічого налаштовувати не треба.
Під префіксом сайт лежить лише локально, за адресою /__site/myapp/ — піддоменів на localhost немає. Там ми підставляємо в HTML <base href="/__site/myapp/">, і відносні шляхи знаходяться; абсолютні <base> не виправляє. Тож якщо перевіряєте збірку локально, збирайте з відносним base: vite build --base=./ (або "homepage": "." у Create React App). На проді це не потрібно.
Адреса
Малі літери, цифри й одиничні дефіси, від 3 до 40 символів. Частина назв зайнята нами: api, app, www, help, sites і подібні.
Як публікувати
Натисніть «Опублікувати папку» й виберіть теку зі збіркою — браузер віддасть усі файли разом із відносними шляхами. Якщо все лежить в одній теці (dist/), ця тека обрізається, і dist/index.html стає index.html.
Повторна публікація того самого шляху перезаписує файл. Файли, яких у новій публікації немає, залишаються — щоб прибрати старе, видаліть сайт і створіть заново.
Межі: до 25 МБ на файл, до 100 МБ і 500 файлів на сайт.
Публікація сайту разом із флоу
Сайт і його API можуть їхати одним паком. Покладіть поруч зі збіркою теку _flows, у ній — по JSON-файлу на флоу, і натисніть «Опублікувати папку» як завжди:
dist/
index.html
assets/app.js
_flows/
get.index.json → GET /_api/
post.orders.json → POST /_api/orders
orders/get.all.json → GET /_api/orders/*
_nightly-report.json → флоу без власного шляху
Завантаження розділяється надвоє: усе поза _flows публікується як файли сайту, а кожен .json усередині імпортується як флоу й чіпляється до шляху. Ніщо з _flows не стає файлом сайту — ні самі флоу, ні випадковий notes.txt, який туди поклали: про такий файл ми скажемо у відповіді, але не опублікуємо.
Що говорить назва файлу
Назва файлу і є маршрут:
<метод>.<назва>.json— метод один ізget,post,put,patch,delete,any; без нього шлях відповідає наany;- теки вкладаються у шлях:
_flows/admin/get.users.jsonстаєGET /admin/users; indexозначає саму теку:_flows/get.index.json— цеGET /, а_flows/orders/get.index.json—GET /orders;allозначає підстановку:_flows/orders/any.all.json— цеANY /orders/*;- назва, що починається з підкреслення (
_nightly-report.json), або будь-що в теці з підкресленням (_flows/_lib/…), імпортується як звичайне флоу без шляху — туди кладуть флоу за розкладом чи підфлоу.
Флоу може сказати це й прямо — тоді назва файлу не має значення:
{ "name": "Замовлення", "route": { "method": "POST", "path": "/orders" }, "nodes": [] }
"route": false імпортує флоу без шляху.
Яку назву отримає флоу
Спереду додається назва сайту, тож флоу одного паку тримаються купи в списку: Замовлення із сайту shop стає Shop · Замовлення. Повторна публікація не наліплює префікс двічі.
Повторна публікація паку
Друга публікація оновлює те, що вже є, а не плодить копії:
- файл флоу, чий шлях на цьому сайті вже заведено, оновлює те флоу, на яке цей шлях указує;
- флоу без шляху знаходиться за назвою з префіксом;
- решта створюється, і шлях додається до сайту.
Значення секретних змінних, які ви вписали, лишаються на місці — порожній секрет у файлі їх не стирає.
Файл флоу, який не тримається купи, повертається у відповіді з поясненням, а решта паку все одно публікується.
Межі: до 50 флоу в одному паку і 50 шляхів на сайт.
Односторінкові застосунки
Перемикач «односторінковий застосунок» вирішує, що робити з невідомим шляхом:
- увімкнено — шлях без розширення (наприклад
/orders/42) віддаєindex.html, і маршрутизацію робить ваш скрипт; - вимкнено — такий шлях дає 404.
Відсутній файл із розширенням (/assets/app.js) завжди дає 404 — інакше зламаний шлях до скрипта тихо повертав би HTML.
Що віддається у заголовках
X-Robots-Tag: noindex, nofollow— сайти не потрапляють у пошук;Cache-Controlзалежить від файлу: HTML перевіряється щоразу, файли з хешем у назві кешуються на рік;ETagіз R2, тож повторний запит отримує 304;- діапазони підтримуються, тож відео й аудіо перемотуються.
Власний API на тому самому походженні
Сайт може мати свій API: ви прив'язуєте шлях до флоу, і звернення до /_api/… запускає це флоу.
POST https://myapp.sites.oxta.io/_api/orders → ваше флоу
Оскільки це те саме походження, що й сторінка, фронтенду не потрібен ні CORS, ні токен у коді.
- шлях виглядає як
/ordersабо/orders/*; точний шлях виграє в підстановки, довша підстановка — в коротшої; - метод можна вказати конкретний або
ANY; конкретний виграє; - на відміну від вебхука, такий виклик чекає на відповідь типово (15 с), бо браузер її потребує. Щоб не чекати, надішліть
Prefer: respond-async— прийде202; - відповідь формує вузол «Відповідь» у флоу; без нього прийде
{"ok": true}; - флоу мусить бути вашим і увімкненим: чуже флоу прив'язати не вийде, вимкнене дає
503; - кожен виклик — це запуск флоу, тож він списує квоту
runs. Ліміт частоти береться з налаштувань самого флоу або, якщо його нема, платформний дефолт 60 запитів на хвилину: понад ліміт —429ізRetry-After.
Що бачить флоу у своєму тригері: headers, query, body, method і path (шлях уже без /_api).
Ці шляхи перехоплюються до підстановки односторінкового застосунку, тож невідомий /_api/… дає 404 JSON, а не index.html.
Як перевірити локально
Публічний хост у розробці не існує, тож локально сайт відкривається за адресою з префіксом:
http://localhost:8787/__site/myapp/
http://localhost:8787/__site/myapp/_api/orders
Кнопка «Відкрити» в картці сама веде на цю адресу, коли ви на localhost — у списку тоді видно позначку «відкривається локально».
Ця форма працює лише на localhost. На бойовому домені вона вимкнена: інакше HTML сайту виконувався б на походженні застосунку й міг би прочитати ваш токен.
Скільки сайтів входить у тариф
Ліміт — на кількість сайтів і на загальний обсяг файлів по всіх ваших сайтах:
| Тариф | Сайтів | Обсяг |
|---|---|---|
| Free | 1 | 25 МБ |
| Starter | 3 | 100 МБ |
| Pro | 10 | 500 МБ |
| Business | 50 | 2 ГБ |
| Scale | 200 | 10 ГБ |
Звернення до сайту не лімітуються — статика в рази дешевша за запуск флоу. А от кожен виклик /_api/… — це запуск флоу, тож він списує квоту runs.
На сторінці «Сайти» видно, скільки з тарифу вже зайнято. Понад ліміт: сайт не створиться, а публікація файлів відхилиться з назвою тарифу в повідомленні.
Вимикач
Перемикач «опубліковано» вимикає сайт миттєво: усі шляхи починають віддавати 404, файли лишаються на місці. Це те, чим варто скористатися, якщо на сайт хтось поскаржився.