Add help-osk-docs stack: Zensical instructions site for 05-osk
Static instructions site for client 05 served at help.osk.team. Markdown + images built by Zensical (zensical build) into public/, served by nginx behind Traefik. Content is bind-mounted from /mnt/containers/help-osk-docs (absolute host path — Portainer GitOps can't resolve relative mounts). Adds the help/help-osk router+service to traefik/dynamic/web.yml (TLS via Let's Encrypt, public/open). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
# Build output — generated by `zensical build`, not tracked.
|
||||||
|
/public/
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# help-osk-docs — instructions site for client 05
|
||||||
|
|
||||||
|
Public static site at **https://help.osk.team** with user instructions, written
|
||||||
|
in Markdown and built with [Zensical](https://github.com/zensical/zensical)
|
||||||
|
(static-site generator from the Material for MkDocs team). Served by `nginx`
|
||||||
|
behind the existing Traefik reverse proxy.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
- `docs/` — Markdown pages (+ `docs/assets/` for images). **This is the content.**
|
||||||
|
- `zensical.toml` — site config (name, nav, theme, Markdown extensions).
|
||||||
|
- `nginx.conf` — static web-server config.
|
||||||
|
- `docker-compose.yaml` — two services:
|
||||||
|
- `help-osk-build` — Zensical image, one-shot: runs `zensical build --clean`,
|
||||||
|
rendering `docs/` → `public/`, then exits. (The Zensical image is a
|
||||||
|
builder, **not** a host server — by design.)
|
||||||
|
- `help-osk` — `nginx:alpine`, serves `public/`, joins `reverseproxy-nw`.
|
||||||
|
Starts only after the build succeeds.
|
||||||
|
- Traefik terminates TLS for `help.osk.team` (Let's Encrypt) and proxies to the
|
||||||
|
`help-osk` container. Router/service live in
|
||||||
|
`../traefik/dynamic/web.yml` (`help` / `help-osk`).
|
||||||
|
|
||||||
|
## Bind-mount / host-sync (important)
|
||||||
|
|
||||||
|
Content is **bind-mounted**, and Portainer GitOps can't bind-mount the
|
||||||
|
repo checkout (relative paths resolve inside Portainer's container and become
|
||||||
|
empty dirs). So the project must exist on the host at an absolute path —
|
||||||
|
exactly like the Traefik config. This git repo is the **source of truth**.
|
||||||
|
|
||||||
|
Sync the project to the host (excluding the generated `public/`):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rsync -a --delete --exclude 'public/' \
|
||||||
|
help-osk-docs/ <docker-host>:/mnt/containers/help-osk-docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
The `help-osk-build` container creates `/mnt/containers/help-osk-docs/public`
|
||||||
|
on the host during the build.
|
||||||
|
|
||||||
|
## Deploy (Portainer)
|
||||||
|
|
||||||
|
1. DNS: `help.osk.team` → public edge IP, reachable on :80 (Let's Encrypt
|
||||||
|
http-01). *(Done.)*
|
||||||
|
2. Sync this folder to `/mnt/containers/help-osk-docs/` on the docker host
|
||||||
|
(see above).
|
||||||
|
3. Make sure the Traefik `help` router/service in `../traefik/dynamic/web.yml`
|
||||||
|
is also synced to the host dynamic dir
|
||||||
|
(`/mnt/containers/traefik/container-data/dynamic/web.yml`). Traefik's file
|
||||||
|
provider (`watch: true`) picks it up live — no Traefik restart needed.
|
||||||
|
4. In Portainer, add a stack pointing at `help-osk-docs/docker-compose.yaml`
|
||||||
|
(or redeploy the existing stack). The builder runs, then nginx comes up.
|
||||||
|
|
||||||
|
## Add or edit an instruction
|
||||||
|
|
||||||
|
1. Add/edit a Markdown file in `docs/` (e.g. `docs/nastrojka-vpn.md`).
|
||||||
|
2. Put images in `docs/assets/`, reference them: ``
|
||||||
|
(size with `{ width="600" }`).
|
||||||
|
3. Add the page to `nav` in `zensical.toml`.
|
||||||
|
4. Commit, re-sync to the host (step 2 above), redeploy the stack in Portainer
|
||||||
|
(re-runs the build; nginx serves the updated `public/`).
|
||||||
|
|
||||||
|
## Local preview (optional)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Build and inspect output
|
||||||
|
docker run --rm -v "$PWD":/docs:Z zensical/zensical build --clean
|
||||||
|
# Live preview on http://localhost:8000
|
||||||
|
docker run --rm -p 8000:8000 -v "$PWD":/docs zensical/zensical
|
||||||
|
```
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
services:
|
||||||
|
# One-shot builder. Zensical's official image is for builds/previews only
|
||||||
|
# (NOT a host server), so we use it to render docs/ -> public/, then exit.
|
||||||
|
help-osk-build:
|
||||||
|
image: zensical/zensical:latest
|
||||||
|
container_name: help-osk-build
|
||||||
|
working_dir: /docs
|
||||||
|
command: ["build", "--clean"]
|
||||||
|
restart: "no"
|
||||||
|
volumes:
|
||||||
|
# Whole project (zensical.toml + docs/ + writes public/) on the host.
|
||||||
|
#
|
||||||
|
# NOTE: absolute host path (NOT ./relative). Portainer GitOps runs compose
|
||||||
|
# with paths relative to its own container (/data/compose/<id>/...), which
|
||||||
|
# the Docker daemon can't resolve — relative mounts silently become empty
|
||||||
|
# dirs. Keep /mnt/containers/help-osk-docs in sync with this repo (source
|
||||||
|
# of truth), same as the traefik config.
|
||||||
|
- /mnt/containers/help-osk-docs:/docs:z
|
||||||
|
|
||||||
|
# Static web server. Serves the generated public/ and joins the Traefik
|
||||||
|
# reverse-proxy network; Traefik terminates TLS for help.osk.team.
|
||||||
|
help-osk:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: help-osk
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
help-osk-build:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
volumes:
|
||||||
|
- /mnt/containers/help-osk-docs/public:/usr/share/nginx/html:ro,z
|
||||||
|
- /mnt/containers/help-osk-docs/nginx.conf:/etc/nginx/conf.d/default.conf:ro,z
|
||||||
|
networks:
|
||||||
|
- reverseproxy-nw
|
||||||
|
|
||||||
|
networks:
|
||||||
|
reverseproxy-nw:
|
||||||
|
external: true
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Положите сюда картинки для инструкций (png/jpg/svg).
|
||||||
|
# Ссылка из Markdown: 
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
icon: lucide/book-open
|
||||||
|
---
|
||||||
|
|
||||||
|
# Инструкции
|
||||||
|
|
||||||
|
Добро пожаловать в базу инструкций. Выберите нужный раздел в меню слева
|
||||||
|
или воспользуйтесь поиском вверху страницы.
|
||||||
|
|
||||||
|
## Разделы
|
||||||
|
|
||||||
|
- [Пример инструкции](primer-instrukcii.md) — шаблон страницы с картинками и подсказками.
|
||||||
|
|
||||||
|
!!! tip "Как добавить новую инструкцию"
|
||||||
|
|
||||||
|
Создайте Markdown-файл в папке `docs/`, добавьте его в `nav` в файле
|
||||||
|
`zensical.toml`, положите картинки в `docs/assets/` — и пересоберите сайт.
|
||||||
|
Подробнее — в `README.md` этого репозитория.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
icon: lucide/file-text
|
||||||
|
---
|
||||||
|
|
||||||
|
# Пример инструкции
|
||||||
|
|
||||||
|
Эта страница — шаблон. Скопируйте её, переименуйте и наполните своим текстом.
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
1. Откройте нужное приложение.
|
||||||
|
2. Выполните действие.
|
||||||
|
3. Проверьте результат.
|
||||||
|
|
||||||
|
## Картинки
|
||||||
|
|
||||||
|
Положите изображение в `docs/assets/` и вставьте его так:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|

|
||||||
|
```
|
||||||
|
|
||||||
|
Картинку можно масштабировать (включено расширение `attr_list`):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
{ width="500" }
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- Когда добавите файл docs/assets/primer.png, раскомментируйте строку ниже:
|
||||||
|
{ width="600" }
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Подсказки и предупреждения
|
||||||
|
|
||||||
|
!!! note "Заметка"
|
||||||
|
|
||||||
|
Используйте блоки-заметки для важной информации.
|
||||||
|
|
||||||
|
!!! warning "Внимание"
|
||||||
|
|
||||||
|
Так выделяются предупреждения.
|
||||||
|
|
||||||
|
??? info "Нажмите, чтобы развернуть"
|
||||||
|
|
||||||
|
Скрытый блок — удобно для длинных пояснений или частых вопросов.
|
||||||
|
|
||||||
|
## Сочетания клавиш
|
||||||
|
|
||||||
|
Скопировать: ++ctrl+c++ Вставить: ++ctrl+v++
|
||||||
|
|
||||||
|
## Чек-лист
|
||||||
|
|
||||||
|
- [x] Открыть приложение
|
||||||
|
- [ ] Выполнить настройку
|
||||||
|
- [ ] Проверить результат
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Serves the static site built by Zensical (./public) for client 05.
|
||||||
|
# Mounted at /etc/nginx/conf.d/default.conf inside nginx:alpine.
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Zensical uses directory-style URLs (/page/ -> /page/index.html).
|
||||||
|
# Unknown paths fall through to the generated 404 page.
|
||||||
|
error_page 404 /404.html;
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Long-cache fingerprinted theme assets; keep HTML uncached so content
|
||||||
|
# updates show up immediately after a rebuild.
|
||||||
|
location /assets/ {
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
}
|
||||||
|
location = /404.html {
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/css application/javascript application/json image/svg+xml;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Zensical configuration — client 05 (05-osk) instructions site.
|
||||||
|
# Docs: https://zensical.org/docs/setup/basics/
|
||||||
|
# Build: `zensical build --clean` -> outputs static site to ./public
|
||||||
|
|
||||||
|
[project]
|
||||||
|
site_name = "ОСК — Инструкции"
|
||||||
|
site_description = "Инструкции для пользователей (клиент 05)."
|
||||||
|
site_author = "ОСК"
|
||||||
|
site_url = "https://help.osk.team/"
|
||||||
|
copyright = """
|
||||||
|
Copyright © 2026 ОСК
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Explicit navigation. Add a line per Markdown page you create under docs/.
|
||||||
|
# Remove this block to let Zensical derive the nav from the folder structure.
|
||||||
|
nav = [
|
||||||
|
{ "Главная" = "index.md" },
|
||||||
|
{ "Пример инструкции" = "primer-instrukcii.md" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.theme]
|
||||||
|
language = "ru"
|
||||||
|
features = [
|
||||||
|
"announce.dismiss",
|
||||||
|
"content.code.annotate",
|
||||||
|
"content.code.copy",
|
||||||
|
"content.tooltips",
|
||||||
|
"navigation.footer",
|
||||||
|
"navigation.indexes",
|
||||||
|
"navigation.instant",
|
||||||
|
"navigation.instant.prefetch",
|
||||||
|
"navigation.path",
|
||||||
|
"navigation.sections",
|
||||||
|
"navigation.top",
|
||||||
|
"navigation.tracking",
|
||||||
|
"search.highlight",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Optional: drop a logo at docs/assets/logo.png and uncomment.
|
||||||
|
#logo = "assets/logo.png"
|
||||||
|
|
||||||
|
[[project.theme.palette]]
|
||||||
|
scheme = "default"
|
||||||
|
toggle.icon = "lucide/sun"
|
||||||
|
toggle.name = "Тёмная тема"
|
||||||
|
|
||||||
|
[[project.theme.palette]]
|
||||||
|
scheme = "slate"
|
||||||
|
toggle.icon = "lucide/moon"
|
||||||
|
toggle.name = "Светлая тема"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Markdown extensions — enables admonitions, image attributes (sizing/align),
|
||||||
|
# task lists, code highlighting, tabs, etc. Kept from Zensical's defaults.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
[project.markdown_extensions.abbr]
|
||||||
|
[project.markdown_extensions.admonition]
|
||||||
|
[project.markdown_extensions.attr_list]
|
||||||
|
[project.markdown_extensions.def_list]
|
||||||
|
[project.markdown_extensions.footnotes]
|
||||||
|
[project.markdown_extensions.md_in_html]
|
||||||
|
[project.markdown_extensions.toc]
|
||||||
|
permalink = true
|
||||||
|
[project.markdown_extensions.pymdownx.betterem]
|
||||||
|
[project.markdown_extensions.pymdownx.caret]
|
||||||
|
[project.markdown_extensions.pymdownx.details]
|
||||||
|
[project.markdown_extensions.pymdownx.emoji]
|
||||||
|
emoji_generator = "zensical.extensions.emoji.to_svg"
|
||||||
|
emoji_index = "zensical.extensions.emoji.twemoji"
|
||||||
|
[project.markdown_extensions.pymdownx.highlight]
|
||||||
|
anchor_linenums = true
|
||||||
|
line_spans = "__span"
|
||||||
|
pygments_lang_class = true
|
||||||
|
[project.markdown_extensions.pymdownx.inlinehilite]
|
||||||
|
[project.markdown_extensions.pymdownx.keys]
|
||||||
|
[project.markdown_extensions.pymdownx.magiclink]
|
||||||
|
[project.markdown_extensions.pymdownx.mark]
|
||||||
|
[project.markdown_extensions.pymdownx.smartsymbols]
|
||||||
|
[project.markdown_extensions.pymdownx.superfences]
|
||||||
|
[project.markdown_extensions.pymdownx.tabbed]
|
||||||
|
alternate_style = true
|
||||||
|
combine_header_slug = true
|
||||||
|
[project.markdown_extensions.pymdownx.tasklist]
|
||||||
|
custom_checkbox = true
|
||||||
|
[project.markdown_extensions.pymdownx.tilde]
|
||||||
@@ -15,6 +15,13 @@ http:
|
|||||||
tls:
|
tls:
|
||||||
certResolver: le
|
certResolver: le
|
||||||
|
|
||||||
|
help: # client 05 instructions site (public, open)
|
||||||
|
entryPoints: ["websecure"]
|
||||||
|
rule: "Host(`help.osk.team`)"
|
||||||
|
service: help-osk
|
||||||
|
tls:
|
||||||
|
certResolver: le
|
||||||
|
|
||||||
portainer:
|
portainer:
|
||||||
entryPoints: ["websecure"]
|
entryPoints: ["websecure"]
|
||||||
rule: "Host(`portainer.osk.team`)"
|
rule: "Host(`portainer.osk.team`)"
|
||||||
@@ -35,6 +42,10 @@ http:
|
|||||||
loadBalancer:
|
loadBalancer:
|
||||||
servers:
|
servers:
|
||||||
- url: "http://flame:5005"
|
- url: "http://flame:5005"
|
||||||
|
help-osk:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://help-osk:80" # nginx container on reverseproxy-nw
|
||||||
portainer:
|
portainer:
|
||||||
# Portainer must share a network with Traefik. Either add the portainer
|
# Portainer must share a network with Traefik. Either add the portainer
|
||||||
# container to reverseproxy-nw, or point this at the host IP instead:
|
# container to reverseproxy-nw, or point this at the host IP instead:
|
||||||
|
|||||||
Reference in New Issue
Block a user