docs(reports): backup from typst online

Signed-off-by: Klagarge <remi@heredero.ch>
This commit is contained in:
2026-06-10 22:00:34 +02:00
parent 5902a47605
commit 26f1553e26
7 changed files with 222 additions and 111 deletions

View File

@@ -10,6 +10,28 @@
// mqtt->db // mqtt->db
// db->rest // db->rest
== Database & API - Server
#let server = [
#figure(
image("server.png", width: 100%),
caption: [],
) <fig:server>
]
#grid(
columns: (1.5fr, 1fr),
column-gutter: 2em,
server,
align(top+left)[
#v(3em) #pause
- LXC Debian #pause
- @ssh:short certificate by user #pause
- Docker #pause
- Traefik
],
)
== Database & API - Save measures in DB
#let toDB = { #let toDB = {
import chronos: * import chronos: *
b.display b.display

View File

@@ -2,11 +2,8 @@
#import "/tail/bibliography.typ": * #import "/tail/bibliography.typ": *
#import "/tail/glossary.typ": * #import "/tail/glossary.typ": *
#import "/main/architecture/description.typ": * #import "/main/architecture/description.typ": *
#import "/resources/slides.typ": * #import "/resources/slides.typ": *
== Gateway — BLE to MQTT Bridge == Gateway — BLE to MQTT Bridge
#slide[ #slide[
#grid( #grid(
columns: (1fr, 1fr), columns: (1fr, 1fr),
@@ -22,18 +19,25 @@
[ [
*Data flow*\ *Data flow*\
Thingy:52 Thingy:52
-> BLE advertising $->$ BLE advertising
Raspberry Pi Raspberry Pi
-> MQTTS (TLS) $->$ MQTTS (TLS)
RabbitMQ broker RabbitMQ broker
-> $->$
InfluxDB InfluxDB
] ]
) )
] ]
== Gateway — Overview
#slide[
#align(center)[
#figure(
image("/resources/img/gateway_overview.svg", height: 40%),
caption: [Gateway communication chain from @ble:short advertising to database storage]
)
]
]
== Gateway — Key Challenges == Gateway — Key Challenges
#slide[ #slide[
#grid( #grid(
columns: (1fr, 1fr), columns: (1fr, 1fr),
@@ -48,23 +52,15 @@
- Deduplication: 10s cache per MAC address - Deduplication: 10s cache per MAC address
], ],
[ [
*Reliability* *Reliability — validated in production*
- MQTT auto-restart via systemd - MQTT auto-restart via systemd
- `os._exit(1)` on disconnection - `os._exit(1)` on disconnection
- Validated with 2 cases: - Validated with 2 cases:
- Network disconnection - Network disconnection
- Wrong MQTT credentials - Wrong MQTT credentials
- Multiple nodes publishing
simultaneously confirmed by broker
- No duplicate data
] ]
) )
]
== Gateway — Data Collection Flow
#slide[
#align(center)[
#figure(
image("/resources/img/gateway_overview.svg", height: 40%),
caption: [Gateway communication chain from @ble:short advertising to database storage]
)
]
] ]

View File

@@ -36,8 +36,10 @@
caption: [Data communicated in the nodes_interface], caption: [Data communicated in the nodes_interface],
)<tab:nodes_interface_content> )<tab:nodes_interface_content>
---
== Nodes | Takeaways == Nodes | Takeaways
#align(top+left)[
#v(5em)
- Breadboard validation #pause - Breadboard validation #pause
- 28 days later #pause - 28 days later #pause
- Improve @ble:short reliability - Improve @ble:short reliability
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

View File

@@ -0,0 +1,13 @@
<diagram program="umletino" version="15.1"><zoom_level>10</zoom_level><element><id>UMLDeployment</id><coordinates><x>310</x><y>280</y><w>330</w><h>350</h></coordinates><panel_attributes>Server</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>340</x><y>390</y><w>100</w><h>40</h></coordinates><panel_attributes>&lt;&lt;Broker MQTT&gt;&gt;
RabbitMQ</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>490</x><y>320</y><w>100</w><h>40</h></coordinates><panel_attributes>&lt;&lt;Database&gt;&gt;
InfluxDB3</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>490</x><y>390</y><w>100</w><h>40</h></coordinates><panel_attributes>&lt;&lt;API DB&gt;&gt;
Go service</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>430</x><y>400</y><w>80</w><h>30</h></coordinates><panel_attributes>lt=&lt;-</panel_attributes><additional_attributes>60;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>560</x><y>350</y><w>30</w><h>60</h></coordinates><panel_attributes>lt=-&gt;</panel_attributes><additional_attributes>10;10;10;40</additional_attributes></element><element><id>Relation</id><coordinates><x>510</x><y>350</y><w>30</w><h>60</h></coordinates><panel_attributes>lt=-&gt;</panel_attributes><additional_attributes>10;40;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>230</x><y>390</y><w>130</w><h>50</h></coordinates><panel_attributes>lt=-()
m2=MQTT
</panel_attributes><additional_attributes>110;20;10;20</additional_attributes></element><element><id>Relation</id><coordinates><x>530</x><y>430</y><w>190</w><h>50</h></coordinates><panel_attributes>lt=-()
m2=REST
</panel_attributes><additional_attributes>10;20;170;20</additional_attributes></element><element><id>UMLDeployment</id><coordinates><x>330</x><y>510</y><w>110</w><h>50</h></coordinates><panel_attributes>&lt;&lt;Dashboard&gt;&gt;
Angular</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>430</x><y>490</y><w>140</w><h>60</h></coordinates><panel_attributes>lt=)-
m1=
</panel_attributes><additional_attributes>110;20;110;40;10;40</additional_attributes></element><element><id>UMLDeployment</id><coordinates><x>330</x><y>570</y><w>110</w><h>50</h></coordinates><panel_attributes>&lt;&lt;Notification&gt;&gt;
Java</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>430</x><y>520</y><w>130</w><h>90</h></coordinates><panel_attributes></panel_attributes><additional_attributes>110;10;110;70;10;70</additional_attributes></element><element><id>Relation</id><coordinates><x>530</x><y>420</y><w>60</w><h>90</h></coordinates><panel_attributes>lt=-()
m2=REST</panel_attributes><additional_attributes>10;10;10;70</additional_attributes></element></diagram>

View File

@@ -4,7 +4,6 @@
#import "/main/architecture/description.typ": * #import "/main/architecture/description.typ": *
#import "/resources/slides.typ": * #import "/resources/slides.typ": *
#show:make-glossary #show:make-glossary
#register-glossary(entry-list) #register-glossary(entry-list)

View File

@@ -5,6 +5,10 @@
#import "/resources/slides.typ": * #import "/resources/slides.typ": *
// Chemin racine des images — adapter selon ta structure de projet
#let img-root = "../../resources/img/ui_images/images/"
// ── Palette ───────────────────────────────────────────────────────────────────
#let c-dark = rgb("#0F172A") #let c-dark = rgb("#0F172A")
#let c-teal = rgb("#0EA5E9") #let c-teal = rgb("#0EA5E9")
#let c-text = rgb("#1E293B") #let c-text = rgb("#1E293B")
@@ -12,105 +16,180 @@
#let c-white = rgb("#FFFFFF") #let c-white = rgb("#FFFFFF")
#let c-border = rgb("#E2E8F0") #let c-border = rgb("#E2E8F0")
--- // ── Helpers ───────────────────────────────────────────────────────────────────
───────────────────────────────────────────────────────────────────────────── #let icon-circle(img-path, size: 38pt, bg: rgb("#0EA5E9")) = {
// SLIDE 2 — Cycle DevSecOps box(
// ───────────────────────────────────────────────────────────────────────────── width: size, height: size,
#let slide-devsecops() = { fill: bg, radius: (size / 2),
// Header custom (subtitle à droite) inset: 0pt, clip: true,
block(width: 100%, height: 43.9pt, fill: c-dark, inset: 0pt)[ )[
#pad(x: 25.5pt)[ #align(center + horizon)[
#align(horizon)[ #image(img-path, width: (size * 0.62), height: (size * 0.62), fit: "contain")
#grid(columns: (1fr, auto), ]
]
}
#let badge(label, fill: rgb("#EF4444")) = {
box(fill: fill, radius: 3pt, inset: (x: 5pt, y: 2pt))[
#text(size: 6.5pt, weight: "bold", fill: rgb("#FFFFFF"))[#label]
]
}
#let devsec-col(phase-title, items) = {
block(width: 100%)[
#block(
width: 100%, height: 28pt,
fill: c-dark,
radius: (top-left: 5pt, top-right: 5pt, bottom-left: 0pt, bottom-right: 0pt),
inset: 0pt,
)[
#pad(x: 7pt)[
#align(horizon + center)[
#text(size: 8.5pt, weight: "bold", fill: rgb("#FFFFFF"))[#phase-title]
]
]
]
#block(
width: 100%,
fill: rgb("#F1F5F9"),
radius: (top-left: 0pt, top-right: 0pt, bottom-left: 5pt, bottom-right: 5pt),
inset: 8pt,
)[
#for item in items {
grid(columns: (30pt, 1fr), gutter: 6pt,
icon-circle(item.at("icon"), size: 28pt, bg: item.at("bg", default: c-teal)),
align(left + horizon)[ align(left + horizon)[
#text(size: 16pt, weight: "bold", fill: rgb("#FFFFFF"))[Cycle DevSecOps - PI E2EEDA] #text(size: 8pt, weight: "bold", fill: c-text)[#item.at("name")]
], #linebreak()
align(right + horizon)[ #item.at("extra", default: [])
#text(size: 8pt, fill: rgb("#94A3B8"))[Shift-left · Security by design]
], ],
) )
v(5pt)
}
]
]
}
== Cycle DevSecOps
#slide[
#grid(columns: (1fr, 1fr, 1fr, 1fr), gutter: 8pt,
devsec-col("① Code & PR Gate", (
(icon: img-root + "image10.png", bg: rgb("#3178C6"), name: "TypeScript",
extra: [#text(size: 6.5pt, fill: c-muted)[tsc / ESLint]]),
(icon: img-root + "image11.png", bg: rgb("#DD0031"), name: "Angular CI",
extra: [#text(size: 6.5pt, fill: c-muted)[Build check]]),
(icon: img-root + "image19.png", bg: rgb("#1F2328"), name: "GitHub Actions",
extra: [#text(size: 6.5pt, fill: c-muted)[Coverage]]),
)),
devsec-col("② SAST · SCA", (
(icon: img-root + "image7.png", bg: rgb("#000000"), name: "SpotBugs",
extra: [#badge("BLOCKING")]),
(icon: img-root + "image13.png", bg: rgb("#1F2328"), name: "CodeQL",
extra: [#badge("BLOCKING")]),
(icon: img-root + "image14.png", bg: rgb("#F97316"), name: "Dep. Check",
extra: [#badge("NON-BLOCK", fill: rgb("#F97316"))]),
)),
devsec-col("③ DAST · Tests", (
(icon: img-root + "image15.png", bg: rgb("#00549E"), name: "OWASP ZAP",
extra: [#badge("BLOCKING")]),
(icon: img-root + "image16.png", bg: rgb("#DD0031"), name: "Karma Tests",
extra: [#badge("BLOCKING")]),
(icon: img-root + "image17.png", bg: rgb("#64748B"), name: "Runtime check",
extra: [#text(size: 6.5pt, fill: c-muted)[HTTP headers]]),
)),
devsec-col("④ Build · Deploy", (
(icon: img-root + "image18.png", bg: rgb("#0DB7ED"), name: "Docker",
extra: [#text(size: 6.5pt, fill: c-muted)[SHA-tagged]]),
(icon: img-root + "image19.png", bg: rgb("#1F2328"), name: "GHCR Push",
extra: [#text(size: 6.5pt, fill: c-muted)[main only]]),
(icon: img-root + "image17.png", bg: rgb("#10B981"), name: "SSH Deploy",
extra: [#text(size: 6.5pt, fill: c-muted)[cert-auth]]),
)),
)
#v(7pt)
#rect(width: 100%, height: 26pt, fill: c-dark, radius: 4pt, inset: 0pt)[
#pad(x: 20pt)[
#align(horizon)[
#grid(columns: (1fr, 1fr, 1fr),
align(center + horizon)[#text(size: 8pt, fill: rgb("#FFFFFF"))[🔐 #h(2pt) Shift-left security]],
align(center + horizon)[#text(size: 8pt, fill: rgb("#FFFFFF"))[🔑 #h(2pt) Zero secret in code]],
align(center + horizon)[#text(size: 8pt, fill: rgb("#FFFFFF"))[🔄 #h(2pt) Automated Deployment]],
)
] ]
] ]
] ]
pad(x: 17pt, top: 11pt, bottom: 9pt)[ #v(6pt)
#grid(columns: (1fr, 1fr, 1fr, 1fr), gutter: 10pt, ]
// ① Code & PR Gate
// image10 = TypeScript (TS logo), image11 = Angular (grisé)
// image19 = GitHub Actions (GHCR/GitHub logo)
devsec-col("① Code & PR Gate", (
(icon: "../../resources/img/ui_images/images/image10.png", bg: rgb("#3178C6"), name: "TypeScript",
extra: [#text(size: 7pt, fill: c-muted)[tsc / ESLint]]),
(icon: "../../resources/img/ui_images/images/image11.png", bg: rgb("#DD0031"), name: "Angular CI",
extra: [#text(size: 7pt, fill: c-muted)[Build check]]),
)),
// ② SAST · SCA // ── SLIDE 3 — Dashboard ───────────────────────────────────────────────────────
// image13 = GitHub octocat (SpotBugs hébergé GitHub), image13 aussi CodeQL (GitHub) == Dashboard
// image14 = checklist hexagone (Dep. Check / OWASP DC)
devsec-col("② SAST · SCA", (
(icon: "../../resources/img/ui_images/images/image13.png", bg: rgb("#1F2328"), name: "SpotBugs",
extra: [#badge("BLOCKING")]),
(icon: "../../resources/img/ui_images/images/image13.png", bg: rgb("#1F2328"), name: "CodeQL",
extra: [#badge("BLOCKING")]),
(icon: "../../resources/img/ui_images/images/image14.png", bg: rgb("#F97316"), name: "Dep. Check",
extra: [#badge("NON-BLOCK", fill: rgb("#F97316"))]),
)),
// ③ DAST · Tests #slide[
// image15 = libellule OWASP ZAP, image16 = Karma/Jasmine, image17 = Tux Linux #align(center + horizon)[
devsec-col("③ DAST · Tests", ( #figure(
(icon: "../../resources/img/ui_images/images/image15.png", bg: rgb("#00549E"), name: "OWASP ZAP", image(img-root + "image20.png", width: 100%, fit: "contain"),
extra: [#badge("BLOCKING")]), caption: [Dashboard]
(icon: "../../resources/img/ui_images/images/image16.png", bg: rgb("#DD0031"), name: "Karma Tests", )
extra: [#badge("BLOCKING")]),
(icon: "../../resources/img/ui_images/images/image17.png", bg: rgb("#64748B"), name: "Runtime check",
extra: [#text(size: 7pt, fill: c-muted)[HTTP headers]]),
)),
// ④ Build · Deploy
// image18 = Docker, image19 = GHCR, image17 = Tux Linux (SSH)
devsec-col("④ Build · Deploy", (
(icon: "../../resources/img/ui_images/images/image18.png", bg: rgb("#0DB7ED"), name: "Docker",
extra: [#text(size: 7pt, fill: c-muted)[SHA-tagged]]),
(icon: "../../resources/img/ui_images/images/image19.png", bg: rgb("#1F2328"), name: "GHCR Push",
extra: [#text(size: 7pt, fill: c-muted)[main only]]),
(icon: "../../resources/img/ui_images/images/image17.png", bg: rgb("#10B981"), name: "SSH Deploy",
extra: [#text(size: 7pt, fill: c-muted)[cert-auth]]),
)),
)
#v(9pt)
// Barre résumé
#rect(width: 100%, height: 30pt, fill: c-dark, radius: 5pt, inset: 0pt)[
#pad(x: 30pt)[
#align(horizon)[
#grid(columns: (1fr, 1fr, 1fr),
align(center + horizon)[#text(size: 9pt, fill: rgb("#FFFFFF"))[🔐 #h(3pt) Shift-left security]],
align(center + horizon)[#text(size: 9pt, fill: rgb("#FFFFFF"))[🔑 #h(3pt) Zero secret in code]],
align(center + horizon)[#text(size: 9pt, fill: rgb("#FFFFFF"))[🔄 #h(3pt) Automated Deployment]],
)
]
]
]
#v(8pt)
// GitHub Actions — image19 (GitHub/GHCR logo)
#align(center)[
#grid(columns: (auto, auto, auto), gutter: 7pt,
align(horizon)[#icon-circle("../../resources/img/ui_images/images/image19.png", size: 24pt, bg: rgb("#1F2328"))],
align(horizon)[#text(size: 8.5pt, weight: "bold", fill: c-text)[GitHub Actions]],
align(horizon)[#text(size: 8.5pt, fill: c-muted)[Coverage]],
)
]
] ]
]
v(1fr) // ── SLIDE 4 — Details Page ────────────────────────────────────────────────────
== Details Page
}
// #slide[
#align(center + horizon)[
#figure(
image(img-root + "image21.png", width: 100%, fit: "contain"),
caption: [Details page]
)
]
]
// ── SLIDE 5 — Notification ────────────────────────────────────────────────────
== Notification
#slide[
#grid(columns: (1fr, 1fr), gutter: 16pt,
// Screenshot Telegram
align(center + horizon)[
#figure(
image(img-root + "image22.png", height: 300pt, fit: "contain"),
caption: [Telegram notification]
)
],
// Carte descriptive
rect(width: 100%, radius: 6pt, stroke: 0.5pt + c-border, fill: rgb("#FFFFFF"), inset: 12pt)[
#text(size: 10pt, weight: "bold", fill: c-text)[CO₂ Alerts System]
#v(5pt)
#line(length: 100%, stroke: 0.5pt + c-border)
#v(6pt)
#let alert-row(col, level, desc) = {
grid(columns: (10pt, 52pt, 1fr), gutter: 5pt,
box(width: 8pt, height: 8pt, fill: col, radius: 4pt),
text(size: 8.5pt, weight: "bold")[#level],
text(size: 8pt, fill: c-muted)[#desc],
)
v(4pt)
}
#alert-row(rgb("#EF4444"), "Critical", "> 2000 ppm")
#alert-row(rgb("#F97316"), "Poor", "12001500 ppm")
#alert-row(rgb("#EAB308"), "Moderate", "10001200 ppm")
#alert-row(rgb("#22C55E"), "Good", "< 800 ppm")
#v(6pt)
#line(length: 100%, stroke: 0.5pt + c-border)
#v(6pt)
#v(6pt)
],
)
]