diff --git a/LateX/main.aux b/LateX/main.aux index aec9596..26649f1 100644 --- a/LateX/main.aux +++ b/LateX/main.aux @@ -69,12 +69,49 @@ \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Firewall aktivieren}{10}{subsubsection.2.5.3}\protected@file@percent } \@writefile{lol}{\contentsline {lstlisting}{\numberline {21}Firewall aktivieren}{10}{lstlisting.21}\protected@file@percent } \BKM@entry{id=19,dest={73756273656374696F6E2E322E36},srcline={179}}{5C3337365C3337375C3030305A5C303030755C303030735C303030615C3030306D5C3030306D5C303030655C3030306E5C303030665C303030615C303030735C303030735C303030755C3030306E5C30303067} +\BKM@entry{id=20,dest={73656374696F6E2E33},srcline={5}}{5C3337365C3337375C303030445C3030306F5C303030635C3030306B5C303030655C303030725C3030302D5C303030495C3030306D5C303030615C303030675C303030655C303030735C3030305C3034305C303030625C303030615C303030755C303030655C3030306E5C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C303030415C303030705C303030705C3030305C3034305C303030645C303030655C303030705C3030306C5C3030306F5C303030795C303030655C3030306E} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.4}Konfiguration überprüfen}{11}{subsubsection.2.5.4}\protected@file@percent } \@writefile{lol}{\contentsline {lstlisting}{\numberline {22}Firewall-Status mit Details anzeigen}{11}{lstlisting.22}\protected@file@percent } \@writefile{lol}{\contentsline {lstlisting}{\numberline {23}Erwartete Firewall-Ausgabe (gekürzt)}{11}{lstlisting.23}\protected@file@percent } \@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Zusammenfassung}{11}{subsection.2.6}\protected@file@percent } +\BKM@entry{id=21,dest={73756273656374696F6E2E332E31},srcline={10}}{5C3337365C3337375C303030575C303030615C303030735C3030305C3034305C303030695C303030735C303030745C3030305C3034305C303030445C3030306F5C303030635C3030306B5C303030655C303030725C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C303030775C303030615C303030725C303030755C3030306D5C3030305C3034305C3030306E5C303030755C303030745C3030307A5C303030655C3030306E5C3030305C3034305C303030775C303030695C303030725C3030305C3034305C303030655C303030735C3030303F} +\BKM@entry{id=22,dest={73756273656374696F6E2E332E32},srcline={27}}{5C3337365C3337375C303030445C303030695C303030655C3030305C3034305C303030645C303030725C303030655C303030695C3030305C3034305C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C303030655C303030735C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C} +\BKM@entry{id=23,dest={73756273756273656374696F6E2E332E322E31},srcline={29}}{5C3337365C3337375C303030425C303030615C303030635C3030306B5C303030655C3030306E5C303030645C3030302D5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C303030655C3030303A5C3030305C3034305C303030615C303030705C303030705C303030735C3030302F5C303030615C303030705C303030695C3030302F5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C30303065} +\@writefile{toc}{\contentsline {section}{\numberline {3}Docker-Images bauen und App deployen}{12}{section.3}\protected@file@percent } +\newlabel{sec:step03}{{3}{12}{Docker-Images bauen und App deployen}{section.3}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}Was ist Docker und warum nutzen wir es?}{12}{subsection.3.1}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Die drei Dockerfiles im Detail}{12}{subsection.3.2}\protected@file@percent } +\@writefile{toc}{\contentsline {subsubsection}{\numberline {3.2.1}Backend-Dockerfile: \texttt {apps/api/Dockerfile}}{12}{subsubsection.3.2.1}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {24}Dockerfile für das .NET Backend}{12}{lstlisting.24}\protected@file@percent } +\BKM@entry{id=24,dest={73756273756273656374696F6E2E332E322E32},srcline={64}}{5C3337365C3337375C303030465C303030725C3030306F5C3030306E5C303030745C303030655C3030306E5C303030645C3030302D5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C303030655C3030303A5C3030305C3034305C303030615C303030705C303030705C303030735C3030302F5C303030775C303030655C303030625C3030302F5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C30303065} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {3.2.2}Frontend-Dockerfile: \texttt {apps/web/Dockerfile}}{13}{subsubsection.3.2.2}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {25}Dockerfile für das React Frontend}{13}{lstlisting.25}\protected@file@percent } +\BKM@entry{id=25,dest={73756273756273656374696F6E2E332E322E33},srcline={99}}{5C3337365C3337375C3030304E5C303030675C303030695C3030306E5C303030785C3030302D5C3030304B5C3030306F5C3030306E5C303030665C303030695C303030675C303030755C303030725C303030615C303030745C303030695C3030306F5C3030306E5C3030303A5C3030305C3034305C303030615C303030705C303030705C303030735C3030302F5C303030775C303030655C303030625C3030302F5C3030306E5C303030675C303030695C3030306E5C303030785C3030302E5C303030635C3030306F5C3030306E5C30303066} +\BKM@entry{id=26,dest={73756273656374696F6E2E332E33},srcline={130}}{5C3337365C3337375C303030445C303030615C303030735C3030305C3034305C303030425C303030615C303030635C3030306B5C303030655C3030306E5C303030645C3030303A5C3030305C3034305C303030505C303030725C3030306F5C303030675C303030725C303030615C3030306D5C3030302E5C303030635C303030735C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {3.2.3}Nginx-Konfiguration: \texttt {apps/web/nginx.conf}}{14}{subsubsection.3.2.3}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {26}Nginx-Konfiguration mit Reverse Proxy}{14}{lstlisting.26}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {3.3}Das Backend: Program.cs im Detail}{15}{subsection.3.3}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {27}Vollständige Program.cs}{15}{lstlisting.27}\protected@file@percent } +\BKM@entry{id=27,dest={73756273656374696F6E2E332E34},srcline={186}}{5C3337365C3337375C303030445C303030655C303030725C3030305C3034305C303030415C303030505C303030495C3030302D5C303030435C3030306C5C303030695C303030655C3030306E5C303030745C3030303A5C3030305C3034305C303030635C3030306C5C303030695C303030655C3030306E5C303030745C3030302E5C303030745C303030735C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C} +\BKM@entry{id=28,dest={73756273656374696F6E2E332E35},srcline={226}}{5C3337365C3337375C303030445C303030615C303030735C3030305C3034305C303030465C303030725C3030306F5C3030306E5C303030745C303030655C3030306E5C303030645C3030303A5C3030305C3034305C303030415C303030705C303030705C3030302E5C303030745C303030735C303030785C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.4}Der API-Client: client.ts im Detail}{16}{subsection.3.4}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {28}Manueller API-Client}{16}{lstlisting.28}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {3.5}Das Frontend: App.tsx im Detail}{16}{subsection.3.5}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {29}Hauptkomponente mit Workout-Liste und Formular}{16}{lstlisting.29}\protected@file@percent } +\BKM@entry{id=29,dest={73756273656374696F6E2E332E36},srcline={265}}{5C3337365C3337375C303030495C3030306D5C303030615C303030675C303030655C303030735C3030305C3034305C303030625C303030615C303030755C303030655C3030306E} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.6}Images bauen}{17}{subsection.3.6}\protected@file@percent } +\BKM@entry{id=30,dest={73756273656374696F6E2E332E37},srcline={287}}{5C3337365C3337375C303030495C3030306D5C303030615C303030675C303030655C303030735C3030305C3034305C303030655C303030785C303030705C3030306F5C303030725C303030745C303030695C303030655C303030725C303030655C3030306E5C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C303030615C303030755C303030665C3030305C3034305C303030645C303030655C3030306E5C3030305C3034305C303030535C303030655C303030725C303030765C303030655C303030725C3030305C3034305C3030306B5C3030306F5C303030705C303030695C303030655C303030725C303030655C3030306E} +\BKM@entry{id=31,dest={73756273656374696F6E2E332E38},srcline={311}}{5C3337365C3337375C303030435C3030306F5C3030306E5C303030745C303030615C303030695C3030306E5C303030655C303030725C3030305C3034305C303030615C303030755C303030665C3030305C3034305C303030645C303030655C3030306D5C3030305C3034305C303030535C303030655C303030725C303030765C303030655C303030725C3030305C3034305C303030735C303030745C303030615C303030725C303030745C303030655C3030306E} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.7}Images exportieren und auf den Server kopieren}{18}{subsection.3.7}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {30}Images exportieren und kopieren}{18}{lstlisting.30}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {3.8}Container auf dem Server starten}{18}{subsection.3.8}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {31}Docker-Netzwerk, Volume und Container anlegen}{18}{lstlisting.31}\protected@file@percent } +\BKM@entry{id=32,dest={73756273656374696F6E2E332E39},srcline={345}}{5C3337365C3337375C303030415C303030755C303030665C303030675C303030655C303030745C303030725C303030655C303030745C303030655C3030306E5C303030655C3030305C3034305C303030505C303030725C3030306F5C303030625C3030306C5C303030655C3030306D5C303030655C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C3030304C5C3030305C3336365C303030735C303030755C3030306E5C303030675C303030655C3030306E} +\BKM@entry{id=33,dest={73756273656374696F6E2E332E3130},srcline={372}}{5C3337365C3337375C3030305A5C303030755C303030735C303030615C3030306D5C3030306D5C303030655C3030306E5C303030665C303030615C303030735C303030735C303030755C3030306E5C30303067} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.9}Aufgetretene Probleme und Lösungen}{19}{subsection.3.9}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {3.10}Zusammenfassung}{20}{subsection.3.10}\protected@file@percent } \global\@namedef{scr@dte@section@lastmaxnumwidth}{11.00392pt} -\global\@namedef{scr@dte@subsection@lastmaxnumwidth}{19.9919pt} -\global\@namedef{scr@dte@subsubsection@lastmaxnumwidth}{28.39188pt} +\global\@namedef{scr@dte@subsection@lastmaxnumwidth}{24.81589pt} +\global\@namedef{scr@dte@subsubsection@lastmaxnumwidth}{28.41586pt} \@writefile{toc}{\providecommand\tocbasic@end@toc@file{}\tocbasic@end@toc@file} -\gdef \@abspage@last{11} +\gdef \@abspage@last{20} diff --git a/LateX/main.fdb_latexmk b/LateX/main.fdb_latexmk index d6ddf4c..5565f6d 100644 --- a/LateX/main.fdb_latexmk +++ b/LateX/main.fdb_latexmk @@ -1,7 +1,7 @@ # Fdb version 4 -["pdflatex"] 1778074526.98351 "/home/computer/projects/fitness-app/LateX/main.tex" "main.pdf" "main" 1778074529.108 0 +["pdflatex"] 1778081248.77398 "/home/computer/projects/fitness-app/LateX/main.tex" "main.pdf" "main" 1778081251.03276 0 "/etc/texmf/web2c/texmf.cnf" 1776891072.07073 475 c0e671620eb5563b2130f56340a5fde8 "" - "/home/computer/projects/fitness-app/LateX/main.tex" 1778074523.06229 8392 bdb3148a64fa8b2e535471367f7e4f1e "" + "/home/computer/projects/fitness-app/LateX/main.tex" 1778081236.97865 8413 41aad5c5d32e893cafb487728bf379a6 "" "/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc" 1570828436 2978 6d777d1174162fa94ff58f36782f4570 "" "/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc" 1570828436 3385 21a7e8c8dac3c39de5acda2c56e7bd7e "" "/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_iln36p.enc" 1570828436 3071 cfa92ee28d698dd9275559d9d1c3a233 "" @@ -22,6 +22,8 @@ "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1136768653 1120 8b7d695260f3cff42e636090a8002094 "" "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1--base.tfm" 1556836886 1324 0a7092c8d43950fc7585219358b7afd2 "" "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1.tfm" 1556836886 1376 464dde77446a9d55bb77e0f8000924a5 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1--base.tfm" 1559682857 1544 e1d8938f0433eb7b9914e10941e55e1c "" + "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1.tfm" 1559682857 1596 ad99e5db8e2fbbe207fd41ae14f51134 "" "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1--base.tfm" 1556836886 1308 9050f92394f218407f72f188eb4d1a33 "" "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm" 1556836886 1360 dfbacd55e2269d2ff422e3de3884feab "" "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Bold-osf-t1--base.tfm" 1558644978 1796 11e21ab836516246ef24a10a351d2541 "" @@ -32,10 +34,12 @@ "/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-ts1.tfm" 1554498137 1456 8ee3e51b7984f3df81900715b34ebdeb "" "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb" 1248133631 32569 5e5ddc8df908dea60932f3c484a54c0d "" "/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Bold.pfb" 1559682857 205973 90710d15f8d1d4d4f6bdd622521b398f "" + "/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Oblique.pfb" 1559682857 271356 1b50b6cf73a83edb93bda1aa45a1679d "" "/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Regular.pfb" 1559682857 192962 8eef27b162fb2864c33843b9ad6cdbc2 "" "/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Bold.pfb" 1554498137 485112 0bd86946afd04160ee1229d86cd76902 "" "/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Regular.pfb" 1554498137 457228 86b22575fa05027949aa646d89908746 "" "/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Bold-tosf-t1.vf" 1556836886 1692 8b219f0f8fd0e6dfdce79d58d9f68088 "" + "/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Oblique-tosf-t1.vf" 1559682857 1696 f96e3873fa4905e557b6ed829aef1f0f "" "/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf" 1556836886 1696 af65436ccdde59c952e95f8cc457dbb5 "" "/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Bold-osf-t1.vf" 1558644978 1724 1ac6e230aa57cf8ba8f965fbceffd874 "" "/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-t1.vf" 1558644978 1724 e5cbcafbef1b8b346466340ef078339c "" @@ -130,12 +134,13 @@ "/usr/share/texmf/web2c/texmf.cnf" 1707919699 40399 f2c302f7d2af602abb742093540a5834 "" "/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map" 1776891108.46284 5472669 54eaf61a88b6b7896ebd0dac973cb29c "" "/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1776891271 8211336 7fc26d317f030a4855527787ba3b41d3 "" - "main.aux" 1778074529.0082 14507 a445efc066d6f1fab05528a5ef320e35 "pdflatex" - "main.out" 1778074528.61242 0 d41d8cd98f00b204e9800998ecf8427e "pdflatex" - "main.tex" 1778074523.06229 8392 bdb3148a64fa8b2e535471367f7e4f1e "" - "main.toc" 1778074529.0122 1951 c589379c8507d6fcc5ff4c6e2179786a "pdflatex" + "main.aux" 1778081250.91843 23846 804a4d87b04ab7403c0abdcb70108b86 "pdflatex" + "main.out" 1778081250.30135 0 d41d8cd98f00b204e9800998ecf8427e "pdflatex" + "main.tex" 1778081236.97865 8413 41aad5c5d32e893cafb487728bf379a6 "" + "main.toc" 1778081250.92443 3404 2187def0eadc57fc64da16e547916901 "pdflatex" "step_01.tex" 1778074119.30934 10807 dd7fc11a20ecebed2f07638ceddcf838 "" "step_02.tex" 1778074524.52004 9161 30219f0c68c4ae118067f27c09a123fb "" + "step_03.tex" 1778081246.2535 17575 0dde7ed301abaec40a3b2d19357b160b "" (generated) "main.aux" "main.log" diff --git a/LateX/main.fls b/LateX/main.fls index ddf6944..873f3b5 100644 --- a/LateX/main.fls +++ b/LateX/main.fls @@ -230,11 +230,18 @@ INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm +INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd +INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd +INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm OUTPUT main.toc INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Bold-osf-t1.vf INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Bold-osf-t1--base.tfm INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-t1.vf INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-t1--base.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1--base.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc INPUT ./step_01.tex INPUT ./step_01.tex INPUT step_01.tex @@ -242,18 +249,11 @@ INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-ts1.tfm -INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-t1.tfm INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-ts1.vf INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-ts1--base.tfm INPUT /usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc -INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1--base.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-t1.vf INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-t1--base.tfm INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf @@ -264,10 +264,20 @@ INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1- INPUT ./step_02.tex INPUT ./step_02.tex INPUT step_02.tex +INPUT ./step_03.tex +INPUT ./step_03.tex +INPUT step_03.tex +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Bold-tosf-t1.vf +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1--base.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1.tfm +INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Oblique-tosf-t1.vf +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1--base.tfm INPUT main.aux INPUT ./main.out INPUT ./main.out INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Bold.pfb +INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Oblique.pfb INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Regular.pfb INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Bold.pfb INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Regular.pfb diff --git a/LateX/main.log b/LateX/main.log index 6bacf51..4dbf3a0 100644 --- a/LateX/main.log +++ b/LateX/main.log @@ -1,4 +1,4 @@ -This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023/Debian) (preloaded format=pdflatex 2026.4.22) 6 MAY 2026 15:35 +This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023/Debian) (preloaded format=pdflatex 2026.4.22) 6 MAY 2026 17:27 entering extended mode restricted \write18 enabled. file:line:error style messages enabled. @@ -615,13 +615,26 @@ File: mt-cmr.cfg 2013/05/19 v2.2 microtype config. file: Computer Modern Roman ( {/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_iln36p.enc}] Package tocbasic Info: character protrusion at toc deactivated on input line 239. - (./main.toc) + (./main.toc +LaTeX Font Info: Trying to load font information for T1+FiraMono-TOsF on input line 24. + (/usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd +File: T1FiraMono-TOsF.fd 2019/10/10 (autoinst) Font definitions for T1/FiraMono-TOsF. +) +LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/n' in size <12> not available +(Font) Font shape `T1/FiraMono-TOsF/regular/n' tried instead on input line 24. +LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/n' will be +(Font) scaled to size 10.79993pt on input line 24. +Package microtype Info: Loading generic protrusion settings for font family +(microtype) `FiraMono-TOsF' (encoding: T1). +(microtype) For optimal results, create family-specific settings. +(microtype) See the microtype manual for details. +) \tf@toc=\write4 \openout4 = `main.toc'. [2 -] (./step_01.tex +{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc}] (./step_01.tex LaTeX Font Info: Trying to load font information for TS1+FiraSans-OsF on input line 21. (/usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd File: TS1FiraSans-OsF.fd 2019/10/10 (autoinst) Font definitions for TS1/FiraSans-OsF. @@ -634,18 +647,6 @@ Package microtype Info: Loading generic protrusion settings for font family (microtype) `FiraSans-OsF' (encoding: TS1). (microtype) For optimal results, create family-specific settings. (microtype) See the microtype manual for details. -LaTeX Font Info: Trying to load font information for T1+FiraMono-TOsF on input line 21. - (/usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd -File: T1FiraMono-TOsF.fd 2019/10/10 (autoinst) Font definitions for T1/FiraMono-TOsF. -) -LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/n' in size <12> not available -(Font) Font shape `T1/FiraMono-TOsF/regular/n' tried instead on input line 21. -LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/n' will be -(Font) scaled to size 10.79993pt on input line 21. -Package microtype Info: Loading generic protrusion settings for font family -(microtype) `FiraMono-TOsF' (encoding: T1). -(microtype) For optimal results, create family-specific settings. -(microtype) See the microtype manual for details. Package hyperref Info: bookmark level for unknown lstlisting defaults to 0 on input line 35. LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/n' in size <10> not available (Font) Font shape `T1/FiraMono-TOsF/regular/n' tried instead on input line 35. @@ -657,7 +658,7 @@ LaTeX Font Info: Font shape `T1/FiraSans-OsF/regular/n' will be (Font) scaled to size 10.0pt on input line 36. [3 -{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc}{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc}] [4] +{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc}] [4] LaTeX Font Info: Font shape `T1/FiraMono-TOsF/b/n' in size <10> not available (Font) Font shape `T1/FiraMono-TOsF/bold/n' tried instead on input line 140. LaTeX Font Info: Font shape `T1/FiraMono-TOsF/bold/n' will be @@ -709,7 +710,44 @@ Overfull \hbox (3.891pt too wide) in paragraph at lines 77--78 []\T1/FiraSans-OsF/regular/n/12 (-20) HTTP-Verbindungen kön-nen von An-grei-fern ver-än-dert wer-den (Man-in-the-Middle) [] -[9] [10]) [11] (./main.aux) +[9] [10]) (./step_03.tex [11] +LaTeX Font Info: Font shape `T1/FiraMono-TOsF/b/n' in size <12> not available +(Font) Font shape `T1/FiraMono-TOsF/bold/n' tried instead on input line 29. +LaTeX Font Info: Font shape `T1/FiraMono-TOsF/bold/n' will be +(Font) scaled to size 10.79993pt on input line 29. + [12] [13] +Overfull \hbox (74.52446pt too wide) in paragraph at lines 125--126 +[]\T1/FiraMono-TOsF/regular/n/12 location / \T1/FiraSans-OsF/regular/n/12 (-20) ^^U An-fra-gen an die Haupt-sei-te $\OMS/cmsy/m/n/12 !$ \T1/FiraSans-OsF/regular/n/12 (-20) lie-fert React-Dateien aus \T1/FiraMono-TOsF/regular/n/12 /usr/share/nginx/html + [] + + +Overfull \hbox (36.2292pt too wide) in paragraph at lines 126--127 +[]\T1/FiraMono-TOsF/regular/n/12 location /api/ \T1/FiraSans-OsF/regular/n/12 (-20) ^^U An-fra-gen an \T1/FiraMono-TOsF/regular/n/12 /api/* $\OMS/cmsy/m/n/12 !$ \T1/FiraSans-OsF/regular/n/12 (-20) lei-tet sie an das Ba-ckend (\T1/FiraMono-TOsF/regular/n/12 fitness-api:5000\T1/FiraSans-OsF/regular/n/12 (-20) ) + [] + +[14] [15] +LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/it' in size <10> not available +(Font) Font shape `T1/FiraMono-TOsF/regular/it' tried instead on input line 214. +LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/it' in size <10> not available +(Font) Font shape `T1/FiraMono-TOsF/regular/sl' tried instead on input line 214. +LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/sl' will be +(Font) scaled to size 8.99994pt on input line 214. + +Overfull \hbox (5.87708pt too wide) in paragraph at lines 220--221 +[]\T1/FiraMono-TOsF/regular/n/12 API_BASE = \T1/FiraSans-OsF/regular/n/12 (-20) ^^U Kei-ne ab-so-lu-te URL! Statt-des-sen re-la-ti-ve Pfa-de wie \T1/FiraMono-TOsF/regular/n/12 /api/workouts\T1/FiraSans-OsF/regular/n/12 (-20) . + [] + +[16] [17] [18] +Overfull \hbox (10.69511pt too wide) in paragraph at lines 341--342 +[]\T1/FiraMono-TOsF/regular/n/12 -v fitness-data:/app/data \T1/FiraSans-OsF/regular/n/12 (-20) ^^U Bin-det das Vo-lu-me \T1/FiraMono-TOsF/regular/n/12 fitness-data \T1/FiraSans-OsF/regular/n/12 (-20) in den Container- + [] + + +Overfull \hbox (0.99844pt too wide) in paragraph at lines 361--362 +[]\T1/FiraSans-OsF/regular/n/12 (-20) Ursache: Im Cli-ent stand \T1/FiraMono-TOsF/regular/n/12 const API_BASE = "http://192.168.178.189:5107"\T1/FiraSans-OsF/regular/n/12 (-20) . + [] + +[19]) [20] (./main.aux) *********** LaTeX2e <2023-11-01> patch level 1 L3 programming layer <2024-01-22> @@ -718,18 +756,18 @@ Package rerunfilecheck Info: File `main.out' has not changed. (rerunfilecheck) Checksum: D41D8CD98F00B204E9800998ECF8427E;0. ) Here is how much of TeX's memory you used: - 19308 strings out of 474222 - 335876 string characters out of 5748733 - 2134975 words of memory out of 5000000 - 41036 multiletter control sequences out of 15000+600000 - 686653 words of font info for 181 fonts, out of 8000000 for 9000 + 19894 strings out of 474222 + 343688 string characters out of 5748733 + 2200975 words of memory out of 5000000 + 41353 multiletter control sequences out of 15000+600000 + 692737 words of font info for 213 fonts, out of 8000000 for 9000 1141 hyphenation exceptions out of 8191 - 108i,10n,107p,10941b,2145s stack positions out of 10000i,1000n,20000p,200000b,200000s - -Output written on main.pdf (11 pages, 219584 bytes). + 108i,10n,107p,10941b,2229s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on main.pdf (20 pages, 300891 bytes). PDF statistics: - 292 PDF objects out of 1000 (max. 8388607) - 265 compressed objects within 3 object streams - 115 named destinations out of 1000 (max. 500000) - 44697 words of extra memory for PDF output out of 51595 (max. 10000000) + 651 PDF objects out of 1000 (max. 8388607) + 609 compressed objects within 7 object streams + 336 named destinations out of 1000 (max. 500000) + 45833 words of extra memory for PDF output out of 51595 (max. 10000000) diff --git a/LateX/main.pdf b/LateX/main.pdf index 5c36311..7a56249 100644 Binary files a/LateX/main.pdf and b/LateX/main.pdf differ diff --git a/LateX/main.synctex.gz b/LateX/main.synctex.gz index fca2f9a..c8b9a8a 100644 Binary files a/LateX/main.synctex.gz and b/LateX/main.synctex.gz differ diff --git a/LateX/main.tex b/LateX/main.tex index cb48bc4..339973f 100644 --- a/LateX/main.tex +++ b/LateX/main.tex @@ -245,6 +245,7 @@ % ============================================ \input{step_01.tex} \input{step_02.tex} +\input{step_03.tex} % Weitere Kapitel folgen hier: % \input{step_02.tex} diff --git a/LateX/main.toc b/LateX/main.toc index 77223cb..3ddd723 100644 --- a/LateX/main.toc +++ b/LateX/main.toc @@ -18,4 +18,18 @@ \contentsline {subsubsection}{\numberline {2.5.3}Firewall aktivieren}{10}{subsubsection.2.5.3}% \contentsline {subsubsection}{\numberline {2.5.4}Konfiguration überprüfen}{11}{subsubsection.2.5.4}% \contentsline {subsection}{\numberline {2.6}Zusammenfassung}{11}{subsection.2.6}% +\contentsline {section}{\numberline {3}Docker-Images bauen und App deployen}{12}{section.3}% +\contentsline {subsection}{\numberline {3.1}Was ist Docker und warum nutzen wir es?}{12}{subsection.3.1}% +\contentsline {subsection}{\numberline {3.2}Die drei Dockerfiles im Detail}{12}{subsection.3.2}% +\contentsline {subsubsection}{\numberline {3.2.1}Backend-Dockerfile: \texttt {apps/api/Dockerfile}}{12}{subsubsection.3.2.1}% +\contentsline {subsubsection}{\numberline {3.2.2}Frontend-Dockerfile: \texttt {apps/web/Dockerfile}}{13}{subsubsection.3.2.2}% +\contentsline {subsubsection}{\numberline {3.2.3}Nginx-Konfiguration: \texttt {apps/web/nginx.conf}}{14}{subsubsection.3.2.3}% +\contentsline {subsection}{\numberline {3.3}Das Backend: Program.cs im Detail}{15}{subsection.3.3}% +\contentsline {subsection}{\numberline {3.4}Der API-Client: client.ts im Detail}{16}{subsection.3.4}% +\contentsline {subsection}{\numberline {3.5}Das Frontend: App.tsx im Detail}{16}{subsection.3.5}% +\contentsline {subsection}{\numberline {3.6}Images bauen}{17}{subsection.3.6}% +\contentsline {subsection}{\numberline {3.7}Images exportieren und auf den Server kopieren}{18}{subsection.3.7}% +\contentsline {subsection}{\numberline {3.8}Container auf dem Server starten}{18}{subsection.3.8}% +\contentsline {subsection}{\numberline {3.9}Aufgetretene Probleme und Lösungen}{19}{subsection.3.9}% +\contentsline {subsection}{\numberline {3.10}Zusammenfassung}{20}{subsection.3.10}% \providecommand \tocbasic@end@toc@file {}\tocbasic@end@toc@file diff --git a/LateX/step_03.tex b/LateX/step_03.tex new file mode 100644 index 0000000..eb63af3 --- /dev/null +++ b/LateX/step_03.tex @@ -0,0 +1,382 @@ +% ============================================ +% STEP 03: DOCKER, DEPLOYMENT & ERSTE LIVE-APP +% ============================================ + +\section{Docker-Images bauen und App deployen} +\label{sec:step03} + +In diesem Schritt bringen wir unsere Fitness-App vom lokalen Entwicklungsrechner auf den Server und machen sie weltweit erreichbar. Dafür nutzen wir \textbf{Docker} – eine Container-Plattform, die Anwendungen in standardisierten, isolierten Umgebungen verpackt und ausführt. + +\subsection{Was ist Docker und warum nutzen wir es?} + +Docker funktioniert wie ein \textbf{Versandkarton für Software}. Stell dir vor, du verschickst ein zerbrechliches Paket: Du packst es in einen genormten Karton, der überall auf der Welt gleich behandelt wird – egal ob in Deutschland, Japan oder Brasilien. Docker macht dasselbe mit Software: + +\begin{itemize} + \item \textbf{Image} = Der Bauplan des Kartons (inkl. Inhalt). Ein Image enthält das Betriebssystem, alle Abhängigkeiten und die Anwendung selbst. + \item \textbf{Container} = Der tatsächliche laufende Karton. Ein Container ist eine laufende Instanz eines Images. + \item \textbf{Volume} = Ein separater Speicherort, der den Container überlebt. Wie ein externer USB-Stick, den man an den Karton anschließt. +\end{itemize} + +\textbf{Konkret für unser Projekt:} +\begin{itemize} + \item \texttt{fitness-api:latest} – Image mit .NET 8 Backend + \item \texttt{fitness-web:latest} – Image mit React Frontend + Nginx + \item \texttt{fitness-data} – Volume für die SQLite-Datenbank (überlebt Container-Neustarts) +\end{itemize} + +\subsection{Die drei Dockerfiles im Detail} + +\subsubsection{Backend-Dockerfile: \texttt{apps/api/Dockerfile}} + +\begin{lstlisting}[language=Dockerfile, caption={Dockerfile für das .NET Backend}] +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY ["apps/api/Api.csproj", "apps/api/"] +RUN dotnet restore "apps/api/Api.csproj" +COPY . . +WORKDIR /src/apps/api +RUN dotnet publish "Api.csproj" -c Release -o /app/publish + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final +WORKDIR /app +COPY --from=build /app/publish . +EXPOSE 5000 +ENV ASPNETCORE_URLS=http://+:5000 +ENTRYPOINT ["dotnet", "Api.dll"] +\end{lstlisting} + +\textbf{Zeile für Zeile erklärt:} +\begin{enumerate} + \item \texttt{FROM ... AS build} – Startet mit dem .NET 8 SDK Image (enthält Compiler, Tools). Der Alias \texttt{build} erlaubt später darauf zuzugreifen. + \item \texttt{WORKDIR /src} – Setzt das Arbeitsverzeichnis im Container auf \texttt{/src}. + \item \texttt{COPY ["apps/api/Api.csproj", "apps/api/"]} – Kopiert NUR die Projektdatei. Dadurch cached Docker diesen Schritt: Solange sich \texttt{Api.csproj} nicht ändert, wird der Cache verwendet $\rightarrow$ schnellere Builds! + \item \texttt{RUN dotnet restore} – Lädt alle NuGet-Pakete herunter (Entity Framework, NSwag, Swagger usw.). + \item \texttt{COPY . .} – Kopiert den gesamten restlichen Quellcode. + \item \texttt{WORKDIR /src/apps/api} – Wechselt ins Backend-Verzeichnis. + \item \texttt{RUN dotnet publish} – Kompiliert die Anwendung im Release-Modus in den Ordner \texttt{/app/publish}. + \item \texttt{FROM ... AS final} – Startet ein NEUES, schlankeres Image (nur ASP.NET Runtime, kein SDK). Das spart Speicher! + \item \texttt{COPY --from=build ...} – Kopiert die kompilierte Anwendung aus dem Build-Image. + \item \texttt{EXPOSE 5000} – Dokumentiert, dass der Container auf Port 5000 lauscht. + \item \texttt{ENV ASPNETCORE\_URLS=http://+:5000} – Sagt .NET, es soll auf Port 5000 auf ALLEN Netzwerkschnittstellen lauschen. + \item \texttt{ENTRYPOINT ["dotnet", "Api.dll"]} – Startet die Anwendung beim Container-Start. +\end{enumerate} + +\subsubsection{Frontend-Dockerfile: \texttt{apps/web/Dockerfile}} + +\begin{lstlisting}[language=Dockerfile, caption={Dockerfile für das React Frontend}] +FROM node:22-alpine AS build +WORKDIR /app +COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ +COPY apps/web/package.json apps/web/ +RUN npm install -g pnpm && pnpm install --no-frozen-lockfile +COPY apps/web/ apps/web/ +WORKDIR /app/apps/web +RUN pnpm run build + +FROM nginx:stable-alpine +COPY --from=build /app/apps/web/dist /usr/share/nginx/html +COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +\end{lstlisting} + +\textbf{Zeile für Zeile erklärt:} +\begin{enumerate} + \item \texttt{FROM node:22-alpine} – Leichtgewichtiges Node.js 22 Image (Alpine Linux = nur $\sim$5 MB statt $\sim$180 MB bei Ubuntu). + \item \texttt{COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./} – Kopiert die Monorepo-Konfiguration aus dem Root. \texttt{pnpm-workspace.yaml} ist nötig, damit pnpm die Workspace-Struktur erkennt. + \item \texttt{COPY apps/web/package.json apps/web/} – Kopiert die Frontend-Paketliste an ihren Platz. + \item \texttt{RUN npm install -g pnpm \&\& pnpm install} – Installiert pnpm global und dann alle Abhängigkeiten. + \item \texttt{COPY apps/web/ apps/web/} – Kopiert den restlichen Frontend-Code. + \item \texttt{WORKDIR /app/apps/web} – Wechselt ins Frontend-Verzeichnis. + \item \texttt{RUN pnpm run build} – Baut das Frontend mit Vite (erzeugt \texttt{dist/}). + \item \texttt{FROM nginx:stable-alpine} – NEUES schlankes Image mit Nginx (Webserver). + \item \texttt{COPY --from=build ... /usr/share/nginx/html} – Kopiert den Build-Output in Nginx's Standard-Webverzeichnis. + \item \texttt{COPY apps/web/nginx.conf ...} – Unsere eigene Nginx-Konfiguration. + \item \texttt{EXPOSE 80} – HTTP-Port. + \item \texttt{CMD ["nginx", "-g", "daemon off;"]} – Startet Nginx im Vordergrund (Container bleibt am Leben). +\end{enumerate} + +\subsubsection{Nginx-Konfiguration: \texttt{apps/web/nginx.conf}} + +\begin{lstlisting}[language=Bash, caption={Nginx-Konfiguration mit Reverse Proxy}] +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/ { + proxy_pass http://fitness-api:5000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection keep-alive; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } +} +\end{lstlisting} + +\textbf{Erklärung:} Dies ist ein \textbf{Reverse Proxy}. Nginx nimmt alle Anfragen entgegen und entscheidet, wohin sie weitergeleitet werden: +\begin{itemize} + \item \texttt{location /} – Anfragen an die Hauptseite $\rightarrow$ liefert React-Dateien aus \texttt{/usr/share/nginx/html} + \item \texttt{location /api/} – Anfragen an \texttt{/api/*} $\rightarrow$ leitet sie an das Backend (\texttt{fitness-api:5000}) weiter + \item \texttt{try\_files \$uri \$uri/ /index.html} – Sorgt dafür, dass Reacts Client-Side-Routing funktioniert (z.B. \texttt{/workouts/123} wird an React weitergegeben, nicht als 404 beantwortet) +\end{itemize} + +\subsection{Das Backend: Program.cs im Detail} + +\begin{lstlisting}[language=CSharp, caption={Vollständige Program.cs}] +using Microsoft.EntityFrameworkCore; + +var builder = WebApplication.CreateBuilder(args); + +var dbPath = Path.Combine("/app/data", "fitness.db"); +builder.Services.AddDbContext(options => + options.UseSqlite($"Data Source={dbPath}")); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddOpenApiDocument(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +using (var scope = app.Services.CreateScope()) +{ + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); +} + +if (app.Environment.IsDevelopment()) +{ + app.UseOpenApi(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.MapPost("/api/workouts", async (Workout workout, AppDbContext db) => { ... }); +app.MapGet("/api/workouts", async (AppDbContext db) => ...); +app.MapGet("/api/workouts/{id:guid}", async (Guid id, AppDbContext db) => ...); +app.MapPut("/api/workouts/{id:guid}", async (Guid id, Workout input, AppDbContext db) => ...); +app.MapDelete("/api/workouts/{id:guid}", async (Guid id, AppDbContext db) => ...); + +app.MapGet("/", () => "H Fitness API läuft!"); +app.Run(); +\end{lstlisting} + +\textbf{Die NuGet-Pakete in der .csproj-Datei:} +\begin{itemize} + \item \texttt{Microsoft.EntityFrameworkCore.Sqlite} (8.0.14) – EF Core für SQLite + \item \texttt{Microsoft.EntityFrameworkCore.Design} (8.0.14) – EF Core Tools für Migrationen + \item \texttt{NSwag.AspNetCore} (14.1.0) – OpenAPI/Swagger-Generator + \item \texttt{Swashbuckle.AspNetCore} (6.6.2) – Swagger UI (die schöne Oberfläche) +\end{itemize} + +\textbf{Wichtige Details:} +\begin{itemize} + \item \texttt{Path.Combine("/app/data", "fitness.db")} – Im Docker-Container liegt die Datenbank im Volume-Ordner \texttt{/app/data}. Das stellt sicher, dass die Daten erhalten bleiben, auch wenn der Container gelöscht und neu erstellt wird. + \item \texttt{db.Database.EnsureCreated()} – Erstellt die Datenbank-Tabellen automatisch beim Start, falls sie noch nicht existieren. Erspart uns manuelle Migrationen im Produktivbetrieb. + \item Die CRUD-Endpunkte sind \textbf{Minimal API Endpoints} – .NET 8's leichtgewichtige Alternative zu Controllern. +\end{itemize} + +\subsection{Der API-Client: client.ts im Detail} + +\begin{lstlisting}[language=TypeScript, caption={Manueller API-Client}] +const API_BASE = ""; + +export interface Workout { + id?: string; + name: string; + date: string; + durationMinutes: number; + notes?: string; +} + +export const fitnessApi = { + async getWorkouts(): Promise { + const res = await fetch(`/api/workouts`); + return res.json(); + }, + + async createWorkout(workout: Workout): Promise { + const res = await fetch(`/api/workouts`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(workout), + }); + return res.json(); + }, + + // Weitere Methoden: getWorkout, updateWorkout, deleteWorkout... +}; +\end{lstlisting} + +\textbf{Erklärung:} +\begin{itemize} + \item \texttt{API\_BASE = ""} – Keine absolute URL! Stattdessen relative Pfade wie \texttt{/api/workouts}. Der Browser sendet die Anfrage dann an dieselbe Domain, auf der die Seite gehostet ist. Nginx leitet sie an das Backend weiter. + \item \texttt{interface Workout} – TypeScript-Interface für Typsicherheit. Stellt sicher, dass wir keine falschen Felder an die API senden. + \item \texttt{fetch()} – Native Browser-API für HTTP-Anfragen. Kein Axios, kein jQuery nötig! + \item Die Methoden geben direkt das geparste JSON zurück. +\end{itemize} + +\subsection{Das Frontend: App.tsx im Detail} + +\begin{lstlisting}[language=TypeScript, caption={Hauptkomponente mit Workout-Liste und Formular}] +function App() { + const [workouts, setWorkouts] = useState([]); + const [form, setForm] = useState({ ... }); + + const loadWorkouts = async () => { + const data = await fitnessApi.getWorkouts(); + setWorkouts(data); + }; + + useEffect(() => { loadWorkouts(); }, []); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + await fitnessApi.createWorkout({ ...form }); + loadWorkouts(); + }; + + return ( +
+ {/* Formular */} +
...
+ {/* Workout-Liste */} + {workouts.map(w => ( ... ))} +
+ ); +} +\end{lstlisting} + +\textbf{Erklärung:} +\begin{itemize} + \item \texttt{useState} – Reacts State-Management für die Workout-Liste und das Formular. + \item \texttt{useEffect} – Lädt die Workouts einmalig beim ersten Rendern der Komponente. + \item \texttt{handleSubmit} – Wird beim Absenden des Formulars aufgerufen. Verhindert den Standard-Seiten-Reload (\texttt{e.preventDefault()}), sendet das Workout an die API und lädt die Liste neu. + \item Tailwind CSS-Klassen wie \texttt{bg-gray-950}, \texttt{text-white}, \texttt{p-4} gestalten die App im Dark-Mode. +\end{itemize} + +\subsection{Images bauen} + +Am Anfang war das Frontend-Image fehlerhaft. Hier die drei wichtigsten Fixes: + +\textbf{Fehler 1: \texttt{pnpm-lock.yaml} nicht gefunden} +\begin{itemize} + \item Ursache: Dockerfile suchte in \texttt{apps/web/}, aber die Datei liegt im Root. + \item Lösung: \texttt{COPY pnpm-lock.yaml ./} (vom Root kopieren). +\end{itemize} + +\textbf{Fehler 2: \texttt{--frozen-lockfile} schlug fehl} +\begin{itemize} + \item Ursache: pnpm-Lockfile war nicht aktuell mit der Root-\texttt{package.json}. + \item Lösung: \texttt{--no-frozen-lockfile} verwenden, damit pnpm fehlende Pakete nachinstalliert. +\end{itemize} + +\textbf{Fehler 3: TypeScript Compiler (\texttt{tsc}) nicht gefunden} +\begin{itemize} + \item Ursache: \texttt{tsc -b} benötigt TypeScript als Abhängigkeit, die im Container fehlte. + \item Lösung: Build-Script von \texttt{"tsc -b \&\& vite build"} auf \texttt{"vite build"} geändert. Vite führt den TypeScript-Check beim Dev-Server durch – im Produktions-Build reicht die reine Vite-Kompilierung. +\end{itemize} + +\subsection{Images exportieren und auf den Server kopieren} + +\begin{lstlisting}[language=Bash, caption={Images exportieren und kopieren}] +# Images als tar-Datei speichern +docker save fitness-api:latest fitness-web:latest -o fitness-images.tar + +# Auf den Server kopieren (scp = Secure Copy über SSH) +scp fitness-images.tar testserver:/root/ + +# Auf dem Server importieren +ssh testserver +docker load -i /root/fitness-images.tar +docker images | grep fitness +\end{lstlisting} + +\textbf{Befehle erklärt:} +\begin{itemize} + \item \texttt{docker save} – Exportiert Docker-Images in eine portable tar-Datei. + \item \texttt{scp} – Secure Copy: Kopiert Dateien verschlüsselt über SSH. + \item \texttt{testserver:/root/} – Der Alias aus unserer \texttt{\textasciitilde/.ssh/config}. Die Datei landet im \texttt{/root/}-Verzeichnis des Servers. + \item \texttt{docker load -i} – Importiert Images aus einer tar-Datei in Docker. + \item \texttt{docker images} – Listet alle lokal verfügbaren Docker-Images auf. +\end{itemize} + +\subsection{Container auf dem Server starten} + +\begin{lstlisting}[language=Bash, caption={Docker-Netzwerk, Volume und Container anlegen}] +# Volume für die Datenbank (überlebt Container-Neustarts) +docker volume create fitness-data + +# Netzwerk (damit Backend und Frontend kommunizieren können) +docker network create fitness-net + +# Backend starten +docker run -d \ + --name fitness-api \ + --network fitness-net \ + -v fitness-data:/app/data \ + -p 5000:5000 \ + fitness-api:latest + +# Frontend starten +docker run -d \ + --name fitness-web \ + --network fitness-net \ + -p 80:80 \ + fitness-web:latest +\end{lstlisting} + +\textbf{Optionen erklärt:} +\begin{itemize} + \item \texttt{-d} – Detached Mode: Container läuft im Hintergrund (gibt die Konsole frei). + \item \texttt{--name fitness-api} – Gibt dem Container einen festen Namen (sonst vergibt Docker Zufallsnamen). + \item \texttt{--network fitness-net} – Bindet den Container in unser Docker-Netzwerk ein. Container im selben Netzwerk können sich über ihren Namen erreichen (z.B. \texttt{fitness-api}). + \item \texttt{-v fitness-data:/app/data} – Bindet das Volume \texttt{fitness-data} in den Container-Pfad \texttt{/app/data} ein. Alles, was im Container unter \texttt{/app/data} gespeichert wird, landet tatsächlich im Volume und überlebt. + \item \texttt{-p 5000:5000} – Port-Mapping: Leitet Port 5000 des Hosts (Server) an Port 5000 des Containers weiter. +\end{itemize} + +\subsection{Aufgetretene Probleme und Lösungen} + +\textbf{Problem 1: Nginx konnte Host "backend" nicht auflösen} +\begin{itemize} + \item Ursache: In \texttt{nginx.conf} stand \texttt{proxy\_pass http://backend:5000}, aber der Backend-Container heißt \texttt{fitness-api}. + \item Lösung: \texttt{backend} $\rightarrow$ \texttt{fitness-api} in \texttt{nginx.conf} ändern, Image neu bauen. +\end{itemize} + +\textbf{Problem 2: Frontend lud Assets mit Pfad \texttt{/app/...}} +\begin{itemize} + \item Ursache: In \texttt{vite.config.ts} war \texttt{base: "/app/"} für PWA-Zwecke gesetzt. + \item Lösung: Geändert auf \texttt{base: "/"}. +\end{itemize} + +\textbf{Problem 3: API-Anfragen gingen an lokale IP \texttt{192.168.178.189}} +\begin{itemize} + \item Ursache: Im Client stand \texttt{const API\_BASE = "http://192.168.178.189:5107"}. + \item Lösung: Geändert auf relative Pfade (\texttt{/api/workouts}), sodass Nginx die Anfragen per Reverse Proxy ans Backend weiterleitet. +\end{itemize} + +\textbf{Problem 4: Doppeltes \texttt{/api} im Pfad} +\begin{itemize} + \item Ursache: Client hatte \texttt{API\_BASE = "/api"} und Endpunkte begannen ebenfalls mit \texttt{/api}. + \item Ergebnis: Anfragen gingen an \texttt{/api/api/workouts}. + \item Lösung: API-Base auf leeren String gesetzt und Endpunkte mit \texttt{/api/} beginnen lassen. +\end{itemize} + +\subsection{Zusammenfassung} + +Nach diesem Schritt ist die Fitness-App produktiv auf dem VPS im Einsatz: + +\begin{itemize} + \item Die App ist unter \texttt{http://185.209.229.167} weltweit erreichbar + \item Workouts werden persistent in einer SQLite-Datenbank gespeichert + \item Docker-Volumes stellen sicher, dass Daten Container-Neustarts überleben + \item Das Backend läuft auf .NET 8, das Frontend auf Nginx + \item Ein Reverse Proxy (Nginx) leitet API-Anfragen intern an das Backend weiter +\end{itemize} \ No newline at end of file