history+outline: production deployment, nginx+systemd, build fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
932e570ade
commit
debaeb2b6f
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
.wsnav-inner
|
.wsnav-inner
|
||||||
{
|
{
|
||||||
width: 57em;
|
width: 65em;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0.45rem 1.5rem;
|
padding: 0.45rem 1.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="3840"
|
||||||
|
height="2160"
|
||||||
|
viewBox="0 0 3840 2160"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
|
||||||
|
sodipodi:docname="roject.svg"
|
||||||
|
xml:space="preserve"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#333333"
|
||||||
|
bordercolor="#404040"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#333333"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.20890503"
|
||||||
|
inkscape:cx="1969.7946"
|
||||||
|
inkscape:cy="1179.962"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g17" /><defs
|
||||||
|
id="defs2"><linearGradient
|
||||||
|
id="linearGradient12"
|
||||||
|
inkscape:collect="never"><stop
|
||||||
|
style="stop-color:#00bcf5;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop12" /><stop
|
||||||
|
style="stop-color:#296af5;stop-opacity:0.65098041;"
|
||||||
|
offset="0.3475728"
|
||||||
|
id="stop14" /><stop
|
||||||
|
style="stop-color:#7800f5;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop13" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient9"
|
||||||
|
inkscape:collect="never"><stop
|
||||||
|
style="stop-color:#ff26a7;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop9" /><stop
|
||||||
|
style="stop-color:#ff3426;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop10" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient7"
|
||||||
|
inkscape:collect="never"><stop
|
||||||
|
style="stop-color:#2636ff;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop7" /><stop
|
||||||
|
style="stop-color:#29006f;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop8" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient5"
|
||||||
|
inkscape:collect="never"><stop
|
||||||
|
style="stop-color:#beffc5;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop5" /><stop
|
||||||
|
style="stop-color:#26c5ff;stop-opacity:1;"
|
||||||
|
offset="0.53096259"
|
||||||
|
id="stop11" /><stop
|
||||||
|
style="stop-color:#0051eb;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop6" /></linearGradient><clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath7940"><rect
|
||||||
|
style="fill:#333333;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||||
|
id="rect7942"
|
||||||
|
width="1440"
|
||||||
|
height="810"
|
||||||
|
x="0"
|
||||||
|
y="0" /></clipPath><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient5"
|
||||||
|
id="linearGradient6"
|
||||||
|
x1="2094.8511"
|
||||||
|
y1="824.97723"
|
||||||
|
x2="2175.8127"
|
||||||
|
y2="1376.4214"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.69112949,0,0,0.69112949,529.92586,259.08767)" /><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient7"
|
||||||
|
id="linearGradient8"
|
||||||
|
x1="2107.4609"
|
||||||
|
y1="790.479"
|
||||||
|
x2="2563.2524"
|
||||||
|
y2="1203.4646"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.69112949,0,0,0.69112949,529.92586,259.08767)" /><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient9"
|
||||||
|
id="linearGradient10"
|
||||||
|
x1="1966.5072"
|
||||||
|
y1="1228.4695"
|
||||||
|
x2="1874.9818"
|
||||||
|
y2="528.3299"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.69112949,0,0,0.69112949,529.92586,259.08767)" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter10"
|
||||||
|
x="-0.32921062"
|
||||||
|
y="-0.82302656"
|
||||||
|
width="1.6584212"
|
||||||
|
height="2.6460531"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="329.39922"
|
||||||
|
id="feGaussianBlur10" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter11"
|
||||||
|
x="-0.017136762"
|
||||||
|
y="-0.0835418"
|
||||||
|
width="1.0342735"
|
||||||
|
height="1.1670836"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="11.456677"
|
||||||
|
id="feGaussianBlur11" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter12"
|
||||||
|
x="-0.1102434"
|
||||||
|
y="-0.51284629"
|
||||||
|
width="1.2203709"
|
||||||
|
height="2.0256926"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="61.763442"
|
||||||
|
id="feGaussianBlur12" /></filter><radialGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient12"
|
||||||
|
id="radialGradient13"
|
||||||
|
cx="1008.5775"
|
||||||
|
cy="617.15338"
|
||||||
|
fx="1008.5775"
|
||||||
|
fy="617.15338"
|
||||||
|
r="749.229"
|
||||||
|
gradientTransform="matrix(3.747293,0,0,0.21523126,-1392.2413,742.81336)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter16"
|
||||||
|
x="-0.32921062"
|
||||||
|
y="-0.82302656"
|
||||||
|
width="1.6584212"
|
||||||
|
height="2.6460531"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="329.39922"
|
||||||
|
id="feGaussianBlur16" /></filter></defs><g
|
||||||
|
inkscape:label="Content"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="fill:#ffffff;fill-opacity:1"><rect
|
||||||
|
style="fill:#000000;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1"
|
||||||
|
id="rect1"
|
||||||
|
width="3840"
|
||||||
|
height="2160"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
ry="7.9545546" /><g
|
||||||
|
id="g17"
|
||||||
|
transform="translate(38.057367,61.315595)"><ellipse
|
||||||
|
style="opacity:1;mix-blend-mode:normal;fill:#0a0066;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter10)"
|
||||||
|
id="path10"
|
||||||
|
cx="1834.5552"
|
||||||
|
cy="1143.0068"
|
||||||
|
rx="1200.6875"
|
||||||
|
ry="480.27499"
|
||||||
|
transform="matrix(0.69112949,0,0,0.69112949,566.63996,353.0411)" /><ellipse
|
||||||
|
style="opacity:1;mix-blend-mode:screen;fill:#005d78;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter16)"
|
||||||
|
id="ellipse16"
|
||||||
|
cx="1834.5552"
|
||||||
|
cy="1143.0068"
|
||||||
|
rx="1200.6875"
|
||||||
|
ry="480.27499"
|
||||||
|
transform="matrix(0.59256734,0,0,0.55929452,795.32629,304.17302)" /><g
|
||||||
|
id="g12"
|
||||||
|
style="opacity:1;filter:url(#filter12)"
|
||||||
|
transform="matrix(0.69112949,0,0,0.69112949,491.63996,291.80532)"><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:457.124px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:11.4281;stroke-linecap:round;stroke-linejoin:round;filter:url(#filter11)"
|
||||||
|
x="1423.6522"
|
||||||
|
y="1196.2206"
|
||||||
|
id="text11"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,-55,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan11"
|
||||||
|
x="1423.6522"
|
||||||
|
y="1196.2206"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:#000000;fill-opacity:1;stroke-width:11.4281">ROJECT</tspan></text><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:457.124px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:11.4281;stroke-linecap:round;stroke-linejoin:round;filter:url(#filter11)"
|
||||||
|
x="1423.6522"
|
||||||
|
y="1196.2206"
|
||||||
|
id="text12"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,-4.2276373,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan12"
|
||||||
|
x="1423.6522"
|
||||||
|
y="1196.2206"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:#000000;fill-opacity:1;stroke-width:11.4281">ROJECT</tspan></text></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:315.932px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:1;fill:url(#linearGradient10);stroke-width:7.8983;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
x="1484.9595"
|
||||||
|
y="1087.6149"
|
||||||
|
id="text2"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,0,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2"
|
||||||
|
x="1484.9595"
|
||||||
|
y="1087.6149"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:url(#linearGradient10);fill-opacity:1;stroke-width:7.8983">ROJECT</tspan></text><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:315.932px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:1;fill:url(#linearGradient8);stroke-width:7.8983;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
x="1498.2201"
|
||||||
|
y="1087.0574"
|
||||||
|
id="text1"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,0,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan1"
|
||||||
|
x="1498.2201"
|
||||||
|
y="1087.0574"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:url(#linearGradient8);fill-opacity:1;stroke-width:7.8983">ROJECT</tspan></text><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:315.932px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:1;fill:url(#linearGradient6);stroke-width:7.8983;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
x="1513.8538"
|
||||||
|
y="1085.8309"
|
||||||
|
id="text3"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,0,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3"
|
||||||
|
x="1513.8538"
|
||||||
|
y="1085.8309"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:url(#linearGradient6);fill-opacity:1;stroke-width:7.8983">ROJECT</tspan></text><g
|
||||||
|
id="g16"
|
||||||
|
transform="translate(9.5737285,-66.875187)"><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:67.5143px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;opacity:1;fill:#0056f5;fill-opacity:1;stroke:none;stroke-width:16.8787;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="1480.9904"
|
||||||
|
y="1349.8354"
|
||||||
|
id="text15"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan15"
|
||||||
|
x="1480.9904"
|
||||||
|
y="1349.8354"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Kode Mono';-inkscape-font-specification:'Kode Mono Bold';fill:#0056f5;fill-opacity:1;stroke-width:16.8787">COMPUTER! ZOOM IN!</tspan></text><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:67.5143px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;opacity:1;fill:#00f526;fill-opacity:1;stroke:none;stroke-width:16.8787;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="1480.9904"
|
||||||
|
y="1342.0404"
|
||||||
|
id="text16"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan16"
|
||||||
|
x="1480.9904"
|
||||||
|
y="1342.0404"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Kode Mono';-inkscape-font-specification:'Kode Mono Bold';fill:#00f526;fill-opacity:1;stroke-width:16.8787">COMPUTER! ZOOM IN!</tspan></text><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:67.5143px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;opacity:1;fill:#00cbf5;fill-opacity:1;stroke:none;stroke-width:16.8787;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="1480.9904"
|
||||||
|
y="1344.5404"
|
||||||
|
id="text14"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan14"
|
||||||
|
x="1480.9904"
|
||||||
|
y="1344.5404"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Kode Mono';-inkscape-font-specification:'Kode Mono Bold';fill:#00cbf5;fill-opacity:1;stroke-width:16.8787">COMPUTER! ZOOM IN!</tspan></text></g></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -35,7 +35,7 @@ body
|
||||||
|
|
||||||
.page
|
.page
|
||||||
{
|
{
|
||||||
max-width: 40em;
|
max-width: 50em;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@
|
||||||
<p class="date">Sunday, 12 July 2026</p>
|
<p class="date">Sunday, 12 July 2026</p>
|
||||||
<h1>Roject — Session Summary</h1>
|
<h1>Roject — Session Summary</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
Full project directory restructure — source/, build/app/, build/data/,
|
Full project directory restructure and first production deployment —
|
||||||
source/pages/ with a copy-pages build step.
|
source/, build/app/, build/data/, source/pages/; Roject live at
|
||||||
|
roject.rokojori.com via nginx + systemd on Server A.
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -64,6 +65,36 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>First production deployment — roject.rokojori.com</h3>
|
||||||
|
<p>
|
||||||
|
Roject deployed to Server A and served publicly at
|
||||||
|
<code>https://roject.rokojori.com</code>. Stack: Node.js 20,
|
||||||
|
<code>ts-node</code> running <code>source/server/index.ts</code> managed
|
||||||
|
by a systemd service, nginx as TLS-terminating reverse proxy with a
|
||||||
|
Let's Encrypt certificate, port 3000 kept closed at the IONOS firewall.
|
||||||
|
</p>
|
||||||
|
<p style="margin-top:0.75rem">
|
||||||
|
Several build gaps were discovered and fixed during the deploy:
|
||||||
|
component CSS files were never in source control (moved to
|
||||||
|
<code>source/components/</code>); vendor libraries were in the gitignored
|
||||||
|
<code>build/app/vendor/</code> (moved to <code>source/vendor/</code>);
|
||||||
|
<code>mkdirSync</code> calls in the server lacked <code>{ recursive: true }</code>
|
||||||
|
causing a crash on a fresh clone; and the library-ts browser
|
||||||
|
<code>tsconfig.roject.json</code> still referenced the old
|
||||||
|
<code>public/</code> outDir instead of <code>build/app/</code>.
|
||||||
|
The copy script was also expanded to copy all non-TS static assets
|
||||||
|
(CSS, SVG, etc.) from all frontend source directories.
|
||||||
|
</p>
|
||||||
|
<div class="tags">
|
||||||
|
<span class="tag">roject.rokojori.com</span>
|
||||||
|
<span class="tag">systemd</span>
|
||||||
|
<span class="tag">nginx</span>
|
||||||
|
<span class="tag">Let's Encrypt</span>
|
||||||
|
<span class="tag">Node.js 20</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|
@ -91,6 +122,30 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="decision">
|
||||||
|
<strong>systemd over PM2</strong>
|
||||||
|
<p>
|
||||||
|
systemd is already present on every Linux server and integrates with
|
||||||
|
<code>journalctl</code> for log management. PM2 is easier for day-to-day
|
||||||
|
Node.js process management but adds an extra global dependency. For a
|
||||||
|
single-service deployment systemd is sufficient and keeps the server
|
||||||
|
dependency surface minimal.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="decision">
|
||||||
|
<strong>Static assets belong in source/, not build/</strong>
|
||||||
|
<p>
|
||||||
|
CSS files, vendor libraries, and SVGs were historically placed directly
|
||||||
|
in <code>public/</code> (gitignored) and never tracked. A fresh server
|
||||||
|
clone exposes this immediately — nothing in <code>build/</code> exists
|
||||||
|
until the build runs, and the build can only copy what it finds in
|
||||||
|
<code>source/</code>. Moving all static assets to <code>source/</code>
|
||||||
|
makes them first-class source files and ensures a clean deploy from any
|
||||||
|
fresh clone.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="decision">
|
<div class="decision">
|
||||||
<strong>Plain Node.js copy script, no npm dependency</strong>
|
<strong>Plain Node.js copy script, no npm dependency</strong>
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -121,7 +176,14 @@
|
||||||
<code>source/library-ts/browser/tsconfig.roject.json</code> — outDir updated<br>
|
<code>source/library-ts/browser/tsconfig.roject.json</code> — outDir updated<br>
|
||||||
<code>package.json</code> — main and build script updated<br>
|
<code>package.json</code> — main and build script updated<br>
|
||||||
<code>.gitignore</code> — replaced data/, storage/, public/ with build/<br>
|
<code>.gitignore</code> — replaced data/, storage/, public/ with build/<br>
|
||||||
<code>source/server/</code> — all __dirname paths and library-ts imports updated
|
<code>source/server/</code> — all __dirname paths and library-ts imports updated<br>
|
||||||
|
<code>source/components/*.css</code> — moved from build/app/ into source control<br>
|
||||||
|
<code>source/vendor/</code> — vendor libs moved from build/app/ into source control<br>
|
||||||
|
<code>source/library-ts/browser/tsconfig.roject.json</code> — outDir fixed in submodule, committed and pushed<br>
|
||||||
|
<code>scripts/copy-pages.js</code> — expanded to copy all non-TS static assets from all frontend source dirs<br>
|
||||||
|
<code>source/server/db.ts</code>, <code>storage.ts</code> — mkdirSync updated to use <code>{ recursive: true }</code><br>
|
||||||
|
<code>/etc/systemd/system/roject.service</code> — new on Server A<br>
|
||||||
|
<code>/etc/nginx/sites-available/roject</code> — new on Server A
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3><a href="2026/07-July/12-Sunday/index.html">Sunday, 12 July 2026</a></h3>
|
<h3><a href="2026/07-July/12-Sunday/index.html">Sunday, 12 July 2026</a></h3>
|
||||||
<p>Full directory restructure — source/, build/app/, build/data/db/, build/data/storage/, source/pages/ with copy-pages build step.</p>
|
<p>Full directory restructure and first production deployment — source/, build/, source/pages/; Roject live at roject.rokojori.com via nginx + systemd.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
||||||
<div style="width:100%;height:260px;overflow:hidden;background:#000;margin-bottom:2rem;border-radius:8px;">
|
<div style="width:100%;height:400px;overflow:hidden;background:#000;margin-bottom:2rem;border-radius:8px;">
|
||||||
<img src="../../src/rojos/roject.svg" alt="Rojects"
|
<img src="../_assets_/roject.svg" alt="Rojects"
|
||||||
style="width:100%;height:100%;object-fit:cover;object-position:center;">
|
style="width:100%;height:100%;object-fit:cover;object-position:center;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -71,6 +71,14 @@
|
||||||
A reusable <code><confirm-dialog></code> component replaces browser
|
A reusable <code><confirm-dialog></code> component replaces browser
|
||||||
<code>confirm()</code> for destructive actions (currently project deletion).
|
<code>confirm()</code> for destructive actions (currently project deletion).
|
||||||
</p>
|
</p>
|
||||||
|
<p style="margin-top:0.75rem">
|
||||||
|
The app is deployed and publicly accessible at
|
||||||
|
<code>https://roject.rokojori.com</code> on Server A. nginx handles TLS
|
||||||
|
termination and reverse-proxies to the Node.js process managed by a
|
||||||
|
systemd service. Deployment is currently manual
|
||||||
|
(<code>git pull && npm run build && systemctl restart roject</code>);
|
||||||
|
CI automation is the remaining open item.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
@ -79,23 +87,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>1 — Production server + CI</h3>
|
<h3>1 — CI pipeline (server is live)</h3>
|
||||||
<p>
|
<p>
|
||||||
Deploy the Node.js/Express app to a publicly reachable server and wire up a
|
The server is deployed and running at <code>https://roject.rokojori.com</code>
|
||||||
CI pipeline that builds, tests, and deploys automatically on push. The app's
|
(nginx + systemd + Let's Encrypt on Server A). What remains is automating the
|
||||||
file-based storage and no-database design make it straightforward to host on
|
deploy on push: a Gitea Actions runner installed on Server A registers with the
|
||||||
any VPS: nginx as reverse proxy, PM2 or a systemd service for the Node.js
|
repo on <code>community.rokojori.com</code> and runs a workflow that pulls,
|
||||||
process. CI can run as a self-hosted Gitea/Forgejo Actions runner (matching
|
builds, and restarts the service on every push to main. This is the last piece
|
||||||
the existing self-hosted git remote) or mirror to GitHub Actions. This is
|
that turns the manual deploy into a proper CI/CD pipeline.
|
||||||
infrastructure work rather than feature work — it should be done once and early
|
|
||||||
because everything else ships faster and more safely once a real deployment
|
|
||||||
target and regression-catching pipeline exist.
|
|
||||||
</p>
|
</p>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span class="tag">nginx</span>
|
<span class="tag">Gitea Actions runner</span>
|
||||||
<span class="tag">PM2 / systemd</span>
|
<span class="tag">act_runner</span>
|
||||||
<span class="tag">Gitea Actions</span>
|
<span class="tag">auto-deploy on push</span>
|
||||||
<span class="tag">VPS</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue