Compare commits
No commits in common. "1c5e428d65a523fe78f222c5a719a7c6e0b61e85" and "d30cf4cef484a7ce97bcb0e7f0dc3e91bda4dc8f" have entirely different histories.
1c5e428d65
...
d30cf4cef4
5 changed files with 5 additions and 158 deletions
|
|
@ -1,162 +1,10 @@
|
|||
---
|
||||
title: 'Наш первый блог'
|
||||
thumbnail: 'wave.png'
|
||||
thumbnailAlt: 'Смайлик с махающей рукой'
|
||||
date: '2025-09-30'
|
||||
description: 'Немного о том, как мы делали наш сайт'
|
||||
thumbnail: 'wasd_perelesoq_game_jam_2025.png'
|
||||
date: '2025-09-19'
|
||||
description: 'Немного о самом сайте'
|
||||
---
|
||||
|
||||
# ПРИВЕТ !
|
||||
|
||||
Добро пожаловать на первый блог-пост нашего нового сайта!
|
||||
На момент написания мы ведём работу над кучей проектов, и ввиду общей занятости [народа](/team),
|
||||
работа идёт медленно, да и посты писать особо некому.
|
||||
|
||||
Так давайте расскажем хотя бы про этот сайт!
|
||||
|
||||
## Вводные данные
|
||||
|
||||
Сайт написан на TypeScript с применением фреймворка [SvelteKit](https://svelte.dev). Некоторые отзываются о нём, мягко говоря, не лестно,
|
||||
но нас Svelte подкупил тем, что все страницы, и даже ручки API, можно предварительно отрендерить по файлам, получив полностью статичный сайт!
|
||||
А фреймворк [Tailwind CSS](https://tailwindcss.com) даёт нам удобные стили и неплохие дефолтные стили с цветовой палитрой. Пока у нас нет
|
||||
_полноценных_ веб-дизайнеров, довольствуемся их встроенной палитрой.
|
||||
|
||||
Это решение нам досталось в наследство от сайта [Small Fish](https://smallfi.sh). Впрочем, как и половина их кодовой базы:
|
||||
для быстрого старта с разрешения команды мы воспользовались [их исходниками](https://github.com/Small-Fish-Dev/small-fish-dev.github.io).
|
||||
Но вы не подумайте, что сайт слизан подчистую! Как минимум в процессе обновления версий фреймворков пришлось провести небольшой рефакторинг,
|
||||
а потом ещё пошли наши прочие хотелки (об этом дальше), и можно смело сказать, что спустя
|
||||
[сотню коммитов](https://git.teasanctuary.ru/TeaSanctuary/teasanctuary.ru/commits/branch/master),
|
||||
из общего остались только фреймворки и общая структура сайта.
|
||||
|
||||
В общем-то, главная причина почему мы взяли их сайт за основу, а не написали вручную — это удобная **система блогов**.
|
||||
|
||||
## Собственно, блоги
|
||||
|
||||
Конечно, в нашей небольшой команде абсолютно все компьютерно грамотные ([вы можете это исправить!](https://teasanctuary.ru/discord)), и никому не составит
|
||||
труда настрочить текст вот так, просто HTML-ом. Но как же хочется писать блог в удобном WYSIWYG-редакторе, или хотя бы в простом формате!
|
||||
|
||||
На помощь приходит библиотека [mdsvex](https://mdsvex.pngwn.io/). Она по сути превращает `.md` файлы в Svelte-компоненты, которые можно потом
|
||||
встроить куда угодно. Здорово!
|
||||
|
||||
Только надо решить парочку проблем. Во-первых, все элементы, вроде ссылок или картинок, переводятся в обычные HTML5-объекты. А у нас есть свои собственные,
|
||||
особые ссылки ([вот такие!](https://хамяк.рф)). Благо, mdsvex позволяет просто использовать Svelte-компоненты, если правильно настроить авто-импортирование.
|
||||
_Но это же не удобно!_ Это ведь надо знать, что есть _такие_ ссылки, а есть _не_ такие. Плюс, это будет выглядеть уродливо в тех же WYSIWYG-редакторах по
|
||||
типу [Obsidian](https://obsidian.md).
|
||||
|
||||
Решилось это с помощью системы layout-ов внутри mdsvex: можно задавать шаблоны, которые будут натянуты вокруг преобразованного Markdown-текста. Тут же
|
||||
можно экспортировать особый объект, где к названию каждого стандартного HTML-элемента можно поставить ссылку на свой собственный класс. Таким образом,
|
||||
каждый `<a href="...">...</a>` превращается в `<SocialHyperlink href="...">...</SocialHyperlink>`, без лишних телодвижений писателя блога.
|
||||
|
||||
Отлично, у нас есть отдельные блоги, теперь хочется собрать их все вместе, и желательно рассортировать по дате. И вот тут всплывает вторая проблема:
|
||||
система Git, которой мы пользуемся, просто не хранит дату создания и изменения файла! Надо это всё записать куда-то в метаданные. Самое простое решение:
|
||||
присобачить к каждому Markdown-блогу по JSON-у. Но есть решение ещё лучше, оно
|
||||
называется [frontmatter YAML](https://docs.github.com/ru/contributing/writing-for-github-docs/using-yaml-frontmatter) — де-факто стандарт встраивания
|
||||
любых метаданных в файл Markdown.
|
||||
|
||||
Здесь мы пишем дату в формате ISO, ну и на сдачу можно добавить кучу других данных. За примером далеко идти не надо: вот вам заголовок этой же заметки:
|
||||
|
||||
```md
|
||||
---
|
||||
title: 'Наш первый блог'
|
||||
thumbnail: 'wave.png'
|
||||
thumbnailAlt: 'Смайлик с махающей рукой'
|
||||
date: '2025-09-30'
|
||||
description: 'Немного о том, как мы делали наш сайт'
|
||||
---
|
||||
```
|
||||
|
||||
Тут вам и дата, и описание, и даже красивая миниатюра! И сюда можно вставить вообще всё, что поддерживает YAML, в том числе списки и булевы переменные.
|
||||
Зачем? Ну-у-у... Потом разберёмся🙂
|
||||
|
||||
Важно то, что теперь с помощью `import.meta.glob('/src/blogs/*.md')` можно просто пройтись по всем постам, выдернуть метаданные и отсортировать их по дате.
|
||||
|
||||
Эта система нам пригодилась в ещё одной вещи, которую, казалось бы, давно забыли, а зря!
|
||||
|
||||
## RSS-лента
|
||||
|
||||
Да, мы сами в шоке, но даже после становления фактической олигополии интернета Google-Twitter-ЧёЕщёТам, RSS-ленты продолжали использоваться для подкастов.
|
||||
А с недавней волной популярности децентрализации ([Mastodon](https://joinmastodon.org/ru), [BlueSky](https://bsky.social), [NeoCities](https://neocities.org/) и прочие),
|
||||
в моду вернулись маленькие бложики. _Да, мы знаем, что они никуда и не уходили, можете не писать гневные комментарии. Ах, да, их некуда писать. Ой._
|
||||
|
||||
RSS в этом плане нам очень удобен, во-первых, потому, что это просто XML-файл, и его можно просто куда-то положить, а во-вторых, на него могут подписаться не только люди,
|
||||
но и машины. В нашем случае это Discord-бот [FeedCord](https://github.com/Qolors/FeedCord/). Ура, можно не городить свой воркфлоу, дёргающий [веб-хуки](https://discord.com/developers/docs/resources/webhook)!
|
||||
|
||||
Поскольку SvelteKit — это fullstack-фреймворк, на нём же напишем простую ручку [rss.xml](https://teasanctuary.ru/blog/rss.xml). Это будет простая функция `GET()`,
|
||||
возвращающая текст, ручками слепленный в что-то приблизительно похожее на XML. Но погодите, наш сайт же статичный, как мы будем вызывать эту JS-функцию!
|
||||
|
||||
_Внимание, фокус!_ Мы берём обычный файл `src/routes/blog/rss.xml/+server.ts`, и добавляем в него волшебную строчку:
|
||||
|
||||
```ts
|
||||
export const prerender = true;
|
||||
```
|
||||
|
||||
При сборке SvelteKit видит, что страницу можно сгенерировать заранее, выполняет весь этот код (а там то же самое, что в странице со списком блог-постов, но очевидно, в формате XML),
|
||||
и в папке `build/` создаёт файл `rss.xml`. _Та-да!_
|
||||
|
||||
Конечно, это отвратительный хак, но мне честно не хотелось добавлять этап постобработки, у нас и так там стоит [генератор sitemap.xml](https://github.com/bartholomej/svelte-sitemap).
|
||||
Да, ещё одна забытая технология. Надеюсь, хоть так [чёртовы ЖиПиТи-боты](https://git.teasanctuary.ru/TeaSanctuary/teasanctuary.ru/src/branch/master/static/robots.txt) перестанут
|
||||
вслепую тыкаться по нашим сайтам, особенно по нашему инстансу Forgejo.
|
||||
|
||||
## Деплой
|
||||
|
||||
Сайт Small Fish, упомянутый в самом начале, располагается на GitHub Pages: CI/CD хук собирает сайт, запаковывает его в zip-архив и публикует на своём домене `.github.io`,
|
||||
который ещё файлом `CNAME` можно поменять на абсолютно любой.
|
||||
|
||||
Мы же хотим оставаться независимыми, в конце концов, зачем мы платим за серваки? Вот Nginx в контейнере, вот SFTP, закидывай и всё!
|
||||
|
||||
Так и было, когда наш сайт был проще, _намного проще_ (умоляю вас, **не смотрите** в Web-архив!!!), но потом захотелось CD как у больших дядь.
|
||||
На это есть [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/reference/), местный аналог GitHub Actions. Пишешь скрипт, пушишь коммит,
|
||||
всё будет сделано за тебя. Так, стоп, а почему не работает? Стоп, надо ещё хостить свой раннер?
|
||||
|
||||
Да уж, совсем забыл упомянуть... На нашем основном сервере с доменом [teasanctuary.ru](https://teasanctuary.ru) всего **один гигабайт ОЗУ**. **Один**. Сюда еле влез Forgejo
|
||||
с несколькими сервисами для нашего внутреннего пользования, а тут ещё надо запускать NodeJs, который запросто выжрет остаток и не подавится.
|
||||
|
||||
Как быть?..
|
||||
|
||||
Помощь пришла откуда не ждали. Я, **rndtrash**, некоторое время назад переехал на другую квартиру, и пока я разбирал вещи, среди ящичков моему взору предстала ма-а-аленькая чёрная
|
||||
коробочка с логотипом _некоторого пчелиного оператора_. Как позже выяснилось, это Smart TV-приставка, которую провайдер просто отказался забирать назад, ну а хозяевам квартиры она
|
||||
просто не нужна.
|
||||
|
||||
Меня накрыло в пот, и спустя один запрос в поисковике, один тред на 4PDA, один звонок владельцам квартиры и тридцать минут беготни в магазин электроники,
|
||||
я понял, что передо мной лежал халявный сервер на Linux с ARMv8 процессором на 1.5 ГГц с одним гигабайтом оперативки.
|
||||
|
||||
**ДЖЕКПОТ!!! 🎰🎰🎰**
|
||||
|
||||
Дальше дело за малым: я просверлил несколько вентиляционных отверстий, чтобы коробка совсем не прокоптилась (вы бы видели местный охлад...), разрегистрировал её в нашем Forgejo,
|
||||
и проработал план действий. Ввиду простоты сайта, наш [deploy-скрипт](https://git.teasanctuary.ru/TeaSanctuary/teasanctuary.ru/src/branch/master/.forgejo/workflows/deploy.yaml)
|
||||
предельно прост:
|
||||
|
||||
1. Ставим Ноду, клонируем репозиторий, устанавливаем Npm-зависимости
|
||||
2. Кешируем зависимости, потому что это всё непотребство работает от _моего_ Интернета
|
||||
3. Собираем сайт командой `npm run build` и формируем артефакт — архив с сайтом
|
||||
4. Достаём SSH-ключ к основному серверу из секретного хранилища и регистрируем в SSH-агенте. Этот ключ имеет доступ только к особому пользователю,
|
||||
который в свою очередь имеет доступ только к особой папке только для сайта
|
||||
5. С помощью `rsync`, полностью заменяем содержимое удалённой папки на содержимое артефакта, сделанного на этапе **3**.
|
||||
Это сделано на случай, если в репозитории случайно оказалось что-то непубличное👻
|
||||
|
||||
В этом плане есть только один прокол, причём очень важный.
|
||||
|
||||
## SSL
|
||||
|
||||
Тот самый заветный зелёный замочек у нас был с самого начала, но в какой-то момент он пропал. Покопавшись в логах [Certbot](https://certbot.eff.org/), мы с ужасом обнаружили,
|
||||
что с переездом всего ПО в Podman-контейнер, он вот уже месяц как не мог запустить свой собственный сервер для сертификации.
|
||||
|
||||
Вообще, процесс получения сертификата [Let's Encrypt](https://letsencrypt.org/) происходит вот так: некий бот раз в два-три месяца запускает свой собственный сервер на порту 80,
|
||||
и с его помощью раздаёт особый файл-ключ по адресу `домен/.well-known/acme-challenge/случайные-буквы`. Главный сервер Let's Encrypt стучится по этому адресу, тем самым удостоверяется,
|
||||
что к нему обратился действительно владелец домена, а не случайный прохожий, и наконец, регистрирует обновлённый файл сертификата.
|
||||
|
||||
У нас опять всё не как у людей, поэтому мы сделали вот как: Certbot имеет особый режим, где он просто кладёт нужный файл, а дальше системный администратор как-то сам решает, каким
|
||||
образом поднять 80 порт. Мы не можем закинуть этот файл прямо в папку сайта, потому что наш deploy-скрипт полностью уничтожает постороннее содержимое, поэтому мы сделали отдельную
|
||||
папку, а Nginx в свою очередь сконфигурировали так, что все обращения по пути `/.well-known/acme-challenge` идут мимо папки сайта.
|
||||
|
||||
## Финал
|
||||
|
||||
Вот столько трудностей нам пришлось преодолеть, чтобы довести сайт хотя бы до такого состояния. Если что, на момент написания работает только главная страница, и собственно блоги.
|
||||
Остаётся надеяться, что написание остальных страниц не составит особых усилий.
|
||||
|
||||
Если вам понравился наш блог, то подписывайтесь на наш [RSS-фид](https://teasanctuary.ru/blog/rss.xml), и обязательно загляните в
|
||||
[наше сообщество в Discord](https://teasanctuary.ru/discord).
|
||||
|
||||
Ещё увидимся!
|
||||
|
||||
- Команда Tea Sanctuary, 2025
|
||||
Добро пожаловать на наш первый блог-пост!
|
||||
|
|
@ -10,4 +10,4 @@ projects: ['ts-hldm']
|
|||
|
||||
Добро пожаловать на наш первый блог-пост!
|
||||
|
||||
<Img src="./wasd_perelesoq_game_jam_2025.png" />
|
||||
<Img src="wasd_perelesoq_game_jam_2025.png" />
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ const icons: Record<string, string> = {
|
|||
'github.com': 'simple-icons:github',
|
||||
'youtube.com': 'simple-icons:youtube',
|
||||
'itch.io': 'simple-icons:itchdotio',
|
||||
'discord.com': 'simple-icons:discord',
|
||||
'discord.gg': 'simple-icons:discord',
|
||||
'gamebanana.com': 'simple-icons:gamebanana',
|
||||
'bsky.app': 'simple-icons:bluesky',
|
||||
|
|
|
|||
BIN
static/blog/hello_world/wasd_perelesoq_game_jam_2025.png
Normal file
BIN
static/blog/hello_world/wasd_perelesoq_game_jam_2025.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 449 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB |
Loading…
Add table
Add a link
Reference in a new issue