Демка — это маленький сайт или сервис, который живёт на своём поддомене
slop-<имя>.slophub.ru. Залил zip — получил адрес, который можно кинуть в чат.
Демки лежат выключенными и поднимаются по первому запросу: первый гость видит
заставку пару секунд, дальше всё быстро. Молчащая дольше двенадцати часов
засыпает обратно. Смысл простой — десяток заброшенных демок не должен занимать
память круглые сутки.
Заливка — в админке admin.slophub.ru, раздел
«Демки». Витрина — slophub.ru/slops.
Заливать может любой, у кого есть вход. У автора демка сначала уходит в
очередь: открывается по адресу сразу, а в витрине появляется после того, как
её посмотрит редактор.
Вид решает ровно две вещи: какой образ взять и какую команду в нём выполнить.
Адрес, засыпание и пробуждение у всех одинаковые.
kind |
Что происходит | Команда по умолчанию |
|---|---|---|
static |
файлы просто раздаются | — |
node |
запускается ваш код | node index.js |
python |
запускается ваш код | python main.py |
bun |
запускается ваш код | bun index.ts |
static — это HTML, CSS и картинки. Ничего на сервере не исполняется, весь код
работает в браузере гостя. В корне архива обязан быть index.html.
Остальные три запускают ваш код у нас на машине. Приложение должно слушать
0.0.0.0 и порт из переменной PORT — не localhost, иначе снаружи
до него никто не достучится.
slop.jsonКладётся в корень архива. Демка описывает себя сама, и поля в форме заливки
заполнять не нужно.
{
"name": "pixel-sea",
"title": "Пиксельное море",
"description": "Волны на канвасе, мышкой можно шевелить",
"kind": "static",
"build": true
}
Все поля необязательны, и самого файла может не быть — тогда всё заполняется
руками в форме.
| Поле | Что это |
|---|---|
name |
Имя. Из него собирается адрес slop-<имя>.slophub.ru. Строчные латинские буквы, цифры и дефис, от 2 до 40 символов, дефис не по краям |
title |
Название для витрины |
description |
Пара строк о том, что это |
kind |
static, node, python или bun. Не указан — static |
port |
На каком порту слушает приложение. Не указан — 3000 |
command |
Чем запускать. Пусто — умолчание вида. У static не используется |
build |
true — в архиве исходники, их надо собрать |
build_command |
Чем собирать. Пусто — умолчание (см. ниже) |
build_output |
Что забрать после сборки. Пусто — dist |
build_with |
В каком рантайме собирать: node, python или bun |
Две вещи, которые стоит знать заранее.
Форма важнее манифеста. Если вы заполнили поле руками в админке, оно
перебьёт то, что написано в slop.json. Иначе правка в админке молча терялась
бы при следующей заливке.
Незнакомое поле — отказ. Написали "titel" вместо "title" — архив не
примут и скажут, какое поле не понято. Это нарочно: молча проглоченная опечатка
оставила бы вас гадать, почему настройка не применилась.
Собирать самому необязательно. "build": true означает, что в архиве лежит
проект, а не результат: тогда его соберёт разовый контейнер, и демкой станет то,
что окажется в каталоге результата.
архив с исходниками
└─ контейнер-сборщик: ставит зависимости, выполняет сборку
└─ каталог из build_output → это и есть демка
Сборка идёт минуты. Ответ на заливку приходит сразу — демка появляется в
админке со статусом «собираю», а состояние и лог видны в её карточке.
Если build_command не указан, берётся умолчание по build_with:
build_with |
Команда по умолчанию | build_output по умолчанию |
|---|---|---|
node |
поставить зависимости и выполнить npm run build, если такой скрипт есть |
dist |
bun |
то же через bun install и bun run |
dist |
python |
pip install --no-cache-dir --target . -r requirements.txt |
. |
Зависимости ставятся npm ci, когда рядом лежит package-lock.json, и
npm install, когда его нет. Класть lock-файл в архив не обязательно.
build_with не указан — берётся вид демки, а у static это node: фронт
почти всегда собирается им. Статику можно собрать и через bun — напишите
"build_with": "bun".
build_output — каталог, который станет демкой. По умолчанию dist, потому
что туда собирают Vite, webpack и почти все остальные.
Точка означает всё дерево целиком. Так собираются бэкенды: npm install
кладёт зависимости в node_modules рядом с кодом, и в демку должно уехать всё,
а не отдельная папка.
Всё, что не попало в build_output, стирается. Исходники и node_modules не
хранятся: демка — это результат сборки.
Кнопки «пересобрать» нет, и это не недоделка. Исходники после сборки
стираются, пересобирать было бы нечего. Чтобы пересобрать — залейте архив
заново.
Прежняя версия демки продолжает работать. Подмена происходит одним движением в
самом конце и только при удачной сборке — сломанная пересборка не гасит то, что
уже открыто у людей в браузере. В карточке при этом будет «не собралось» и лог.
Самый частый случай. Заливаете проект как есть, без node_modules.
{
"name": "moya-igra",
"title": "Моя игра",
"kind": "static",
"build": true
}
Больше ничего не нужно: соберётся npm run build, заберётся dist.
Собрали сами или пишете на голом HTML — манифест можно не класть вовсе. В корне
архива нужен index.html.
{ "name": "landing", "title": "Лендинг", "kind": "static" }
{
"name": "moy-api",
"title": "Мой API",
"kind": "node",
"build": true,
"build_output": ".",
"command": "node server.js"
}
build_output: "." — чтобы node_modules уехали в демку вместе с кодом.
const http = require('http')
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' })
res.end('привет')
}).listen(process.env.PORT || 3000, '0.0.0.0')
{
"name": "moy-bot",
"title": "Мой сервис",
"kind": "python",
"build": true,
"command": "python main.py"
}
Рядом положите requirements.txt. У питона build_output по умолчанию уже .
— своего каталога со сборкой у него нет.
Если библиотек не нужно, сборка не нужна тоже: заливайте код, и всё.
{ "name": "echo", "title": "Эхо", "kind": "node" }
Это не придирки, а рамки, в которых чужой код пускают на общую машину.
/tmp живёт в памяти и исчезает вместе с контейнером. Записанное приЧто демка может — видеть соседние демки по сети и работать до модерации.
Поэтому заливка открыта только тем, кого позвали.
| Архив | 50 МБ |
| Распакованное | 200 МБ, до 4000 файлов |
| Собранное | 500 МБ, до 60 000 файлов |
| Время сборки | 10 минут |
| Память демке / сборке | 256 МБ / 1 ГБ |
Четыре тысячи файлов — это не про node_modules: его в архив класть не надо,
для этого и есть сборка.
«в архиве исходники, а не собранная страница» — статика, у которой
index.html ссылается на .tsx, .jsx или .vue. Браузер такие файлы не
исполняет ни при каких условиях. Либо соберите проект сами и залейте dist,
либо поставьте "build": true и пусть соберётся у нас.
«в архиве нет index.html» — он должен лежать именно в корне. Одну лишнюю
папку-обёртку вокруг мы срежем сами, две — уже нет.
«сборка прошла, но каталога dist после неё нет» — сборщик кладёт
результат в другое место. Посмотрите, куда именно, и напишите это в
build_output.
Сборка упала — откройте лог сборки в карточке демки, там ровно то, что
написал npm или pip.
Демка открывается пустой или отдаёт 502 — откройте логи контейнера в той же
карточке. Самая частая причина у бэкендов: приложение слушает 127.0.0.1
вместо 0.0.0.0 или свой порт вместо PORT.
Адрес не открывается совсем — возможно, для демки ещё не выписан
сертификат. В карточке есть кнопка «Завести адрес», это занимает до минуты.
Скормите модели этот блок — в нём всё, что нужно, чтобы получился заливаемый
архив с первого раза.
Собери демку для SlopHub. Результат — папка, которую я заархивирую в zip.
В корне обязателен slop.json:
{
"name": "<строчные латинские буквы, цифры, дефис; 2-40 символов>",
"title": "<название по-русски>",
"description": "<одна-две строки>",
"kind": "static | node | python | bun",
"build": true,
"build_output": "dist"
}
Правила:
- static — только клиентский код, в dist после сборки обязан быть index.html.
- node/python/bun — сервер обязан слушать 0.0.0.0 и порт из переменной PORT
(значение по умолчанию 3000).
- Для бэкендов с зависимостями ставь "build_output": "." — иначе в демку не
уедут node_modules.
- Интернета у демки нет: никаких обращений к внешним API, шрифтам с CDN,
скриптам с cdnjs. Всё, что нужно, — внутри архива или ставится сборкой.
- Записывать на диск нельзя: файлы только на чтение, состояние держи в памяти
или в localStorage браузера.
- Базы данных нет.
- node_modules в архив не клади — их поставит сборка.
- Язык интерфейса русский.
Проверьте за моделью три вещи: имя латиницей, 0.0.0.0 вместо localhost и
отсутствие обращений в интернет. Остальное поймает заливка.