# Как запустить свой проект на платформе Один файл `.env` + один пуш → сайт на твоём домене с автоматическим SSL. Это единственный репозиторий, который нужно знать: тут и гайд, и файлы стартера, и сам платформенный action. --- ## TL;DR — 4 шага 1. Скопируй в **свой** репозиторий 2 файла из папки [`starter/`](starter) этого репо: - `.forgejo/workflows/deploy.yml` — **не трогать** (тянет платформенный action по ссылке) - `.env` — из `starter/.env.example` Форкать/клонировать этот репо **не надо** — деплой цепляется к твоей репе по самому факту наличия `.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` трогать не нужно никогда. --- ## Как это работает (флоу) ``` 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 живой ``` Один репозиторий = один сайт (один `.env`, один `DOMAIN`). --- ## Тип проекта: static / docker / docker-db Тип определяется полем `DEPLOY` в `.env` или авто-детектом (по наличию `Dockerfile`). ### 1. `static` — фронт (90% проектов) Vite / CRA / Next static export. `bun build` → статика раздаётся напрямую, **без контейнера**. Деплой за секунды; редеплой = только заливка новых файлов. ```env DOMAIN=mysite.com # DEPLOY=static # дефолт, если в корне НЕТ Dockerfile # 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-режиме не работают. ### 2. `docker` — фронт + бэкенд в контейнере Положи `Dockerfile` в корень. Платформа собирает образ, поднимает контейнер и проксирует домен на него. Контейнер должен слушать порт из `APP_PORT`. ```env DOMAIN=mysite.com DEPLOY=docker # или просто наличие Dockerfile APP_PORT=3000 # порт контейнера (default 80) ``` ### 3. `docker-db` — docker + персистентная база (SQLite) Как `docker`, плюс платформа монтирует named-volume в `/data` и прокидывает `DATABASE_URL=file:/data/app.db`. Том переживает редеплои. ```env DOMAIN=mysite.com 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** (или открой в инкогнито). ### Доп. домены — один сайт на нескольких доменах ```env DOMAIN=mysite.com EXTRA_DOMAINS=mysite.io,app.mysite.com,promo.xyz ``` Тот же сайт на всех доменах, на **каждый** — свой авто-SSL. От тебя: A-запись каждого домена на наш IP. `www.` для доп-доменов сам не добавляется — если нужен, впиши `www.mysite.io` явным пунктом. --- ## Twitter / X проекта Хочешь, чтобы в **админ-панели** рядом с сайтом показывался его твиттер — добавь в `.env`: ```env TWITTER_URL=https://x.com/mysite ``` Принимается полный URL, `@handle` или голый `handle` (`TWITTER=` тоже работает). Платформа нормализует и покажет как `𝕏 @mysite`. Ни на деплой, ни на сайт не влияет — только метаданные для админки. --- ## Монорепозиторий Платформа деплоит **один сайт на репозиторий** (один `.env`, один `DOMAIN`). Отдельного поля «папка приложения» нет — сборка идёт от **корня репы**. Поэтому для монорепо: - `.env` и `package.json` (или `Dockerfile`) — **в корне** репозитория. - **static:** корневой скрипт `build` должен собирать нужное приложение (через workspaces / turbo / nx), а `BUILD_DIR` — указывать на его вывод: ```env DOMAIN=mysite.com BUILD_DIR=apps/web/dist # куда собирается целевое приложение ``` ```jsonc // package.json в корне { "scripts": { "build": "npm run build -w apps/web" } } ``` - **docker:** `Dockerfile` в корне, контекст сборки = весь репозиторий, так что он спокойно `COPY apps/web …` и собирает нужный пакет. Задай `APP_PORT`. - Нужно задеплоить **несколько** приложений из одного монорепо на **разные** домены — сделай под каждое отдельный репозиторий (один репо = один сайт). --- ## Установочный сабдомен `curl get. | sh` (self-service) Красивый one-liner установки — делается **сам, без админа**, для `static`-сайтов: 1. Положи скрипт в репу → `public/install.sh` (папка `public/` у Vite попадает в `dist/` как есть → окажется в корне сайта). 2. **DNS:** A-запись `get` → IP платформы (host = `get`). 3. В `.env`: `INSTALL_SH=install.sh` → `git push`. CI сам поднимет `get.`, выпустит SSL и будет отдавать `install.sh` как `text/plain`. Убери `INSTALL_SH` и запушь — сабдомен снимется. В админке этот сабдомен виден в строке «сабдомены». --- ## Секреты (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 ``` ### docker-сайты — кеш слоёв и лёгкие образы Сборка идёт с **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 МБ → деплой заметно быстрее. --- ## Выключить сайт (kill-switch) ```env ENABLED=false ``` Push → контейнер/файлы удаляются, домен отдаёт 404. Убери строку (или `true`) + push → поднимается обратно. Том и секреты сохраняются. (Админ может выключить и без коммита — переменной `ENABLED=false` в Settings → Actions → Variables.) --- ## Все переменные `.env` | 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`: ``` Host git.extract.team Port 2223 User git ``` Ремоут: `git remote set-url origin git@git.extract.team:<логин>/<репа>.git` (или HTTPS — логин + пароль). --- ## Частые проблемы - **502 / сайт не грузится сразу после пуша** — деплой ещё идёт или серт выпускается. Дождись зелёного run в Actions. - **`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)._