first deploy

This commit is contained in:
victor 2025-06-03 17:02:01 +02:00
parent 3021eea6b9
commit d1c76eb767
19 changed files with 512 additions and 626 deletions

View File

@ -36,7 +36,7 @@ RUN git clone https://github.com/novnc/noVNC.git /opt/noVNC
# Create VNC directory and set password # Create VNC directory and set password
RUN mkdir -p /root/.vnc && \ RUN mkdir -p /root/.vnc && \
echo "password" | vncpasswd -f > /root/.vnc/passwd && \ echo "1312" | vncpasswd -f > /root/.vnc/passwd && \
chmod 600 /root/.vnc/passwd chmod 600 /root/.vnc/passwd
RUN rm /opt/noVNC/*.html RUN rm /opt/noVNC/*.html

View File

@ -13,7 +13,7 @@ RUN apk update && \
chmod 700 /entrypoint.sh && \ chmod 700 /entrypoint.sh && \
touch /etc/.firstrun && \ touch /etc/.firstrun && \
ln -s "/usr/share/zoneinfo/$TZ" /etc/localtime && \ ln -s "/usr/share/zoneinfo/$TZ" /etc/localtime && \
echo $TZ > /etc/timezone echo $TZ > /etc/timezone
RUN rm -f /bin/ping /bin/ping6 /usr/bin/wget /usr/bin/curl \ RUN rm -f /bin/ping /bin/ping6 /usr/bin/wget /usr/bin/curl \
/usr/bin/nc /usr/bin/telnet /sbin/ip /bin/netstat /usr/bin/nc /usr/bin/telnet /sbin/ip /bin/netstat

BIN
minishell/bin/minishell Executable file

Binary file not shown.

BIN
minishell/libft/libft.a Normal file

Binary file not shown.

View File

@ -1,22 +0,0 @@
#!/bin/bash
# reset server_website directory
rm -rf server_website
ssh server rm -rf /home/victor/website
mkdir -p server_website/{minishell,vvsite}
# Build server binary
cd vvsite && cargo leptos build --release
cp -r target/release/vvsite Dockerfile Cargo.toml target/site ../server_website/vvsite
# Build minishell files
cd ../minishell
make -C minishell_src
cp -r ./minishell_src/bin ./Dockerfile ./entrypoint.sh ../server_website/minishell
# Copy to server
cd ..
cp -r ./nginx ./compose.yaml server_website
scp -r server_website server:/home/victor/website

View File

@ -3,20 +3,8 @@
set -e set -e
# Compile the minishell project # Compile the minishell project
make -C minishell/minishell_src make -C minishell
make -C miniRT
# rebuild website
cd vvsite && cargo leptos build
cd ..
# stop containers if running
docker compose down -v
# remove volumes
docker volume prune -f
docker rmi website-website website-minishell website-nginx -f
# start the containers # start the containers
docker compose up docker compose up --build

View File

@ -36,11 +36,23 @@ http {
listen 443 ssl; listen 443 ssl;
server_name localhost; server_name localhost;
root /var/www/html;
ssl_certificate /certs/localhost.pem; ssl_certificate /certs/localhost.pem;
ssl_certificate_key /certs/localhost-key.pem; ssl_certificate_key /certs/localhost-key.pem;
# Apply connection limits # Apply connection limits
limit_conn conn_limit_per_ip 20; limit_conn conn_limit_per_ip 2;
location / {
root /var/www/html;
index index.html index.htm;
}
location /minirt/password {
add_header Content-Type application/json;
try_files /json/password.json =404;
}
location /minishell/vnc { location /minishell/vnc {
proxy_pass http://minishell:8006/; proxy_pass http://minishell:8006/;
@ -53,6 +65,25 @@ http {
} }
location /minirt/vnc { location /minirt/vnc {
index vnc.html;
try_files $uri $uri/ /vnc.html;
# Ensure proper MIME types for JavaScript modules
location ~* \.js$ {
add_header Content-Type "application/javascript" always;
expires 1h;
}
location ~* \.css$ {
add_header Content-Type "text/css" always;
expires 1h;
}
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1d;
add_header Cache-Control "public, immutable";
}
}
location /vnc {
alias /opt/noVNC/; alias /opt/noVNC/;
index vnc.html; index vnc.html;
try_files $uri $uri/ /vnc.html; try_files $uri $uri/ /vnc.html;
@ -87,9 +118,5 @@ http {
proxy_buffering off; proxy_buffering off;
} }
location / {
root /var/www/html;
index index.html index.htm;
}
} }
} }

3
nginx/site/config.js Normal file
View File

@ -0,0 +1,3 @@
export const config = {
BASE_URL: 'https://localhost:5443/'
};

39
nginx/site/css/minirt.css Normal file
View File

@ -0,0 +1,39 @@
.minirt-canvas-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(32, 32, 32, 0.5);
}
#top_bar {
background-color: #6e84a3;
color: white;
font: bold 12px Helvetica;
padding: 6px 5px 4px 5px;
border-bottom: 1px outset;
}
#status {
text-align: center;
}
#sendCtrlAltDelButton {
position: fixed;
top: 0px;
right: 0px;
border: 1px outset;
padding: 5px 5px 4px 5px;
cursor: pointer;
}
#screen {
flex: 1; /* fill remaining space */
overflow: hidden;
}

View File

@ -1,113 +1,5 @@
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
/* /*
! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com ! tailwindcss v3.4.0 | MIT License | https://tailwindcss.com
*/ */
/* /*
@ -319,8 +211,6 @@ textarea {
/* 1 */ /* 1 */
line-height: inherit; line-height: inherit;
/* 1 */ /* 1 */
letter-spacing: inherit;
/* 1 */
color: inherit; color: inherit;
/* 1 */ /* 1 */
margin: 0; margin: 0;
@ -344,9 +234,9 @@ select {
*/ */
button, button,
input:where([type='button']), [type='button'],
input:where([type='reset']), [type='reset'],
input:where([type='submit']) { [type='submit'] {
-webkit-appearance: button; -webkit-appearance: button;
/* 1 */ /* 1 */
background-color: transparent; background-color: transparent;
@ -550,10 +440,110 @@ video {
/* Make elements with the HTML hidden attribute stay hidden by default */ /* Make elements with the HTML hidden attribute stay hidden by default */
[hidden]:where(:not([hidden="until-found"])) { [hidden] {
display: none; display: none;
} }
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
.fixed { .fixed {
position: fixed; position: fixed;
} }
@ -566,53 +556,12 @@ video {
position: relative; position: relative;
} }
.inset-0 { .right-\[16px\] {
inset: 0px; right: 16px;
} }
.z-10 { .top-\[16px\] {
z-index: 10; top: 16px;
}
.z-20 {
z-index: 20;
}
.m-24 {
margin: 6rem;
}
.m-auto {
margin: auto;
}
.m-60 {
margin: 15rem;
}
.mx-24 {
margin-left: 6rem;
margin-right: 6rem;
}
.mx-60 {
margin-left: 15rem;
margin-right: 15rem;
}
.mx-\[40\%\] {
margin-left: 40%;
margin-right: 40%;
}
.mx-\[20\%\] {
margin-left: 20%;
margin-right: 20%;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
} }
.mb-4 { .mb-4 {
@ -623,22 +572,6 @@ video {
margin-left: 1rem; margin-left: 1rem;
} }
.ml-2 {
margin-left: 0.5rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.-mt-4 {
margin-top: -1rem;
}
.mr-2 {
margin-right: 0.5rem;
}
.ml-\[10\%\] { .ml-\[10\%\] {
margin-left: 10%; margin-left: 10%;
} }
@ -647,58 +580,34 @@ video {
margin-right: auto; margin-right: auto;
} }
.box-border {
box-sizing: border-box;
}
.flex { .flex {
display: flex; display: flex;
} }
.h-32 { .hidden {
height: 8rem; display: none;
} }
.h-48 { .h-48 {
height: 12rem; height: 12rem;
} }
.h-\[8vh\] { .h-\[20dvh\] {
height: 8vh; height: 20dvh;
} }
.h-full { .h-\[5dvh\] {
height: 100%; height: 5dvh;
}
.h-\[600px\] {
height: 600px;
}
.h-\[20\%\] {
height: 20%;
}
.h-\[10\%\] {
height: 10%;
}
.h-\[8\%\] {
height: 8%;
}
.h-5 {
height: 1.25rem;
}
.min-h-\[8vh\] {
min-height: 8vh;
} }
.min-h-\[600px\] { .min-h-\[600px\] {
min-height: 600px; min-height: 600px;
} }
.w-\[60\%\] {
width: 60%;
}
.w-full { .w-full {
width: 100%; width: 100%;
} }
@ -707,58 +616,23 @@ video {
width: 100vw; width: 100vw;
} }
.w-\[20\%\] { .w-fit {
width: 20%; width: -moz-fit-content;
} width: fit-content;
.w-\[10\%\] {
width: 10%;
}
.w-\[800px\] {
width: 800px;
}
.w-\[50\%\] {
width: 50%;
}
.w-\[60\%\] {
width: 60%;
}
.w-5 {
width: 1.25rem;
}
.min-w-\[800px\] {
min-width: 800px;
}
.max-w-prose {
max-width: 65ch;
} }
.max-w-\[800px\] { .max-w-\[800px\] {
max-width: 800px; max-width: 800px;
} }
.max-w-4xl { .max-w-prose {
max-width: 56rem; max-width: 65ch;
}
.max-w-6xl {
max-width: 72rem;
} }
.grow { .grow {
flex-grow: 1; flex-grow: 1;
} }
.transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.list-inside { .list-inside {
list-style-position: inside; list-style-position: inside;
} }
@ -791,14 +665,6 @@ video {
gap: 1rem; gap: 1rem;
} }
.gap-6 {
gap: 1.5rem;
}
.gap-2 {
gap: 0.5rem;
}
.gap-y-6 { .gap-y-6 {
row-gap: 1.5rem; row-gap: 1.5rem;
} }
@ -809,138 +675,35 @@ video {
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
} }
.space-y-4 > :not([hidden]) ~ :not([hidden]) { .place-self-start {
--tw-space-y-reverse: 0; place-self: start;
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
} }
.overflow-hidden { .place-self-end {
overflow: hidden; place-self: end;
} }
.rounded-md { .rounded-md {
border-radius: 0.375rem; border-radius: 0.375rem;
} }
.rounded {
border-radius: 0.25rem;
}
.rounded-lg {
border-radius: 0.5rem;
}
.rounded-t-xl {
border-top-left-radius: 0.75rem;
border-top-right-radius: 0.75rem;
}
.border { .border {
border-width: 1px; border-width: 1px;
} }
.border-b-2 {
border-bottom-width: 2px;
}
.border-l-4 {
border-left-width: 4px;
}
.border-blue-400 {
--tw-border-opacity: 1;
border-color: rgb(96 165 250 / var(--tw-border-opacity, 1));
}
.border-gray-300 {
--tw-border-opacity: 1;
border-color: rgb(209 213 219 / var(--tw-border-opacity, 1));
}
.border-gray-600 {
--tw-border-opacity: 1;
border-color: rgb(75 85 99 / var(--tw-border-opacity, 1));
}
.bg-gray-800 { .bg-gray-800 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1)); background-color: rgb(31 41 55 / var(--tw-bg-opacity));
} }
.bg-neutral-100\/50 { .bg-neutral-100\/50 {
background-color: rgb(245 245 245 / 0.5); background-color: rgb(245 245 245 / 0.5);
} }
.bg-neutral-100 {
--tw-bg-opacity: 1;
background-color: rgb(245 245 245 / var(--tw-bg-opacity, 1));
}
.bg-neutral-300 {
--tw-bg-opacity: 1;
background-color: rgb(212 212 212 / var(--tw-bg-opacity, 1));
}
.bg-neutral-100\/80 {
background-color: rgb(245 245 245 / 0.8);
}
.bg-neutral-100\/70 {
background-color: rgb(245 245 245 / 0.7);
}
.bg-neutral-200\/70 { .bg-neutral-200\/70 {
background-color: rgb(229 229 229 / 0.7); background-color: rgb(229 229 229 / 0.7);
} }
.bg-black\/10 {
background-color: rgb(0 0 0 / 0.1);
}
.bg-gray-50 {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));
}
.bg-gradient-to-br {
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
}
.bg-gradient-to-r {
background-image: linear-gradient(to right, var(--tw-gradient-stops));
}
.from-gray-800 {
--tw-gradient-from: #1f2937 var(--tw-gradient-from-position);
--tw-gradient-to: rgb(31 41 55 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-green-400 {
--tw-gradient-from: #4ade80 var(--tw-gradient-from-position);
--tw-gradient-to: rgb(74 222 128 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-neutral-100 {
--tw-gradient-from: #f5f5f5 var(--tw-gradient-from-position);
--tw-gradient-to: rgb(245 245 245 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-gray-900 {
--tw-gradient-to: #111827 var(--tw-gradient-to-position);
}
.to-green-500 {
--tw-gradient-to: #22c55e var(--tw-gradient-to-position);
}
.to-neutral-200\/70 {
--tw-gradient-to: rgb(229 229 229 / 0.7) var(--tw-gradient-to-position);
}
.p-1 { .p-1 {
padding: 0.25rem; padding: 0.25rem;
} }
@ -953,13 +716,8 @@ video {
padding: 2rem; padding: 2rem;
} }
.p-6 { .p-2 {
padding: 1.5rem; padding: 0.5rem;
}
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;
} }
.px-16 { .px-16 {
@ -967,13 +725,9 @@ video {
padding-right: 4rem; padding-right: 4rem;
} }
.px-1 { .py-8 {
padding-left: 0.25rem; padding-top: 2rem;
padding-right: 0.25rem; padding-bottom: 2rem;
}
.pb-2 {
padding-bottom: 0.5rem;
} }
.text-center { .text-center {
@ -990,171 +744,48 @@ video {
line-height: 1; line-height: 1;
} }
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.font-bold { .font-bold {
font-weight: 700; font-weight: 700;
} }
.font-semibold {
font-weight: 600;
}
.leading-relaxed { .leading-relaxed {
line-height: 1.625; line-height: 1.625;
} }
.text-red-400 {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity, 1));
}
.text-\[\#FFF6DA\] { .text-\[\#FFF6DA\] {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 246 218 / var(--tw-text-opacity, 1)); color: rgb(255 246 218 / var(--tw-text-opacity));
}
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
}
.text-gray-800 {
--tw-text-opacity: 1;
color: rgb(31 41 55 / var(--tw-text-opacity, 1));
}
.text-purple-400 {
--tw-text-opacity: 1;
color: rgb(192 132 252 / var(--tw-text-opacity, 1));
}
.text-purple-200 {
--tw-text-opacity: 1;
color: rgb(233 213 255 / var(--tw-text-opacity, 1));
}
.text-blue-600 {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
} }
.text-blue-700 { .text-blue-700 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity, 1)); color: rgb(29 78 216 / var(--tw-text-opacity));
}
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity));
}
.text-gray-800 {
--tw-text-opacity: 1;
color: rgb(31 41 55 / var(--tw-text-opacity));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.underline { .underline {
text-decoration-line: underline; text-decoration-line: underline;
} }
.shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.shadow-lg {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.shadow-xl {
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.drop-shadow-lg {
--tw-drop-shadow: drop-shadow(0 10px 8px rgb(0 0 0 / 0.04)) drop-shadow(0 4px 3px rgb(0 0 0 / 0.1));
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-colors {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-150 {
transition-duration: 150ms;
}
.duration-200 {
transition-duration: 200ms;
}
.duration-300 {
transition-duration: 300ms;
}
.hover\:scale-105:hover {
--tw-scale-x: 1.05;
--tw-scale-y: 1.05;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:transform:hover {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:bg-\[\#aa9E73ff\]:hover { .hover\:bg-\[\#aa9E73ff\]:hover {
background-color: #aa9E73ff; background-color: #aa9E73ff;
} }
.hover\:bg-blue-50:hover {
--tw-bg-opacity: 1;
background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));
}
.hover\:from-green-500:hover {
--tw-gradient-from: #22c55e var(--tw-gradient-from-position);
--tw-gradient-to: rgb(34 197 94 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.hover\:to-green-600:hover {
--tw-gradient-to: #16a34a var(--tw-gradient-to-position);
}
.hover\:text-blue-800:hover {
--tw-text-opacity: 1;
color: rgb(30 64 175 / var(--tw-text-opacity, 1));
}
.hover\:text-gray-900:hover {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity, 1));
}
.hover\:text-blue-900:hover { .hover\:text-blue-900:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(30 58 138 / var(--tw-text-opacity, 1)); color: rgb(30 58 138 / var(--tw-text-opacity));
}
.hover\:shadow-lg:hover {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.hover\:shadow-xl:hover {
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }

View File

@ -5,34 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>MiniRT Preview</title> <title>MiniRT Preview</title>
<link href="/css/style.css" rel="stylesheet"> <link href="/css/style.css" rel="stylesheet">
<link href="/css/minirt.css" rel="stylesheet">
<link href="/css/tailwind.css" rel="stylesheet"> <link href="/css/tailwind.css" rel="stylesheet">
</head> </head>
<style>
.minirt-canvas-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(32, 32, 32, 0.5);
}
.minirt-canvas {
height: 80dvh;
width: 80dvw;
color: white;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
</style>
<body> <body>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<header class="flex flex-col h-48 w-screen text-[#FFF6DA]" style="background-color: rgb(169, 74, 74);"> <header class="flex flex-col h-48 w-screen text-[#FFF6DA]" style="background-color: rgb(169, 74, 74);">
@ -45,15 +20,87 @@
</div> </div>
</header> </header>
<div class="flex flex-col gap-y-6 bg-neutral-200/70 w-[60%] ml-[10%] mr-auto px-16 py-8"> <div class="flex flex-col gap-y-6 bg-neutral-200/70 w-[60%] ml-[10%] mr-auto px-16 py-8">
<h2 class="text-4xl font-bold text-gray-800">The Shell</h2> <h2 class="text-4xl font-bold text-gray-800">The Raytracer</h2>
<div class="space-y-2"> <div class="space-y-2">
<p class="leading-relaxed text-gray-700 max-w-prose"> <p class="leading-relaxed text-gray-700 max-w-prose">
The next project i want to showcase is the miniRT (mini-raytracer).<br/>
Like the shell, this was part of the 42 curriculum, and was achieved within the same rules and
constraints as the previous project.
</p> </p>
<div style="display: none" class="minirt-canvas-background"> <p class="leading-relaxed text-gray-700 max-w-prose">
<div class="minirt-canvas"> This time, the assignment was to create a simple raytracer from scratch,
using a custom wrapper around the linux X11 API, for simple window creation and I/O.
</p>
<p class="leading-relaxed text-gray-700 max-w-prose">
A raytracer is a program that uses a technique called
<a class="underline text-blue-700 hover:text-blue-900" href="https://en.wikipedia.org/wiki/Ray_tracing_(graphics)">"raytracing"</a>,
which essentially means that we simulate looking at a scene from our "eye-position", and use the computer screen like a window through which we
look at this scene.
</p>
<p class="leading-relaxed text-gray-700 max-w-prose">
Now, we imagine shooting a perfectly straight "ray" from our "eye" through each pixel of the screen, and trace that ray's path to see if it will
intersect with an object. If yes, we know that this specific pixel can display the color of the object that it hit.
We repeat this process for each pixel. Using some math to check for lighting, shadows from other objects, distance and more,
we can determine a final color for each pixel, and with that create a remarkably realistic image from our scene.
</p>
<p class="leading-relaxed text-gray-700 max-w-prose">
On top of the basic image generation, we added some extra feature that were not required by the subject, but seemed interesting enough to learn about
and implement on our own.<br/>
Some of the extra features are:
</p>
<ul class="list-disc list-inside space-y-2 text-gray-700 ml-4">
<li>Multithreaded</li>
<li>Textures and Bumpmaps</li>
<li>Dynamic Scene/Texture/Bumpmap loading</li>
<li>Menus with buttons and sliders</li>
<li>Simple File-Explorer to load resources at runtime</li>
<li>Diffrent Reflection Models</li>
<li>Objects as Perfect Mirrors</li>
<li>Skybox</li>
</ul>
<p class="leading-relaxed text-gray-700 max-w-prose">
This project was again a team effort, my teammate
<a class="underline text-blue-700 hover:text-blue-900" href="https://github.com/benszilas">Benjámin Szilas</a>
was very effective at implementing a lot of the math formulas.
He is responsible for a lot of the tracing functions, he implemented shadows, reflection, light-dispersion, anti-aliasing and much more,
whereas I prefered to work on the I/O focused parts like the menus, scene manipulation, file explorer and so on.<br/>
I had a lot of fun with this project, and learned a lot about working as a team, graphical programming and (some) maths!
</p>
<p class="leading-relaxed text-gray-700 max-w-prose">
As a final note: since we finished the project, and I was planning on showcasing it here, I have reimplemented the graphical part to use
<a class="underline text-blue-700 hover:text-blue-900" href="https://github.com/raysan5/raylib">Raylib</a>
since it seemed more future proof to use.
</p>
<p class="leading-relaxed text-gray-700 max-w-prose">
Again, I have provided a live version below, feel free to play around in it!
</p>
<p class="leading-relaxed text-gray-700 max-w-prose">
Credits go to
<a class="underline text-blue-700 hover:text-blue-900" href="https://github.com/novnc/noVNC">noVNC</a>
for providing the web integration, their stuff is really great!
</p>
<p class="leading-relaxed text-gray-700 max-w-prose">
Before you start, you will need to request the password by clicking the button below.
</p>
<div class="flex gap-4">
<button id="minirt-password-btn" class="flex p-2 w-full bg-neutral-100/50 place-items-center justify-center rounded-md">
Get Password
</button>
<div id="minirt-password-display" class="flex w-full h-[5dvh] place-items-center justify-center"></div>
</div>
<div class="flex max-w-prose h-[20dvh] justify-center place-items-center">
<button id="minirt-start-button" class="flex p-4 bg-neutral-100/50 place-items-center rounded-md">
Start Raytracer
</button>
<div>
<div id="minirt-canvas-background" style="display: none" class="minirt-canvas-background">
<button id="canvas-close-btn" class="absolute top-[16px] right-[16px] text-white">Close</button>
<div id="top_bar">
<div id="status">Loading</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/js/minirt.js"></script>
</body> </body>
</html> </html>

View File

@ -22,7 +22,8 @@
<h2 class="text-4xl font-bold text-gray-800">The Shell</h2> <h2 class="text-4xl font-bold text-gray-800">The Shell</h2>
<div class="space-y-2"> <div class="space-y-2">
<p class="leading-relaxed text-gray-700 max-w-prose"> <p class="leading-relaxed text-gray-700 max-w-prose">
The minishell project is part of the common core curriculum at ecole42 and The minishell project is part of the common core curriculum at
<a class="underline text-blue-700 hover:text-blue-900" href="https://42vienna.com">ecole42</a> and
is seen as a milestone and one of the biggest jumps in complexity on the way to completing the cursus. is seen as a milestone and one of the biggest jumps in complexity on the way to completing the cursus.
</p> </p>
<p class="leading-relaxed text-gray-700 max-w-prose"> <p class="leading-relaxed text-gray-700 max-w-prose">
@ -37,6 +38,8 @@
<li>Command execution</li> <li>Command execution</li>
<li>File Descriptor redirection</li> <li>File Descriptor redirection</li>
<li>Pipe Operators</li> <li>Pipe Operators</li>
<li>Environment Variables</li>
<li>Builtin functions (cd, export, exit etc.)</li>
<li>Heredoc</li> <li>Heredoc</li>
<li>Logical AND ( && )</li> <li>Logical AND ( && )</li>
<li>Logical OR ( || )</li> <li>Logical OR ( || )</li>
@ -62,8 +65,8 @@
</p> </p>
<p class="leading-relaxed text-gray-700 mb-4 max-w-prose"> <p class="leading-relaxed text-gray-700 mb-4 max-w-prose">
But, out of cursiousity, we decided to recreate the readline function, removing the dependecy and leaks from our project, But, out of cursiousity, we decided to recreate the readline function, removing the dependecy and leaks from our project,
and learing a lot on the way.<br> and leanring a lot along the way.<br>
In my opinion, this is arguably the most interesting part of our implementation, and I very much enjoyed it! In my opinion, this is one of the more interesting part of our implementation, and I very much enjoyed it!
</p> </p>
<p class="leading-relaxed text-gray-700 mb-4 max-w-prose"> <p class="leading-relaxed text-gray-700 mb-4 max-w-prose">
This project was a team effort, and big thanks go out to my team partner<br>and good friend, This project was a team effort, and big thanks go out to my team partner<br>and good friend,
@ -73,7 +76,14 @@
<p class="leading-relaxed text-gray-700 mb-4 max-w-prose"> <p class="leading-relaxed text-gray-700 mb-4 max-w-prose">
The source code for the project can be seen on my The source code for the project can be seen on my
<a class="underline text-blue-700 hover:text-blue-900" href="https://git.victorvobis.org/minishell">git server</a> and <a class="underline text-blue-700 hover:text-blue-900" href="https://git.victorvobis.org/minishell">git server</a> and
I have also provided a live version of the shell found below. I have also provided a live version of the shell found below.<br/>
Please keep in mind, this is barely a prototype, and serves more as a proof of concept, than a fully implemented shell.
</p>
<p class="leading-relaxed text-gray-700 mb-4 max-w-prose">
Credits and many thanks to
<a class="underline text-blue-700 hover:text-blue-900" href="https://github.com/tsl0922/ttyd">ttyd</a>
for providing the web integeration!<br/>
Have fun!
</p> </p>
<div id="shell-container" class="flex max-w-[800px] min-h-[600px] bg-gray-800 justify-center place-items-center"> <div id="shell-container" class="flex max-w-[800px] min-h-[600px] bg-gray-800 justify-center place-items-center">
<button id="shell-start-button" class="flex p-4 bg-neutral-100/50 place-items-center rounded-md"> <button id="shell-start-button" class="flex p-4 bg-neutral-100/50 place-items-center rounded-md">
@ -81,28 +91,14 @@
</button> </button>
</div> </div>
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<p class="leading-relaxed text-gray-700 mb-4"> <p class="leading-relaxed text-gray-700 mb-4">
After trying out the shell, feel free to check out my other projects! After trying out the shell, feel free to check out my other projects!
</p> </p>
<a class="underline text-blue-700 hover:text-blue-900" href="/html/minirt.html">Next Project...</a> <a id="next-project-a" class="underline text-blue-700 hover:text-blue-900" href="/html/minirt.html">Next Project...</a>
</div> </div>
</div> </div>
</div> </div>
<script> <script type="module" src="/js/minishell.js"></script>
const config = {
BASE_URL: 'http://localhost:5080/',
};
const shell_button = document.getElementById('shell-start-button');
shell_button.addEventListener('click', function() {
const shell_container = document.getElementById('shell-container');
const shell_frame = document.createElement('iframe');
shell_frame.height = '600';
shell_frame.width = '800';
shell_frame.src = config.BASE_URL + 'minishell/vnc';
shell_container.appendChild(shell_frame);
shell_button.style = 'display: none';
});
</script>
</body> </body>
</html> </html>

View File

@ -1,7 +0,0 @@
module.exports = {
content: ["./*.html"],
theme: {
extend: {},
},
plugins: [],
}

155
nginx/site/js/minirt.js Normal file
View File

@ -0,0 +1,155 @@
//
// !!! DISCLAIMER !!!
// This is the default template that comes with noVNC, all credit goes to them
//
import { config } from '/config.js';
import RFB from '/vnc/core/rfb.js';
// RFB holds the API to connect and communicate with a VNC server
function closeMinirtFrame() {
const screen = document.getElementById('screen');
if (screen)
screen.remove();
const canvas_background = document.getElementById('minirt-canvas-background');
canvas_background.style = 'display: none';
}
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
closeMinirtFrame();
}
});
const get_passwd = document.getElementById('minirt-password-btn');
get_passwd.addEventListener('click', function() {
try {
fetch(config.BASE_URL + 'minirt/password')
.then(resp => {
if (!resp.ok) {
throw new Error("Failed to fetch password");
}
return resp.json();
})
.then(data => {
const passwd_display = document.getElementById('minirt-password-display');
passwd_display.textContent = `Password: ${data.password}`;
});
} catch (c) {
console.error(e);
}
})
const close_button = document.getElementById('canvas-close-btn');
close_button.addEventListener('click', closeMinirtFrame);
const minirt_button = document.getElementById('minirt-start-button');
minirt_button.addEventListener('click', function() {
// const url = config.BASE_URL + 'minirt/vnc';
const canvas_background = document.getElementById('minirt-canvas-background');
canvas_background.style = '';
const screen = document.createElement('div');
screen.id = 'screen';
canvas_background.appendChild(screen);
let rfb;
let desktopName;
// When this function is called we have
// successfully connected to a server
function connectedToServer(e) {
status("Connected to " + desktopName);
}
// This function is called when we are disconnected
function disconnectedFromServer(e) {
if (e.detail.clean) {
status("Disconnected");
} else {
status("Something went wrong, connection is closed");
}
}
// When this function is called, the server requires
// credentials to authenticate
function credentialsAreRequired(e) {
const password = prompt("Password required:");
if (password)
rfb.sendCredentials({ password: password });
}
// When this function is called we have received
// a desktop name from the server
function updateDesktopName(e) {
desktopName = e.detail.name;
}
// Show a status text in the top bar
function status(text) {
document.getElementById('status').textContent = text;
}
// This function extracts the value of one variable from the
// query string. If the variable isn't defined in the URL
// it returns the default value instead.
function readQueryVariable(name, defaultValue) {
// A URL with a query parameter can look like this:
// https://www.example.com?myqueryparam=myvalue
//
// Note that we use location.href instead of location.search
// because Firefox < 53 has a bug w.r.t location.search
const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = document.location.href.match(re);
if (match) {
// We have to decode the URL since want the cleartext value
return decodeURIComponent(match[1]);
}
return defaultValue;
}
// Read parameters specified in the URL query string
// By default, use the host and port of server that served this file
const host = readQueryVariable('host', window.location.hostname);
let port = readQueryVariable('port', window.location.port);
const password = readQueryVariable('password');
const path = readQueryVariable('path', 'websockify');
// | | | | | |
// | | | Connect | | |
// v v v v v v
status("Connecting");
// Build the websocket URL used to connect
let url;
if (window.location.protocol === "https:") {
url = 'wss';
} else {
url = 'ws';
}
url += '://' + host;
if(port) {
url += ':' + port;
}
url += '/' + path;
// Creating a new RFB object will start a new connection
rfb = new RFB(screen, url,
{ credentials: { password: password } });
// Add listeners to important events from the RFB module
rfb.addEventListener("connect", connectedToServer);
rfb.addEventListener("disconnect", disconnectedFromServer);
rfb.addEventListener("credentialsrequired", credentialsAreRequired);
rfb.addEventListener("desktopname", updateDesktopName);
// Set parameters that can be changed on an active connection
rfb.viewOnly = readQueryVariable('view_only', false);
rfb.scaleViewport = readQueryVariable('scale', false);
});

View File

@ -0,0 +1,19 @@
import { config } from '/config.js';
const shell_button = document.getElementById('shell-start-button');
shell_button.addEventListener('click', function() {
const shell_container = document.getElementById('shell-container');
const shell_frame = document.createElement('iframe');
shell_frame.id = 'minishell-iframe';
shell_frame.height = '600';
shell_frame.width = '800';
shell_frame.src = config.BASE_URL + 'minishell/vnc';
shell_container.appendChild(shell_frame);
shell_button.style = 'display: none';
});
const next_project_a = document.getElementById('next-project-a');
next_project_a.addEventListener('click', function() {
const shell_frame = document.getElementById('minishell-iframe');
shell_frame.remove();
});

View File

@ -0,0 +1 @@
{ "password": "1312" }

View File

@ -0,0 +1,9 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./html/*.html"],
theme: {
extend: {},
},
plugins: [],
}