demo article: diagrams moved here from the code repo (uploads/diagrams), deploy docs pointer updated

This commit is contained in:
2026-09-06 15:32:39 +02:00
parent 4b3dd80ae8
commit f43212df7f
9 changed files with 433 additions and 10 deletions
+6 -6
View File
@@ -4,7 +4,7 @@ description: "Architektura rozwiązania, przydatne linki i porównanie klas rozw
date: 2026-07-23 date: 2026-07-23
category: demo category: demo
author: Cyfronet author: Cyfronet
cover: /diagrams/cover-git-cms.svg cover: /uploads/diagrams/cover-git-cms.svg
lang: pl lang: pl
draft: false draft: false
--- ---
@@ -19,7 +19,7 @@ tego uczciwie niżej. Ten artykuł został napisany i opublikowany dokładnie t
## Od zapisu do publikacji ## Od zapisu do publikacji
![Pięć kroków publikacji: zapis w edytorze → commit w repo → detekcja zmiany → build strony → publikacja](/diagrams/od-zapisu-do-publikacji.svg) ![Pięć kroków publikacji: zapis w edytorze → commit w repo → detekcja zmiany → build strony → publikacja](/uploads/diagrams/od-zapisu-do-publikacji.svg)
Publikacja jest automatyczna — zapis w edytorze po chwili sam pojawia się Publikacja jest automatyczna — zapis w edytorze po chwili sam pojawia się
na stronie, bez żadnego ręcznego deployu. Każda zmiana to commit, więc na stronie, bez żadnego ręcznego deployu. Każda zmiana to commit, więc
@@ -27,14 +27,14 @@ na stronie, bez żadnego ręcznego deployu. Każda zmiana to commit, więc
## Jak to wygląda na maszynie ## Jak to wygląda na maszynie
![Architektura na maszynie demo: redaktor zapisuje przez Sveltię do Gitei, timer systemd uruchamia jednorazowy kontener builda Astro, wynik trafia do wolumenu releases, a nginx serwuje czytelnikom wyłącznie statyczne pliki](/diagrams/architektura-maszyna.svg) ![Architektura na maszynie demo: redaktor zapisuje przez Sveltię do Gitei, timer systemd uruchamia jednorazowy kontener builda Astro, wynik trafia do wolumenu releases, a nginx serwuje czytelnikom wyłącznie statyczne pliki](/uploads/diagrams/architektura-maszyna.svg)
Dla porównania — ta sama maszyna z klasycznym CMS-em. Układ diagramu jest Dla porównania — ta sama maszyna z klasycznym CMS-em. Układ diagramu jest
celowo identyczny, żeby różnice było widać na pierwszy rzut oka: tu każdy celowo identyczny, żeby różnice było widać na pierwszy rzut oka: tu każdy
element działa bez przerwy, a panel logowania i aplikacja są wystawione element działa bez przerwy, a panel logowania i aplikacja są wystawione
do internetu. do internetu.
![Architektura klasycznego CMS z bazą danych: aplikacja z publicznym panelem admina i renderowaniem na żądanie działa non stop, obok baza danych wymagająca backupów i wolumen mediów](/diagrams/architektura-cms-z-baza.svg) ![Architektura klasycznego CMS z bazą danych: aplikacja z publicznym panelem admina i renderowaniem na żądanie działa non stop, obok baza danych wymagająca backupów i wolumen mediów](/uploads/diagrams/architektura-cms-z-baza.svg)
## Gdzie co jest ## Gdzie co jest
@@ -44,14 +44,14 @@ do internetu.
| Edytor treści | `/admin/` (link „Content editor" w stopce) | | Edytor treści | `/admin/` (link „Content editor" w stopce) |
| Gitea (repo `ctao/portal`) | port 3000 na maszynie demo | | Gitea (repo `ctao/portal`) | port 3000 na maszynie demo |
| Konto demo | użytkownik `ctao` (hasło u prowadzącego demo) | | Konto demo | użytkownik `ctao` (hasło u prowadzącego demo) |
| Dokumentacja wdrożenia | katalog `deploy/` w repo: `README.md` (runbook) i `DEPLOY-LOG.md` (pełna historia decyzji) | | Dokumentacja wdrożenia | katalog `deploy/` w repo kodu: `README.md` (runbook) |
Całość działa w podmanie na jednej maszynie: kontener Gitei, kontener nginx Całość działa w podmanie na jednej maszynie: kontener Gitei, kontener nginx
i jednorazowy kontener builda odpalany przez systemd timer. i jednorazowy kontener builda odpalany przez systemd timer.
## Dlaczego strona statyczna ## Dlaczego strona statyczna
![Porównanie powierzchni ataku: CMS z bazą wystawia publicznie aplikację, panel logowania, bazę i pluginy — tu publiczne są tylko pliki HTML, a edycja idzie przez git dla zalogowanych](/diagrams/powierzchnia-ataku.svg) ![Porównanie powierzchni ataku: CMS z bazą wystawia publicznie aplikację, panel logowania, bazę i pluginy — tu publiczne są tylko pliki HTML, a edycja idzie przez git dla zalogowanych](/uploads/diagrams/powierzchnia-ataku.svg)
- **Bezpieczeństwo**: publicznie wystawione są tylko pliki. Nie istnieje - **Bezpieczeństwo**: publicznie wystawione są tylko pliki. Nie istnieje
panel admina dostępny z internetu, baza do wykradzenia ani runtime z CVE. panel admina dostępny z internetu, baza do wykradzenia ani runtime z CVE.
+2 -2
View File
@@ -3,10 +3,10 @@ title: Contact
description: How to reach the CTAO and the Science Portal team. description: How to reach the CTAO and the Science Portal team.
--- ---
*Demo page — final wording will be provided by the CTAO Central Organisation.* *Demo page. Final wording will be provided by the CTAO Central Organisation.*
- **General enquiries:** see the contact section on [ctao.org](https://www.ctao.org/) - **General enquiries:** see the contact section on [ctao.org](https://www.ctao.org/)
- **Science user support:** help desk, FAQ and user forum — via [User Support](/support) (integration planned) - **Science user support:** help desk, FAQ and user forum, via [User Support](/support) (integration planned)
- **Portal (technical):** SUSS Science Portal team - **Portal (technical):** SUSS Science Portal team
For media and outreach enquiries, please use the channels listed on the CTAO website. For media and outreach enquiries, please use the channels listed on the CTAO website.
+1 -1
View File
@@ -3,7 +3,7 @@ title: Disclaimer
description: Terms of use of the CTAO Science Portal. description: Terms of use of the CTAO Science Portal.
--- ---
*Demo page — the final terms of use will be provided by the CTAO Central *Demo page. The final terms of use will be provided by the CTAO Central
Organisation.* Organisation.*
## Content credit ## Content credit
+1 -1
View File
@@ -3,7 +3,7 @@ title: Privacy statement
description: How the CTAO Science Portal handles personal data. description: How the CTAO Science Portal handles personal data.
--- ---
*Demo page — the final privacy statement will be provided by the CTAO Central *Demo page. The final privacy statement will be provided by the CTAO Central
Organisation.* Organisation.*
Sign-in and account information are handled by the central **CTAO AAI** Sign-in and account information are handled by the central **CTAO AAI**
@@ -0,0 +1,110 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 640" role="img" aria-label="Architektura klasycznego CMS z bazą danych: aplikacja CMS z publicznym panelem admina działa non stop, obok baza danych i wolumen mediów; czytelnik dostaje strony renderowane na żądanie">
<defs>
<marker id="arrB" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 L10 5 L0 10 z" fill="#00004a"/>
</marker>
<filter id="softB" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="2" stdDeviation="5" flood-color="#00004a" flood-opacity="0.10"/>
</filter>
<style>
.t { font-family: Inter, "Segoe UI", Arial, sans-serif; }
.box { fill: #ffffff; stroke: #00004a; stroke-width: 1.5; }
.sub { fill: #f5f5f5; stroke: #e2e5ee; stroke-width: 1; }
.ttl { font-weight: 700; font-size: 15.5px; fill: #00004a; letter-spacing: .04em; }
.cap { font-size: 12.5px; fill: #46536a; }
.lbl { font-size: 12px; fill: #46536a; paint-order: stroke; stroke: #ffffff; stroke-width: 4; }
.chipbg { fill: #e9ebf3; }
.chip { font-size: 11.5px; font-weight: 700; fill: #00004a; letter-spacing: .06em; }
.flow { stroke: #00004a; stroke-width: 1.8; fill: none; marker-end: url(#arrB); }
.pill { fill: #f5f5f5; stroke: #e2e5ee; }
.pilltxt { font-size: 12.5px; fill: #101228; }
</style>
</defs>
<!-- machine boundary (mirrors the static-demo diagram 1:1) -->
<rect x="340" y="40" width="830" height="570" rx="18" fill="#f8f9fc" stroke="#e2e5ee" stroke-width="1.5"/>
<text class="t" x="366" y="76" font-size="13" font-weight="700" fill="#00004a" letter-spacing=".18em">MASZYNA · CMS Z BAZĄ DANYCH</text>
<text class="t cap" x="366" y="96">np. WordPress, Strapi, Drupal — wspólny schemat tej klasy rozwiązań</text>
<!-- actor: redaktor -->
<g filter="url(#softB)">
<rect class="box" x="52" y="120" width="220" height="120" rx="16"/>
</g>
<circle cx="88" cy="156" r="11" fill="none" stroke="#00004a" stroke-width="1.8"/>
<path d="M72 186 q16 -18 32 0" fill="none" stroke="#00004a" stroke-width="1.8"/>
<text class="t ttl" x="116" y="162">Redaktor</text>
<text class="t cap" x="70" y="212">panel admina w przeglądarce</text>
<text class="t cap" x="70" y="228">logowanie do aplikacji</text>
<!-- actor: czytelnik -->
<g filter="url(#softB)">
<rect class="box" x="52" y="400" width="220" height="120" rx="16"/>
</g>
<circle cx="88" cy="436" r="11" fill="none" stroke="#00004a" stroke-width="1.8"/>
<path d="M72 466 q16 -18 32 0" fill="none" stroke="#00004a" stroke-width="1.8"/>
<text class="t ttl" x="116" y="442">Czytelnik</text>
<text class="t cap" x="70" y="492">publiczna strona portalu</text>
<text class="t cap" x="70" y="508">HTML składany na żądanie</text>
<!-- CMS application (tall box, always running) -->
<g filter="url(#softB)">
<rect class="box" x="400" y="120" width="320" height="400" rx="16"/>
</g>
<text class="t ttl" x="424" y="152">APLIKACJA CMS</text>
<rect class="chipbg" x="590" y="136" width="106" height="22" rx="11"/>
<text class="t chip" x="643" y="151" text-anchor="middle">NON STOP</text>
<rect class="sub" x="420" y="176" width="280" height="88" rx="12"/>
<text class="t" x="436" y="202" font-size="13.5" font-weight="700" fill="#101228">panel admina</text>
<text class="t cap" x="436" y="224">publiczny adres + logowanie</text>
<text class="t cap" x="436" y="242">z internetu</text>
<rect class="sub" x="420" y="278" width="280" height="88" rx="12"/>
<text class="t" x="436" y="304" font-size="13.5" font-weight="700" fill="#101228">renderowanie stron</text>
<text class="t cap" x="436" y="326">każde wejście czytelnika =</text>
<text class="t cap" x="436" y="344">praca aplikacji i bazy</text>
<rect class="sub" x="420" y="380" width="280" height="88" rx="12"/>
<text class="t" x="436" y="406" font-size="13.5" font-weight="700" fill="#101228">pluginy / rozszerzenia</text>
<text class="t cap" x="436" y="428">własny cykl aktualizacji</text>
<text class="t cap" x="436" y="446">i podatności</text>
<!-- database -->
<g filter="url(#softB)">
<rect class="box" x="790" y="120" width="340" height="185" rx="16"/>
</g>
<text class="t ttl" x="814" y="152">BAZA DANYCH</text>
<rect class="chipbg" x="972" y="136" width="106" height="22" rx="11"/>
<text class="t chip" x="1025" y="151" text-anchor="middle">NON STOP</text>
<text class="t cap" x="814" y="188">treść + konta użytkowników</text>
<text class="t cap" x="814" y="212">wymaga backupów i testów odtwarzania</text>
<text class="t cap" x="814" y="236">migracje schematu przy aktualizacjach</text>
<text class="t cap" x="814" y="266">historia zmian: zależnie od produktu,</text>
<text class="t cap" x="814" y="284">bywa funkcją płatną</text>
<!-- media volume -->
<g filter="url(#softB)">
<rect class="sub" x="790" y="400" width="340" height="120" rx="16"/>
</g>
<text class="t ttl" x="814" y="432">media/</text>
<rect class="chipbg" x="896" y="416" width="82" height="22" rx="11"/>
<text class="t chip" x="937" y="431" text-anchor="middle">WOLUMEN</text>
<text class="t cap" x="814" y="462">obrazy i pliki poza bazą</text>
<text class="t cap" x="814" y="480">osobny backup</text>
<!-- flows -->
<path class="flow" d="M272 180 H396"/>
<text class="t lbl" x="334" y="170" text-anchor="middle">logowanie do panelu</text>
<path class="flow" d="M400 460 H276"/>
<text class="t lbl" x="338" y="450" text-anchor="middle">HTML na żądanie</text>
<path class="flow" d="M720 212 H786"/>
<text class="t lbl" x="753" y="202" text-anchor="middle">SQL</text>
<path class="flow" d="M720 440 H786"/>
<text class="t lbl" x="753" y="430" text-anchor="middle">upload</text>
<!-- summary pills -->
<rect class="pill" x="380" y="552" width="360" height="36" rx="18"/>
<text class="t pilltxt" x="560" y="575" text-anchor="middle">2 procesy działają bez przerwy, także nocą</text>
<rect class="pill" x="770" y="552" width="360" height="36" rx="18"/>
<text class="t pilltxt" x="950" y="575" text-anchor="middle">aktualizacje bezpieczeństwa = stały obowiązek</text>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

+111
View File
@@ -0,0 +1,111 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 640" role="img" aria-label="Architektura demo na maszynie: redaktor i czytelnik łączą się z kontenerami podmana — Gitea, build Astro, wolumen releases i nginx">
<defs>
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 L10 5 L0 10 z" fill="#00004a"/>
</marker>
<filter id="soft" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="2" stdDeviation="5" flood-color="#00004a" flood-opacity="0.10"/>
</filter>
<style>
.t { font-family: Inter, "Segoe UI", Arial, sans-serif; }
.box { fill: #ffffff; stroke: #00004a; stroke-width: 1.5; }
.sub { fill: #f5f5f5; stroke: #e2e5ee; stroke-width: 1; }
.ttl { font-weight: 700; font-size: 15.5px; fill: #00004a; letter-spacing: .04em; }
.cap { font-size: 12.5px; fill: #46536a; }
.lbl { font-size: 12px; fill: #46536a; paint-order: stroke; stroke: #ffffff; stroke-width: 4; }
.chipbg { fill: #d2faf7; }
.chip { font-size: 11.5px; font-weight: 700; fill: #00004a; letter-spacing: .06em; }
.flow { stroke: #00004a; stroke-width: 1.8; fill: none; marker-end: url(#arr); }
.mono { font-family: "SF Mono", Consolas, Menlo, monospace; font-size: 12.5px; fill: #101228; }
</style>
</defs>
<!-- machine boundary -->
<rect x="340" y="40" width="830" height="570" rx="18" fill="#f8f9fc" stroke="#e2e5ee" stroke-width="1.5"/>
<text class="t" x="366" y="76" font-size="13" font-weight="700" fill="#00004a" letter-spacing=".18em">MASZYNA DEMO · PODMAN</text>
<text class="t cap" x="366" y="96">osobne kontenery pod systemd · wszystko w jednym katalogu użytkownika</text>
<!-- actor: redaktor -->
<g filter="url(#soft)">
<rect class="box" x="52" y="120" width="220" height="120" rx="16"/>
</g>
<circle cx="88" cy="156" r="11" fill="none" stroke="#00004a" stroke-width="1.8"/>
<path d="M72 186 q16 -18 32 0" fill="none" stroke="#00004a" stroke-width="1.8"/>
<text class="t ttl" x="116" y="162">Redaktor</text>
<text class="t cap" x="70" y="212">przeglądarka · <tspan font-weight="600">/admin/</tspan> (Sveltia)</text>
<text class="t cap" x="70" y="228">edycja WYSIWYG, podgląd 1:1</text>
<!-- actor: czytelnik -->
<g filter="url(#soft)">
<rect class="box" x="52" y="400" width="220" height="120" rx="16"/>
</g>
<circle cx="88" cy="436" r="11" fill="none" stroke="#00004a" stroke-width="1.8"/>
<path d="M72 466 q16 -18 32 0" fill="none" stroke="#00004a" stroke-width="1.8"/>
<text class="t ttl" x="116" y="442">Czytelnik</text>
<text class="t cap" x="70" y="492">publiczna strona portalu</text>
<text class="t cap" x="70" y="508">czysty, szybki HTML</text>
<!-- gitea -->
<g filter="url(#soft)">
<rect class="box" x="400" y="120" width="320" height="185" rx="16"/>
</g>
<text class="t ttl" x="424" y="152">GITEA</text>
<rect class="chipbg" x="490" y="136" width="52" height="22" rx="11"/>
<text class="t chip" x="516" y="151" text-anchor="middle">:3000</text>
<rect class="sub" x="420" y="172" width="280" height="112" rx="12"/>
<text class="t" x="436" y="198" font-size="13.5" font-weight="700" fill="#101228">repo ctao/portal</text>
<text class="t cap" x="436" y="220">treść: Markdown + obrazy (WebP)</text>
<text class="t cap" x="436" y="238">każda zmiana = commit</text>
<text class="t cap" x="436" y="256">pełna historia + rollback za darmo</text>
<!-- build (ephemeral) -->
<g filter="url(#soft)">
<rect x="790" y="120" width="340" height="185" rx="16" fill="#ffffff" stroke="#00004a" stroke-width="1.5" stroke-dasharray="7 5"/>
</g>
<text class="t ttl" x="814" y="152">BUILD · ASTRO</text>
<rect class="chipbg" x="948" y="136" width="164" height="22" rx="11"/>
<text class="t chip" x="1030" y="151" text-anchor="middle">KONTENER JEDNORAZOWY</text>
<text class="t cap" x="814" y="188">timer systemd (co ~10 s) sprawdza,</text>
<text class="t cap" x="814" y="206">czy w repo jest nowy commit</text>
<text class="t cap" x="814" y="234">jeśli tak: git fetch → checkout →</text>
<text class="t cap" x="814" y="252"><tspan font-weight="700" fill="#101228">Markdown → statyczny HTML</tspan></text>
<text class="t cap" x="814" y="280">po buildzie kontener znika</text>
<!-- releases volume -->
<g filter="url(#soft)">
<rect class="sub" x="790" y="400" width="340" height="150" rx="16"/>
</g>
<text class="t ttl" x="814" y="432">releases/</text>
<rect class="chipbg" x="908" y="416" width="82" height="22" rx="11"/>
<text class="t chip" x="949" y="431" text-anchor="middle">WOLUMEN</text>
<text class="t mono" x="814" y="466">9f2c1d/ poprzednia wersja</text>
<text class="t mono" x="814" y="488">71b980/ <tspan font-weight="700" fill="#00004a">← current (symlink)</tspan></text>
<text class="t cap" x="814" y="518">podmiana atomowa — zero przestoju</text>
<!-- nginx -->
<g filter="url(#soft)">
<rect class="box" x="400" y="400" width="320" height="150" rx="16"/>
</g>
<text class="t ttl" x="424" y="432">NGINX</text>
<rect class="chipbg" x="500" y="416" width="52" height="22" rx="11"/>
<text class="t chip" x="526" y="431" text-anchor="middle">:8080</text>
<text class="t cap" x="424" y="466">serwuje <tspan font-weight="600">releases/current</tspan></text>
<text class="t cap" x="424" y="484">wyłącznie statyczne pliki</text>
<text class="t cap" x="424" y="502">brak aplikacji · brak bazy · brak PHP</text>
<!-- flows -->
<path class="flow" d="M272 180 H396"/>
<text class="t lbl" x="334" y="170" text-anchor="middle">zapis przez API (OAuth)</text>
<path class="flow" d="M790 212 H724"/>
<text class="t lbl" x="757" y="202" text-anchor="middle">git fetch</text>
<path class="flow" d="M960 305 V396"/>
<text class="t lbl" x="968" y="354" text-anchor="start">nowa wersja strony</text>
<path class="flow" d="M790 475 H724"/>
<text class="t lbl" x="757" y="465" text-anchor="middle">current/</text>
<path class="flow" d="M400 475 H276"/>
<text class="t lbl" x="338" y="465" text-anchor="middle">statyczny HTML</text>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

+78
View File
@@ -0,0 +1,78 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-label="Grafika: graf commitów w gicie przechodzący w statyczną stronę WWW">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#00004a"/>
<stop offset=".55" stop-color="#000063"/>
<stop offset="1" stop-color="#00009c"/>
</linearGradient>
<radialGradient id="glow" cx=".5" cy=".5" r=".5">
<stop offset="0" stop-color="#00e4d8" stop-opacity=".55"/>
<stop offset="1" stop-color="#00e4d8" stop-opacity="0"/>
</radialGradient>
<linearGradient id="page" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="1" stop-color="#eef1f8"/>
</linearGradient>
<filter id="blur18"><feGaussianBlur stdDeviation="18"/></filter>
</defs>
<rect width="1600" height="900" fill="url(#bg)"/>
<!-- starfield -->
<g fill="#ffffff">
<circle cx="140" cy="130" r="2" opacity=".55"/><circle cx="360" cy="80" r="1.4" opacity=".35"/>
<circle cx="620" cy="150" r="1.8" opacity=".45"/><circle cx="905" cy="70" r="1.4" opacity=".4"/>
<circle cx="1210" cy="120" r="2" opacity=".5"/><circle cx="1430" cy="200" r="1.5" opacity=".35"/>
<circle cx="90" cy="420" r="1.5" opacity=".35"/><circle cx="240" cy="700" r="2" opacity=".45"/>
<circle cx="520" cy="810" r="1.4" opacity=".3"/><circle cx="1050" cy="840" r="1.8" opacity=".4"/>
<circle cx="1330" cy="760" r="1.4" opacity=".35"/><circle cx="1500" cy="560" r="2" opacity=".5"/>
<circle cx="760" cy="330" r="1.3" opacity=".3"/><circle cx="1130" cy="300" r="1.5" opacity=".35"/>
</g>
<!-- cherenkov glow behind the merge point -->
<circle cx="560" cy="450" r="260" fill="url(#glow)" filter="url(#blur18)"/>
<!-- git graph: two branches merging -->
<g fill="none" stroke-linecap="round">
<path d="M220 260 H420 Q500 260 540 350 L560 450" stroke="#3d3dc4" stroke-width="10"/>
<path d="M220 640 H420 Q500 640 540 550 L560 450" stroke="#3d3dc4" stroke-width="10"/>
<path d="M560 450 H840" stroke="#00e4d8" stroke-width="12"/>
</g>
<!-- commit nodes -->
<g stroke="#00004a" stroke-width="6">
<circle cx="260" cy="260" r="22" fill="#ffffff"/>
<circle cx="400" cy="260" r="22" fill="#ffffff"/>
<circle cx="260" cy="640" r="22" fill="#ffffff"/>
<circle cx="400" cy="640" r="22" fill="#ffffff"/>
<circle cx="560" cy="450" r="30" fill="#00e4d8"/>
</g>
<!-- flowing into a static page -->
<g>
<!-- back pages -->
<rect x="920" y="212" width="420" height="520" rx="24" fill="#c7cde9" opacity=".45" transform="rotate(-4 1130 470)"/>
<rect x="905" y="200" width="420" height="520" rx="24" fill="#c7cde9" opacity=".7" transform="rotate(-2 1115 460)"/>
<!-- front page -->
<rect x="890" y="190" width="430" height="530" rx="24" fill="url(#page)"/>
<!-- browser dots -->
<circle cx="930" cy="232" r="8" fill="#e2e5ee"/><circle cx="958" cy="232" r="8" fill="#e2e5ee"/><circle cx="986" cy="232" r="8" fill="#e2e5ee"/>
<!-- headline + text lines -->
<rect x="930" y="286" width="270" height="26" rx="13" fill="#00004a"/>
<rect x="930" y="336" width="350" height="14" rx="7" fill="#9aa3bf"/>
<rect x="930" y="366" width="330" height="14" rx="7" fill="#9aa3bf"/>
<rect x="930" y="396" width="300" height="14" rx="7" fill="#9aa3bf"/>
<!-- image placeholder with cherenkov accent -->
<rect x="930" y="440" width="350" height="160" rx="16" fill="#e8ecf6"/>
<path d="M950 585 L1030 505 L1090 555 L1140 515 L1260 585 Z" fill="#c7cde9"/>
<circle cx="1215" cy="480" r="16" fill="#00e4d8"/>
<!-- more text -->
<rect x="930" y="630" width="340" height="14" rx="7" fill="#9aa3bf"/>
<rect x="930" y="660" width="250" height="14" rx="7" fill="#9aa3bf"/>
</g>
<!-- markdown glyph riding the flow -->
<g transform="translate(660 380)">
<rect x="0" y="0" width="120" height="84" rx="14" fill="#00004a" stroke="#00e4d8" stroke-width="3"/>
<text x="60" y="56" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, sans-serif" font-size="38" font-weight="800" fill="#ffffff">M↓</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,76 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 430" role="img" aria-label="Pięć kroków od zapisu w edytorze do publikacji: zapis, commit, detekcja, build, publikacja">
<defs>
<marker id="arr2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 L10 5 L0 10 z" fill="#00004a"/>
</marker>
<filter id="soft2" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="2" stdDeviation="5" flood-color="#00004a" flood-opacity="0.10"/>
</filter>
<style>
.t { font-family: Inter, "Segoe UI", Arial, sans-serif; }
.card { fill: #ffffff; stroke: #00004a; stroke-width: 1.5; }
.ttl { font-weight: 700; font-size: 15px; fill: #00004a; }
.cap { font-size: 12.3px; fill: #46536a; }
.num { font-weight: 800; font-size: 15px; fill: #00004a; }
.pill { fill: #f5f5f5; stroke: #e2e5ee; }
.pilltxt { font-size: 12.5px; fill: #101228; }
.flow { stroke: #00004a; stroke-width: 1.8; fill: none; marker-end: url(#arr2); }
</style>
</defs>
<!-- cards -->
<g filter="url(#soft2)"><rect class="card" x="55" y="100" width="200" height="175" rx="16"/></g>
<g filter="url(#soft2)"><rect class="card" x="285" y="100" width="200" height="175" rx="16"/></g>
<g filter="url(#soft2)"><rect class="card" x="515" y="100" width="200" height="175" rx="16"/></g>
<g filter="url(#soft2)"><rect class="card" x="745" y="100" width="200" height="175" rx="16"/></g>
<g filter="url(#soft2)"><rect class="card" x="975" y="100" width="200" height="175" rx="16"/></g>
<!-- connectors -->
<path class="flow" d="M255 187 H281"/>
<path class="flow" d="M485 187 H511"/>
<path class="flow" d="M715 187 H741"/>
<path class="flow" d="M945 187 H971"/>
<!-- step numbers -->
<circle cx="155" cy="100" r="17" fill="#00e4d8"/><text class="t num" x="155" y="106" text-anchor="middle">1</text>
<circle cx="385" cy="100" r="17" fill="#00e4d8"/><text class="t num" x="385" y="106" text-anchor="middle">2</text>
<circle cx="615" cy="100" r="17" fill="#00e4d8"/><text class="t num" x="615" y="106" text-anchor="middle">3</text>
<circle cx="845" cy="100" r="17" fill="#00e4d8"/><text class="t num" x="845" y="106" text-anchor="middle">4</text>
<circle cx="1075" cy="100" r="17" fill="#00e4d8"/><text class="t num" x="1075" y="106" text-anchor="middle">5</text>
<!-- card 1 -->
<text class="t ttl" x="155" y="152" text-anchor="middle">Zapis w edytorze</text>
<text class="t cap" x="155" y="182" text-anchor="middle">Sveltia · WYSIWYG</text>
<text class="t cap" x="155" y="200" text-anchor="middle">podgląd artykułu 1:1</text>
<text class="t cap" x="155" y="218" text-anchor="middle">obrazy → WebP przy uploadzie</text>
<!-- card 2 -->
<text class="t ttl" x="385" y="152" text-anchor="middle">Commit w repo</text>
<text class="t cap" x="385" y="182" text-anchor="middle">Gitea · logowanie OAuth</text>
<text class="t cap" x="385" y="200" text-anchor="middle">zapis przez API = commit</text>
<text class="t cap" x="385" y="218" text-anchor="middle">historia każdej zmiany</text>
<!-- card 3 -->
<text class="t ttl" x="615" y="152" text-anchor="middle">Detekcja zmiany</text>
<text class="t cap" x="615" y="182" text-anchor="middle">timer systemd</text>
<text class="t cap" x="615" y="200" text-anchor="middle">sprawdza repo co ~10 s</text>
<text class="t cap" x="615" y="218" text-anchor="middle">brak zmian = brak pracy</text>
<!-- card 4 -->
<text class="t ttl" x="845" y="152" text-anchor="middle">Build strony</text>
<text class="t cap" x="845" y="182" text-anchor="middle">jednorazowy kontener</text>
<text class="t cap" x="845" y="200" text-anchor="middle">Astro: Markdown →</text>
<text class="t cap" x="845" y="218" text-anchor="middle">statyczny HTML</text>
<!-- card 5 -->
<text class="t ttl" x="1075" y="152" text-anchor="middle">Publikacja</text>
<text class="t cap" x="1075" y="182" text-anchor="middle">atomowa podmiana symlinka</text>
<text class="t cap" x="1075" y="200" text-anchor="middle">nginx od razu serwuje</text>
<text class="t cap" x="1075" y="218" text-anchor="middle">nową wersję</text>
<!-- reassurance pills -->
<rect class="pill" x="240" y="330" width="330" height="40" rx="20"/>
<text class="t pilltxt" x="405" y="355" text-anchor="middle">awaria builda? strona działa dalej na starej wersji</text>
<rect class="pill" x="630" y="330" width="330" height="40" rx="20"/>
<text class="t pilltxt" x="795" y="355" text-anchor="middle">rollback = git revert — jedna komenda</text>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+48
View File
@@ -0,0 +1,48 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 360" role="img" aria-label="Porównanie powierzchni ataku: CMS z bazą wystawia publicznie aplikację, bazę i pluginy; rozwiązanie statyczne wystawia tylko pliki HTML, a edycja idzie przez git">
<defs>
<filter id="soft3" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="2" stdDeviation="5" flood-color="#00004a" flood-opacity="0.10"/>
</filter>
<marker id="arr3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 L10 5 L0 10 z" fill="#00004a"/>
</marker>
<style>
.t { font-family: Inter, "Segoe UI", Arial, sans-serif; }
.hd { font-weight: 800; font-size: 14px; fill: #00004a; letter-spacing: .14em; }
.cap { font-size: 12.5px; fill: #46536a; }
.item{ font-size: 13px; fill: #101228; }
.bad { fill: #fdf0f0; stroke: #e8c5c5; stroke-width: 1.5; }
.good{ fill: #ffffff; stroke: #00004a; stroke-width: 1.5; }
.flow{ stroke: #00004a; stroke-width: 1.8; fill: none; marker-end: url(#arr3); }
.lbl { font-size: 12px; fill: #46536a; paint-order: stroke; stroke: #ffffff; stroke-width: 4; }
</style>
</defs>
<!-- left: classic CMS -->
<text class="t hd" x="300" y="52" text-anchor="middle">CMS Z BAZĄ DANYCH</text>
<text class="t item" x="60" y="150">internet</text>
<path class="flow" d="M130 145 H186"/>
<g filter="url(#soft3)"><rect class="bad" x="190" y="80" width="360" height="200" rx="16"/></g>
<text class="t item" x="218" y="126">▪ aplikacja CMS (PHP / Node) — publiczna</text>
<text class="t item" x="218" y="158">▪ panel logowania — publiczny</text>
<text class="t item" x="218" y="190">▪ baza danych z treścią</text>
<text class="t item" x="218" y="222">▪ pluginy i zależności</text>
<text class="t cap" x="218" y="256" font-weight="700" fill="#8f3b3b">wszystko działa non stop i wymaga patchowania</text>
<!-- divider -->
<line x1="600" y1="60" x2="600" y2="320" stroke="#e2e5ee" stroke-width="1.5"/>
<!-- right: static -->
<text class="t hd" x="900" y="52" text-anchor="middle">TO ROZWIĄZANIE</text>
<text class="t item" x="646" y="130">internet</text>
<path class="flow" d="M716 125 H772"/>
<g filter="url(#soft3)"><rect class="good" x="776" y="88" width="360" height="76" rx="16"/></g>
<text class="t item" x="804" y="120">▪ pliki HTML — i nic więcej</text>
<text class="t cap" x="804" y="146">nie ma czego zhakować ani patchować</text>
<text class="t item" x="646" y="252">edycja</text>
<path class="flow" d="M716 247 H772"/>
<g filter="url(#soft3)"><rect class="good" x="776" y="210" width="360" height="76" rx="16"/></g>
<text class="t item" x="804" y="242">▪ git (Gitea) — tylko zalogowani</text>
<text class="t cap" x="804" y="268">poza ścieżką czytelnika — awaria nie kładzie strony</text>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB