From 452ae5b89823f61ade452927d6ffeb7a980004f0 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 17 Jul 2026 12:18:02 +0000 Subject: [PATCH] docs: rewrite README in plain human wording --- README.md | 312 ++++++++++++++++++++---------------------------------- 1 file changed, 114 insertions(+), 198 deletions(-) diff --git a/README.md b/README.md index 94de9aa..72d925d 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,61 @@ -# Как запустить свой проект на платформе +# Запуск проекта на платформе -Один файл `.env` + один пуш → сайт на твоём домене с автоматическим SSL. -Это единственный репозиторий, который нужно знать: тут и гайд, и файлы стартера, -и сам платформенный action. +Кладёшь в репу `.env` с доменом, пушишь — платформа собирает проект и поднимает +сайт с SSL. Форкать этот репо не нужно: он тут как справка и как источник двух +файлов, которые копируешь к себе. ---- +## Быстрый старт -## TL;DR — 4 шага - -1. Скопируй в **свой** репозиторий 2 файла из папки [`starter/`](starter) этого репо: - - `.forgejo/workflows/deploy.yml` — **не трогать** (тянет платформенный action по ссылке) +1. Скопируй в свой репозиторий два файла из [`starter/`](starter): + - `.forgejo/workflows/deploy.yml` — как есть, не редактируй - `.env` — из `starter/.env.example` +2. В `.env` укажи домен: `DOMAIN=mysite.com` +3. Пропиши A-запись домена на `51.161.128.94` +4. Запушь в основную ветку. Через минуту-полторы сайт открыт по HTTPS. - Форкать/клонировать этот репо **не надо** — деплой цепляется к твоей репе по - самому факту наличия `.forgejo/workflows/deploy.yml`. -2. Заполни `.env`, минимум: - ```env - DOMAIN=mysite.com - ``` -3. Пропиши **A-запись** домена → IP платформы **`51.161.128.94`**. -4. `git push` в дефолтную ветку. Через ~30–90 сек сайт открыт на `https://mysite.com`. +Дальше каждый push пересобирает и передеплоивает. `deploy.yml` больше не трогаешь. -Дальше любой push = редеплой. `deploy.yml` трогать не нужно никогда. +## Что происходит при пуше ---- +Workflow дёргает платформенный action (`action.yml` из этого репо): он клонирует +твою репу, читает `.env`, определяет тип сборки, собирает и публикует, прописывает +домен в Caddy. Caddy сам выпускает сертификат Let's Encrypt. -## Как это работает (флоу) +## Типы проектов -``` -git push - │ - ▼ -Forgejo Actions запускает .forgejo/workflows/deploy.yml - │ (uses: https://git.extract.team/platform/launch@main) - ▼ -platform/launch action: - ├─ клонирует твою репу - ├─ читает .env → DOMAIN, тип деплоя (static / docker / docker-db) - ├─ билд: static → bun build | docker → docker build - ├─ публикует статику в /srv/sites │ или поднимает контейнер - └─ пишет фрагмент Caddy на твой домен - ▼ -Caddy автоматически выпускает SSL (Let's Encrypt) → https://mysite.com живой -``` +Тип берётся из `DEPLOY` в `.env`, либо определяется сам: есть `Dockerfile` — docker, +нет — static. -Один репозиторий = один сайт (один `.env`, один `DOMAIN`). +### static ---- - -## Тип проекта: static / docker / docker-db - -Тип определяется полем `DEPLOY` в `.env` или авто-детектом (по наличию `Dockerfile`). - -### 1. `static` — фронт (90% проектов) - -Vite / CRA / Next static export. `bun build` → статика раздаётся напрямую, **без -контейнера**. Деплой за секунды; редеплой = только заливка новых файлов. +Фронт на Vite / CRA / Next export. Собирается через bun, раздаётся как статика, без +контейнера — самый быстрый вариант. ```env DOMAIN=mysite.com -# DEPLOY=static # дефолт, если в корне НЕТ Dockerfile -# BUILD_DIR=dist # Vite=dist (дефолт), CRA=build, Next export=out +# BUILD_DIR=dist # Vite=dist, CRA=build, Next export=out ``` -- **Vite + React** — ничего настраивать, дефолт. -- **CRA** — `BUILD_DIR=build`. -- **Next.js static export** — в `next.config`: `output: 'export'`, - `images: { unoptimized: true }`, `trailingSlash: true`; в `.env` — `BUILD_DIR=out`. - SSR / API routes / Server Actions в static-режиме не работают. +Для Next.js в static-режиме нужно в `next.config`: `output: 'export'`, +`images.unoptimized: true`, `trailingSlash: true`, и `BUILD_DIR=out`. SSR и API +routes в этом режиме не работают. -### 2. `docker` — фронт + бэкенд в контейнере +### docker -Положи `Dockerfile` в корень. Платформа собирает образ, поднимает контейнер и -проксирует домен на него. Контейнер должен слушать порт из `APP_PORT`. +Свой `Dockerfile` в корне. Платформа собирает образ, поднимает контейнер и +проксирует на него домен. Контейнер должен слушать `APP_PORT`. ```env DOMAIN=mysite.com -DEPLOY=docker # или просто наличие Dockerfile -APP_PORT=3000 # порт контейнера (default 80) +DEPLOY=docker +APP_PORT=3000 ``` -### 3. `docker-db` — docker + персистентная база (SQLite) +### docker-db -Как `docker`, плюс платформа монтирует named-volume в `/data` и прокидывает -`DATABASE_URL=file:/data/app.db`. Том переживает редеплои. +То же, что docker, плюс SQLite: том монтируется в `/data`, в контейнер прокидывается +`DATABASE_URL=file:/data/app.db`. Том переживает редеплои. Миграции гоняешь сам в +entrypoint. `DATABASE_URL` в `RUNTIME_KEYS` добавлять не надо — она ставится сама. ```env DOMAIN=mysite.com @@ -91,193 +63,137 @@ DEPLOY=docker-db APP_PORT=3000 ``` -В коде читаешь `process.env.DATABASE_URL`. Миграции (drizzle/prisma) запускай в -своём entrypoint — платформа их не делает. **Не** указывай `DATABASE_URL` в -`RUNTIME_KEYS` (ставится автоматически). - ---- - ## Домен и SSL -- **`DOMAIN`** — обязательное поле. Заранее пропиши **A-запись** домена на IP - платформы `51.161.128.94` (у своего регистратора). -- **`www.`** подключается автоматически, если у `www.` тоже есть A-запись на - наш IP. Не нужен — поставь `WWW=false`. -- **SSL выпускается сам** (Let's Encrypt), обычно за 30–60 сек после первого деплоя, - когда DNS уже указывает на наш IP. Отдельный серт на каждый домен. Ничего - настраивать не надо. -- **Не открывай сайт в браузере, пока workflow не зелёный** — пока идёт деплой, - валидного серта ещё может не быть, и браузер закеширует «сломанный SSL». Дождись - зелёного run в **Actions** (или открой в инкогнито). +- `DOMAIN` обязателен. Заранее пропиши A-запись домена на `51.161.128.94`. +- `www` подключается сам, если у него тоже есть A-запись на наш IP. Не нужен — + поставь `WWW=false`. +- Сертификат выпускается автоматически, обычно за полминуты после первого деплоя. + На каждый домен — отдельный. +- Пока workflow не позеленел, не открывай сайт в обычном браузере: сертификата ещё + может не быть, и браузер закеширует ошибку. Дождись зелёного run или открой в + инкогнито. -### Доп. домены — один сайт на нескольких доменах +Один сайт на нескольких доменах: ```env -DOMAIN=mysite.com -EXTRA_DOMAINS=mysite.io,app.mysite.com,promo.xyz +EXTRA_DOMAINS=mysite.io,app.mysite.com ``` -Тот же сайт на всех доменах, на **каждый** — свой авто-SSL. От тебя: A-запись -каждого домена на наш IP. `www.` для доп-доменов сам не добавляется — если нужен, -впиши `www.mysite.io` явным пунктом. +На каждый домен — свой сертификат, для каждого пропиши A-запись. `www` для +доп-доменов сам не добавляется (нужен — впиши `www.mysite.io` отдельным пунктом). ---- +## Twitter -## Twitter / X проекта - -Хочешь, чтобы в **админ-панели** рядом с сайтом показывался его твиттер — добавь в -`.env`: +Чтобы в админке рядом с сайтом показывался твиттер проекта: ```env TWITTER_URL=https://x.com/mysite ``` -Принимается полный URL, `@handle` или голый `handle` (`TWITTER=` тоже работает). -Платформа нормализует и покажет как `𝕏 @mysite`. Ни на деплой, ни на сайт не влияет — -только метаданные для админки. - ---- +Принимается URL, `@handle` или просто `handle`. На сам сайт не влияет — только метка +в панели. ## Монорепозиторий -Платформа деплоит **один сайт на репозиторий** (один `.env`, один `DOMAIN`). Отдельного -поля «папка приложения» нет — сборка идёт от **корня репы**. Поэтому для монорепо: +Один репозиторий = один сайт. Отдельного поля под папку приложения нет — сборка идёт +от корня репы. Поэтому: -- `.env` и `package.json` (или `Dockerfile`) — **в корне** репозитория. -- **static:** корневой скрипт `build` должен собирать нужное приложение (через - workspaces / turbo / nx), а `BUILD_DIR` — указывать на его вывод: +- `.env` и `package.json` (или `Dockerfile`) держи в корне. +- static: корневой скрипт `build` должен собирать нужное приложение, а `BUILD_DIR` — + указывать на его вывод: ```env - DOMAIN=mysite.com - BUILD_DIR=apps/web/dist # куда собирается целевое приложение + BUILD_DIR=apps/web/dist ``` - ```jsonc - // package.json в корне + ```json { "scripts": { "build": "npm run build -w apps/web" } } ``` -- **docker:** `Dockerfile` в корне, контекст сборки = весь репозиторий, так что он - спокойно `COPY apps/web …` и собирает нужный пакет. Задай `APP_PORT`. -- Нужно задеплоить **несколько** приложений из одного монорепо на **разные** домены — - сделай под каждое отдельный репозиторий (один репо = один сайт). +- docker: `Dockerfile` в корне, контекст сборки — весь репозиторий, так что `COPY` + нужного пакета работает. +- Нужно несколько приложений на разные домены — заведи под каждое отдельный репозиторий. ---- +## Установочный сабдомен (`curl get.домен | sh`) -## Установочный сабдомен `curl get. | sh` (self-service) +Только для static, настраивается без админа: -Красивый one-liner установки — делается **сам, без админа**, для `static`-сайтов: +1. Положи скрипт в `public/install.sh` (папка `public/` у Vite попадает в корень сборки). +2. Пропиши A-запись `get` → наш IP. +3. Добавь `INSTALL_SH=install.sh` в `.env` и запушь. -1. Положи скрипт в репу → `public/install.sh` (папка `public/` у Vite попадает в - `dist/` как есть → окажется в корне сайта). -2. **DNS:** A-запись `get` → IP платформы (host = `get`). -3. В `.env`: `INSTALL_SH=install.sh` → `git push`. +Платформа поднимет `get.домен`, выпустит SSL и будет отдавать этот файл как +`text/plain`. Убрал `INSTALL_SH` — сабдомен снимется на следующем деплое. -CI сам поднимет `get.`, выпустит SSL и будет отдавать `install.sh` как -`text/plain`. Убери `INSTALL_SH` и запушь — сабдомен снимется. В админке этот -сабдомен виден в строке «сабдомены». +## Секреты ---- +`.env` в контейнер не попадает — он читается только при деплое. Секреты заводишь в +Forgejo: репа → Settings → Actions → Secrets, имя секрета = имя переменной. Потом +перечисляешь нужные: -## Секреты (DATABASE_URL, API-ключи) - -`.env` в runtime контейнера **не попадает** (парсится только при деплое). Секреты: - -1. **Forgejo → репа → Settings → Actions → Secrets → Add Secret.** Имя секрета = имя - переменной окружения (`DATABASE_URL`, `JWT_SECRET`). -2. В `.env` перечисли, какие прокинуть: - ```env - RUNTIME_KEYS=DATABASE_URL,JWT_SECRET - ``` -3. В коде: `process.env.DATABASE_URL`. - -Платформа берёт **только** ключи из `RUNTIME_KEYS`. (Только для `docker`/`docker-db`.) - ---- - -## Скорость билда - -Фронт собирается через **bun** (install в 10–30× быстрее npm). Закоммить lock: - -```bash -bun install # создаст bun.lock -git add bun.lock && git commit -m "chore: bun lock" && git push +```env +RUNTIME_KEYS=DATABASE_URL,JWT_SECRET ``` -### docker-сайты — кеш слоёв и лёгкие образы +В контейнер попадут только они. В коде читаешь как обычно — `process.env.DATABASE_URL`. -Сборка идёт с **BuildKit** и `--cache-from` предыдущего образа. Чтобы это работало: +## Скорость сборки -- ставь зависимости ДО копирования кода (слой install кешируется): - ```dockerfile - COPY package.json package-lock.json ./ - RUN npm ci - COPY . . - RUN npm run build - ``` -- cache-mount'ы: - ```dockerfile - # syntax=docker/dockerfile:1.7 - RUN --mount=type=cache,target=/root/.npm npm ci - RUN --mount=type=cache,target=/app/.next/cache npm run build - ``` -- **Next.js** → `output: 'standalone'` в `next.config` роняет образ с ~1–3 ГБ до - ~150–250 МБ → деплой заметно быстрее. +Фронт собирается через bun — install в разы быстрее npm, закоммить `bun.lock`. ---- +docker-образы собираются с BuildKit и кешем от предыдущего образа. Ставь зависимости +до копирования кода, чтобы слой install кешировался: -## Выключить сайт (kill-switch) +```dockerfile +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +RUN npm run build +``` + +Для Next.js `output: 'standalone'` в `next.config` роняет образ с гигабайтов до +~200 МБ — деплой заметно быстрее. + +## Выключить сайт ```env ENABLED=false ``` -Push → контейнер/файлы удаляются, домен отдаёт 404. Убери строку (или `true`) + -push → поднимается обратно. Том и секреты сохраняются. (Админ может выключить и без -коммита — переменной `ENABLED=false` в Settings → Actions → Variables.) +Push — контейнер и файлы удаляются, домен отдаёт 404. Убрал строку (или `true`) — +вернулся. Том и секреты остаются. Админ может выключить и без коммита — переменной +`ENABLED=false` в Settings → Actions → Variables. ---- +## Переменные `.env` -## Все переменные `.env` +| Ключ | Дефолт | Зачем | +|-----------------|---------|-------| +| `DOMAIN` | — | Обязателен (кроме `ENABLED=false`) | +| `DEPLOY` | auto | `static` / `docker` / `docker-db` | +| `BUILD_DIR` | `dist` | static: папка сборки (в монорепо — путь до вывода приложения) | +| `APP_PORT` | `80` | docker: порт контейнера | +| `WWW` | `auto` | `false` — не подключать www | +| `EXTRA_DOMAINS` | — | доп. домены через запятую, свой SSL на каждый | +| `TWITTER_URL` | — | твиттер/X для админки | +| `INSTALL_SH` | — | static: отдать скрипт на `get.` | +| `RUNTIME_KEYS` | — | docker: какие секреты прокинуть в контейнер | +| `ENABLED` | `true` | `false` — выключить сайт | -| Key | Default | Зачем | -|----------------|---------|-------| -| `DOMAIN` | — | Обязательно (кроме `ENABLED=false`) | -| `DEPLOY` | auto | `static` / `docker` / `docker-db` | -| `BUILD_DIR` | `dist` | static: папка сборки (в монорепо — путь до вывода приложения) | -| `APP_PORT` | `80` | docker: порт контейнера | -| `WWW` | `auto` | `false` — не подключать www | -| `EXTRA_DOMAINS`| — | доп. домены через запятую (тот же сайт, свой SSL на каждый) | -| `TWITTER_URL` | — | твиттер/X проекта для админки (`@handle` / URL) | -| `INSTALL_SH` | — | static: отдать файл-скрипт на `get.` (`curl \| sh`) | -| `RUNTIME_KEYS` | — | docker: список секретов в контейнер | -| `ENABLED` | `true` | `false` — выключить сайт | +## Доступ к гиту ---- - -## Доступ к гиту (один раз) - -- Форж: **https://git.extract.team**, логин — твой username. -- SSH: `git@git.extract.team:2223`. Добавь ключ: **Settings → SSH/GPG Keys**, затем в - `~/.ssh/config`: +- Форж: https://git.extract.team, логин прежний. +- SSH: `git@git.extract.team:2223`, ключ добавляешь в Settings → SSH/GPG Keys. ``` Host git.extract.team Port 2223 User git ``` Ремоут: `git remote set-url origin git@git.extract.team:<логин>/<репа>.git` - (или HTTPS — логин + пароль). ---- +## Если не работает -## Частые проблемы - -- **502 / сайт не грузится сразу после пуша** — деплой ещё идёт или серт выпускается. - Дождись зелёного run в Actions. +- **502 сразу после пуша** — деплой ещё идёт или выпускается сертификат, дождись + зелёного run. - **`build output 'dist' not found`** — не та `BUILD_DIR` (CRA → `build`, Next export → `out`, монорепо → путь до вывода приложения). -- **`no .env` / `no package.json`** — оба файла должны быть в **корне** репозитория. -- **SSL не выпускается** — нет A-записи домена на `51.161.128.94` (в логе деплоя - будет `::warning::`). Пропиши DNS — серт выпустится сам. -- Застрял — пиши админу. - ---- - -_Файлы для копирования — в [`starter/`](starter). Платформенный action — [`action.yml`](action.yml)._ +- **`no .env` / `no package.json`** — оба файла должны лежать в корне репы. +- **Сертификат не выпускается** — нет A-записи домена на `51.161.128.94`. +- Не разобрался — пиши админу.