Go + Astro CMS

Как работи системата за сайтове, която не яде ресурси когато не се ползва

1 Какво е това накратко?

Система за правене на уебсайтове. Състои се от две части:

Go Backend (бекенд)

Админ панел, където редактираш съдържание. Написан на езика Go. Компилира се до един файл. Пази данните в SQLite — обикновен файл на диска, не е нужен отделен database сървър.

Astro Frontend (фронтенд)

Генератор за сайта. Взема данните от Go и създава готови HTML файлове. Написан на JavaScript/Astro. Работи само при "Build" — после спира.

💡 Ключовата идея

Публичният сайт са готови HTML файлове. Когато посетител отвори сайта, се чете файл от диска — никакъв код не се изпълнява. Не работи нито Go, нито Node.js, нито PHP, нито нищо друго. Само nginx подава файла.

2 Кои програми участват?

Програма Какво прави Кога работи RAM
nginx Подава HTML файлове на посетители.
Пренасочва /admin и /api заявки към Go.
Винаги ~5 MB
systemd Слуша порта на Go. Когато дойде заявка — буди Go. Винаги (част от Linux) ~0 MB
(вече работи)
Go CMS Админ панел, обработка на форми, API, стартиране на build. Само при заявка.
Спи след 2 мин.
~20 MB
(когато работи)
Node.js / Astro Генерира HTML файлове от данните. Само при Build.
Секунди, после спира.
~100 MB
(само при build)
SQLite Базата данни. Просто файл — site.db Не е програма.
Файл на диска.
0 MB
⏰ 99% от времето

Работи САМО nginx (~5 MB). Всичко останало спи. Ако имаш 20 сайта на сървъра — пак само nginx работи, защото всички Go процеси спят. Общо ~5 MB RAM за 20 сайта.

3 Какво трябва на сървъра?

Софтуер За какво Как се инсталира
nginx Уеб сървър (подава файлове, proxy) apt install nginx
Node.js Нужен за Astro build (генериране на HTML) apt install nodejs npm
rsync Копира генерираните файлове в публичната папка apt install rsync
systemd Управлява Go процеса (буди/спира) Вече е на всеки Linux
❓ Трябва ли Go compiler на сървъра?

Не задължително. Go binary-то може да се компилира на друг компютър и да се копира готово. Компилираш на лаптопа си с GOOS=linux go build и качваш 1 файл. Но ако имаш Go на сървъра — може и там.

❓ Трябва ли Node.js да работи постоянно?

НЕ. Node.js се стартира САМО когато админът натисне "Build". Работи 5-30 секунди (генерира HTML файловете) и спира. Не работи постоянно. Не яде RAM когато не се ползва.

❓ Трябва ли Docker?

НЕ. Няма Docker, няма контейнери. Всичко работи директно на операционната система.

❓ Трябва ли отделен database сървър?

НЕ. Базата данни е SQLite — един файл (site.db) вътре в папката на проекта. Няма MySQL, PostgreSQL, нищо друго. Файлът може да се копира на USB, по email — това е цялата база.

4 Какво се случва при различни ситуации

Ситуация А: Посетител отваря страница

Някой пише https://example.com/about в браузъра.

Браузърът праща заявка към nginx nginx получава: "Искам /about"
nginx проверява правилата си /about — не започва с /admin или /api → значи е статичен файл
nginx търси файла на диска Търси: public_html/about/index.html → Намерен!
nginx връща файла HTML файлът отива при браузъра. Готово.
Време: < 5ms Go: Не участва. Не се буди. Не знае. Node.js: Не участва. Не се буди. Не знае. RAM: 0 допълнителна (nginx вече работи)

Ситуация Б: Посетител праща контактна форма

Посетител попълва форма за контакт и натиска "Изпрати".

Браузърът праща POST заявка POST /api/form/contact с данни от формата (име, имейл, съобщение)
nginx получава заявката /api/* → не е статичен файл, трябва Go. Пренасочва към порт 8080.
systemd буди Go systemd слуша порт 8080. Вижда връзка → стартира Go binary-то. Отнема ~100ms (0.1 секунда). Посетителят не усеща забавяне.
Go обработва формата Проверява rate limit (макс 1 заявка на 30 сек от един IP). Записва данните в SQLite таблица "submissions".
Go връща отговор {"ok": "saved"} → Браузърът показва "Съобщението е изпратено!"
Go чака 120 секунди... Ако няма нови заявки за 2 минути → Go спира сам → 0 RAM, 0 CPU. Ако дойде нова заявка → таймерът се нулира.
✅ Формите работят дори когато Go спи!

systemd автоматично буди Go при заявка. Посетителят не знае, че Go е спял. Вижда нормална бърза реакция.

Ситуация В: Админът влиза в панела

Админът отваря https://example.com/admin/

nginx получава /admin/ заявка /admin/* → пренасочва към порт 8080
systemd буди Go (ако спи) Go стартира за ~100ms. Ако вече работи — стъпката се пропуска.
Go проверява дали е логнат Търси JWT cookie в браузъра. Няма cookie → показва login страница.
Админът въвежда потребител и парола Go проверява с bcrypt (защитен алгоритъм за пароли). Ако е правилно → създава JWT token и го записва в cookie.
Админът вижда Dashboard Списък с всички страници. Може да създава, редактира, трие.

Докато админът работи (кликва, навигира) — всяко действие нулира idle таймера. Go не спи докато някой го ползва.

Ситуация Г: Админът редактира и публикува страница

Админът отваря страница за редакция /admin/edit/5 → Go зарежда страница #5 от SQLite и показва формата.
Админът променя съдържанието и натиска Save Go записва промените в SQLite. Маркира страницата: needs_rebuild = 1 (трябва rebuild).
Админът натиска Build Go проверява кои страници имат needs_rebuild = 1.
Go стартира Astro build Изпълнява командата npx astro build с указание кои страници да се генерират. Node.js стартира, Astro работи.
Astro генерира HTML файлове Astro пита Go API: "Дай ми данните за всички страници" → Go връща JSON → Astro създава HTML файлове САМО за променените страници.
rsync копира файловете Новите HTML файлове се копират в public_html/ (без да се трият останалите).
Готово! Node.js спира. Go маркира страниците: needs_rebuild = 0. Посетителите вече виждат новото съдържание.
⚡ Selective Build — защо е бързо

Ако имаш 50 страници и промениш само 2 — Astro генерира САМО тези 2. Не ги прави всичките 50. Затова отнема 5 секунди вместо 30+.

Full rebuild (бутон "Full rebuild") генерира ВСИЧКИ страници. Ползва се при промяна на дизайн или при начален deploy.

5 Как точно работи "заспиването"

Това е най-важната функционалност. Ето я подробно:

Какво е systemd socket activation?

Вместо Go да слуша порт 8080 постоянно (и да яде RAM), systemd (вградена програма в Linux) слуша вместо него. Когато дойде заявка — systemd стартира Go и му подава връзката.

Конфигурация — 2 файла: cms.socket — казва на systemd: "Слушай порт 8080" ┌──────────────────────────────────────┐ │ [Socket] │ │ ListenStream=127.0.0.1:8080 │ │ Accept=no │ └──────────────────────────────────────┘ cms.service — казва на systemd: "Когато дойде връзка, стартирай това" ┌──────────────────────────────────────┐ │ [Service] │ │ ExecStart=/var/www/.../backend/cms │ │ Restart=on-failure │ └──────────────────────────────────────┘

Idle timeout — как Go решава кога да спре

Таймлайн: 0:00 Заявка пристига → systemd буди Go → таймер: 120 сек 0:05 Нова заявка → таймер НУЛИРАН: 120 сек 0:30 Нова заявка → таймер НУЛИРАН: 120 сек 0:45 Нова заявка → таймер НУЛИРАН: 120 сек ... 5:00 Последната заявка → таймер: 120 сек 5:30 ... тишина ... таймер: 90 сек 6:00 ... тишина ... таймер: 60 сек 6:30 ... тишина ... таймер: 30 сек 7:00 120 сек без заявки → Go СПИРА → 0 RAM, 0 CPU ... (часове/дни по-късно) Нова заявка → systemd буди Go → работи отново

Специален случай: Build

Ако Go стартира Astro build (5-30 секунди), idle таймерът се паузира. Иначе може Go да спре по средата на build-а. След build-а — таймерът се възобновява.

6 Файлова структура

Цялият проект живее в една папка. Ето какво има вътре:

/var/www/example.com/ ← цялата папка на проекта │ ├── backend/ ← Go код (CMS сървър) │ ├── main.go ← входна точка, конфигурация │ ├── server.go ← HTTP сървър, socket activation │ ├── db.go ← база данни, таблици │ ├── auth.go ← login, пароли, JWT │ ├── admin.go ← админ панел │ ├── api.go ← API (данни за Astro) │ ├── build.go ← стартиране на Astro build │ ├── media.go ← качване на файлове │ ├── public.go ← обработка на форми │ ├── cms ← компилиран binary (~15MB) │ └── embed/ ← вградени в binary-то │ ├── templates/ ← HTML за админ панела │ └── static/ ← CSS и JS за админа │ ├── frontend/ ← Astro код (генератор на сайта) │ ├── astro.config.mjs ← Astro конфигурация │ ├── package.json ← Node.js зависимости │ └── src/ │ ├── pages/ ← маршрути (URL-и) │ ├── blocks/ ← визуални блокове │ └── layouts/ ← HTML структура │ ├── data/ ← база данни │ └── site.db ← SQLite файл (ЦЯЛАТА база) │ ├── public_html/ ← публичният сайт │ ├── index.html ← начална страница │ ├── about/index.html ← /about │ ├── contacts/index.html ← /contacts │ └── images/uploads/ ← качени картинки │ └── deploy/ ← конфигурации за сървъра ├── setup.sh ← инсталира всичко с 1 команда ├── cms.socket ← systemd socket ├── cms.service ← systemd service └── nginx.conf ← nginx конфигурация

7 Кой процес кога работи

Ситуация nginx Go CMS Node/Astro SQLite
Посетител чете страница ✅ сервира HTML 💤 спи 💤 спи — файл
Посетител праща форма ✅ proxy ✅ буди се 💤 спи ✅ пише
Админ влиза ✅ proxy ✅ буди се 💤 спи ✅ чете
Админ запазва страница ✅ proxy ✅ работи 💤 спи ✅ пише
Админ натиска Build ✅ proxy ✅ стартира build ✅ билдва HTML ✅ Go чете
Покой (99% от времето) ✅ слуша ❌ не съществува ❌ не съществува — файл

8 Пълна схема

СЪРВЪР ┌─────────────────────────────────────────────────────────────────┐ │ │ │ ┌──────────┐ ┌─────────────────────────────────────────┐ │ │ │ │ │ nginx (винаги работи) │ │ │ │ Посетител├────►│ │ │ │ │ │ │ /* ──► public_html/ (статични HTML) │ │ │ └──────────┘ │ │ │ │ │ /admin/* ──┐ │ │ │ ┌──────────┐ │ /api/* ──┤ proxy към порт 8080 │ │ │ │ │ └─────────────┼───────────────────────────┘ │ │ │ Админ ├──────────────────►│ │ │ │ │ ▼ │ │ └──────────┘ ┌─────────────────────────────────────────┐ │ │ │ systemd (слуша порт 8080) │ │ │ │ │ │ │ │ Заявка? ──► Стартирай Go binary │ │ │ └──────────────────┬──────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────┐ │ │ │ Go CMS (on-demand) │ │ │ │ │ │ │ │ • Админ панел (login, edit, save) │ │ │ │ • API (данни за Astro) │ │ │ │ • Форми (contact, newsletter) │ │ │ │ • Media upload │ │ │ │ • Build trigger │ │ │ │ │ │ │ │ SQLite: data/site.db │ │ │ │ │ │ │ │ Idle 120 сек → СПИРА │ │ │ └──────────────────┬──────────────────────┘ │ │ │ │ │ (при Build) │ │ │ ▼ │ │ ┌─────────────────────────────────────────┐ │ │ │ Node.js / Astro (при build) │ │ │ │ │ │ │ │ 1. Fetch данни от Go API │ │ │ │ 2. Генерирай HTML файлове │ │ │ │ 3. rsync → public_html/ │ │ │ │ 4. СПРИ (готово) │ │ │ └─────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘

9 Инсталация от нулата

Стъпка 1: Подготви сървъра

$ apt update $ apt install nginx nodejs npm rsync # Проверка: $ nginx -v # nginx version: nginx/1.x $ node --version # v18.x или по-нова

Стъпка 2: Копирай проекта на сървъра

$ scp -r project/ root@server:/var/www/example.com/

Стъпка 3: Компилирай Go binary

# Вариант А: На сървъра (ако има Go) $ cd /var/www/example.com/backend $ go build -o cms . # Вариант Б: На лаптопа (cross-compile) $ GOOS=linux GOARCH=amd64 go build -o cms . $ scp cms root@server:/var/www/example.com/backend/

Стъпка 4: Инсталирай Node зависимости

$ cd /var/www/example.com/frontend $ npm install # Инсталира САМО astro (без native модули, бързо)

Стъпка 5: Пусни setup.sh

$ cd /var/www/example.com/deploy $ ./setup.sh example.com 8080 # Това прави автоматично: # 1. Създава systemd socket (слуша порт 8080) # 2. Създава systemd service (стартира Go при заявка) # 3. Конфигурира nginx (static + proxy) # 4. Генерира JWT secret

Стъпка 6: SSL сертификат

$ certbot --nginx -d example.com

Стъпка 7: Влез в админа

Отвори: https://example.com/admin/ Login: admin / admin123 # Създай страници → натисни Build → сайтът е жив!

10 Преместване на друг сървър

Копирай цялата папка scp -r /var/www/example.com/ root@new-server:/var/www/
Всичко е вътре: код, база данни, качени файлове, генериран сайт.
На новия сървър: инсталирай nginx, node, rsync apt install nginx nodejs npm rsync
Пусни setup.sh cd deploy && ./setup.sh example.com 8080
Готово Сайтът работи на новия сървър. Същите данни, същите потребители, всичко.

11 Множество сайтове на един сървър

Сайт Порт systemd socket ─────────────────────── ────── ───────────────────────── example.com 8080 example-com-cms.socket portfolio.bg 8081 portfolio-bg-cms.socket shop.store 8082 shop-store-cms.socket gallery.art 8083 gallery-art-cms.socket Всеки сайт има собствена: • Папка (/var/www/domain/) • Go binary • SQLite база • Astro frontend • nginx конфигурация • systemd socket + service В покой: ВСИЧКИ Go процеси спят. RAM = само nginx ≈ 5 MB за ВСИЧКИ сайтове.

12 Блок система (секции)

Съдържанието на всяка страница се състои от блокове (секции). Всеки блок е визуален елемент:

Блок Описание Файл
hero Голям банер с заглавие, подзаглавие, фоново изображение WfHero.astro
text Текстов блок с HTML съдържание WfText.astro
image Картинка с описание WfImage.astro
gallery Мрежа от картинки (галерия) WfGallery.astro
contact Контактна форма (име, имейл, съобщение) WfContact.astro

В базата данни блоковете се пазят като JSON:

[ { "type": "hero", "data": { "title": "Добре дошли", "image": "/images/uploads/hero.jpg" } }, { "type": "text", "data": { "content": "<p>Ние сме компания...</p>" } }, { "type": "gallery", "data": { "images": [ { "url": "/images/uploads/1.jpg" }, { "url": "/images/uploads/2.jpg" } ] } } ]

Добавяне на нов тип блок:

  1. Създай frontend/src/blocks/WfNewBlock.astro
  2. Импортирай го в WfRender.astro
  3. Ползвай го в страница: {"type": "newblock", "data": {...}}

Обобщение

Един Go binary + Astro за генерация на HTML + nginx за сервиране + systemd за on-demand стартиране. Нищо не работи когато не се ползва. Всичко е в една папка. Мести се с scp.

Нужен софтуер: nginx, Node.js, rsync, systemd

НЕ трябва: Docker, MySQL, Go compiler*, PHP, Redis, или нещо друго

* Go compiler трябва само за компилация на binary-то. Може да се направи на друг компютър.