Compare commits

..
2 Commits
Author SHA1 Message Date
pipistrelloandClaude Opus 4.8 d222121e6d Sync traefik config from docker host (host is source of truth)
Repo traefik files had drifted behind the live config on serverweb. Pull the
authoritative versions: file-only provider (drop docker provider), Portainer
self-signed serversTransport, and correct '||' HostSNI syntax for exchange.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 14:59:33 +03:00
pipistrelloandClaude Opus 4.8 1d6be15448 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>
2026-06-25 14:59:33 +03:00
11 changed files with 319 additions and 6 deletions
+3
View File
@@ -0,0 +1,3 @@
# Build output — generated by `zensical build`, not tracked.
/public/
.DS_Store
+69
View File
@@ -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: `![alt](assets/file.png)`
(size with `![alt](assets/file.png){ 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
```
+37
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
# Положите сюда картинки для инструкций (png/jpg/svg).
# Ссылка из Markdown: ![alt](assets/имя-файла.png)
+18
View File
@@ -0,0 +1,18 @@
---
icon: lucide/book-open
---
# Инструкции
Добро пожаловать в базу инструкций. Выберите нужный раздел в меню слева
или воспользуйтесь поиском вверху страницы.
## Разделы
- [Пример инструкции](primer-instrukcii.md) — шаблон страницы с картинками и подсказками.
!!! tip "Как добавить новую инструкцию"
Создайте Markdown-файл в папке `docs/`, добавьте его в `nav` в файле
`zensical.toml`, положите картинки в `docs/assets/` — и пересоберите сайт.
Подробнее — в `README.md` этого репозитория.
+55
View File
@@ -0,0 +1,55 @@
---
icon: lucide/file-text
---
# Пример инструкции
Эта страница — шаблон. Скопируйте её, переименуйте и наполните своим текстом.
## Шаги
1. Откройте нужное приложение.
2. Выполните действие.
3. Проверьте результат.
## Картинки
Положите изображение в `docs/assets/` и вставьте его так:
```markdown
![Описание картинки](assets/primer.png)
```
Картинку можно масштабировать (включено расширение `attr_list`):
```markdown
![Описание](assets/primer.png){ width="500" }
```
<!-- Когда добавите файл docs/assets/primer.png, раскомментируйте строку ниже:
![Пример скриншота](assets/primer.png){ width="600" }
-->
## Подсказки и предупреждения
!!! note "Заметка"
Используйте блоки-заметки для важной информации.
!!! warning "Внимание"
Так выделяются предупреждения.
??? info "Нажмите, чтобы развернуть"
Скрытый блок — удобно для длинных пояснений или частых вопросов.
## Сочетания клавиш
Скопировать: ++ctrl+c++ &nbsp;&nbsp; Вставить: ++ctrl+v++
## Чек-лист
- [x] Открыть приложение
- [ ] Выполнить настройку
- [ ] Проверить результат
+31
View File
@@ -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;
}
+85
View File
@@ -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 &copy; 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]
+1 -1
View File
@@ -24,7 +24,7 @@ tcp:
exchange:
entryPoints: ["websecure"]
rule: "HostSNI(`mail.osk.team`, `autodiscover.osk.team`)"
rule: "HostSNI(`mail.osk.team`) || HostSNI(`autodiscover.osk.team`)"
tls:
passthrough: true
service: servermail-ex
+15
View File
@@ -7,6 +7,10 @@
# default cert instead (see traefik.yml).
http:
serversTransports:
insecure: # skip cert verification to self-signed backends (Portainer 9443)
insecureSkipVerify: true
routers:
flame:
entryPoints: ["websecure"]
@@ -15,6 +19,13 @@ http:
tls:
certResolver: le
help: # client 05 instructions site (public, open)
entryPoints: ["websecure"]
rule: "Host(`help.osk.team`)"
service: help-osk
tls:
certResolver: le
portainer:
entryPoints: ["websecure"]
rule: "Host(`portainer.osk.team`)"
@@ -35,6 +46,10 @@ http:
loadBalancer:
servers:
- url: "http://flame:5005"
help-osk:
loadBalancer:
servers:
- url: "http://help-osk:80" # nginx container on reverseproxy-nw
portainer:
# Portainer must share a network with Traefik. Either add the portainer
# container to reverseproxy-nw, or point this at the host IP instead:
+3 -5
View File
@@ -27,11 +27,9 @@ entryPoints:
# everything else falls through to the HTTP routers.
providers:
# Docker labels (for local containers that opt in with traefik.enable=true)
docker:
exposedByDefault: false
network: reverseproxy-nw
# File provider = all the static routing (SNI passthrough + dashboards)
# File provider = ALL routing (SNI passthrough + dashboards). No docker
# provider — every service is defined in dynamic/*.yml, so Traefik needs no
# access to the docker socket (avoids the SELinux socket-permission denial).
file:
directory: /etc/traefik/dynamic
watch: true