diff --git a/docs/AUTO_STARTING.md b/docs/AUTO_STARTING.md new file mode 100644 index 0000000..5fb73e8 --- /dev/null +++ b/docs/AUTO_STARTING.md @@ -0,0 +1,23 @@ +# Auto-starting DrawIt + +After you know that DrawIt is up and running, you should have a mechanism that auto-start the service. + +## SystemD + +A common way to auto-start a service is to use systemd. There is a [dedicated file](SYSTEMD.md) for how to use this method. + +## PM2 + +Another way to start the service is using something called PM2. This can be used for any npm/node project. + +### Guide + +1. Install PM2: `npm install pm2` +2. Install PM2 Logrotate: `npm install pm2-logrotate` +3. Start the server: `pm2 start npm --name "drawit" -- start` + +You should now be running your very own DrawIt instance! + +## Additional reading + +After you know *it works!*, you should set up a [reverse proxy](REVERSE_PROXIES.md). \ No newline at end of file diff --git a/docs/BUILDING.md b/docs/BUILDING.md new file mode 100644 index 0000000..17efae7 --- /dev/null +++ b/docs/BUILDING.md @@ -0,0 +1,29 @@ +# Building guide + +## Prerequisites + +- [node](https://nodejs.org/en/download) v22.22.0+ +- npm v10.9.4+ +- git v2.34.1+ + +Other versions may work, they are simply untested. + +## Guide + +1. Clone the repo: `git clone https://git.dou.bet/iamdoubz/drawit.git` +2. Enter the repo: `cd drawit` +3. Install DrawIt's dependencies: `npm install --omit=dev` +4. Copy env example: `cp .env.example .env.local` +5. Edit `.env.local` to match your environment: `nano .env.local` + +**NOTE:** be sure to generate a long and unique `SESSION_SECRET`. Recommendation: `openssl rand -hex 32` + +6. Build the production assets: `npm run build` +7. OPTIONALLY, check the TypeScript files: `npm run typecheck` +8. Start the server: `npm run start` + +You should now be running your very own DrawIt instance! + +## Additional reading + +After you know *it works!*, you should set up an [init startup service](AUTO_STARTING.md) and [reverse proxy](REVERSE_PROXIES.md). \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md new file mode 100644 index 0000000..e0717f3 --- /dev/null +++ b/docs/DOCKER.md @@ -0,0 +1,19 @@ +# Docker installation + +This guide does not cover how to install docker. Please use [docker's website](https://www.docker.com/get-started/) in order to find the best solution for you. If you already have docker installed, continue reading. + +## Pre-built images + +This is the recommended way by pulling images from the [repository](https://git.dou.bet/iamdoubz/drawit/packages/drawit). + +### Docker-Compose + +1. Copy the contents of `.env.example` and paste them into a file called `.env` +2. Change the variables to match your environment +3. Be sure to generate a long and secure SESSION_SECRET. My favorite is: `openssl rand -hex 32` +4. Download the `docker-compose.yml` file and run `sudo docker compose up -d` +5. DrawIt will be running on localhost on port 3000 by default + +### Building your own images + +If you really want to build your own image, you probably already know what you are doing. Best of luck! \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..ea6939b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,29 @@ +# Documentation files + +In this directory, you will find files relating to how DrawIt can be built, installed, auto-started, put behind a reverse proxy, and the thought process behind the creation of this application. + +## Getting started + +If you want to try out the lessons before you install your own instance, you can sign up at https://drawit.dou.bet. + +If you are ready to self-host your own instance of DrawIt, keep reading! + +## Docker + +The easiest way to setup your own instance is to use docker. See [DOCKER.md](DOCKER.md) for more details. + +## Building from source + +If you do not want to use docker, you can build it from source. See [BUILDING.md](BUILDING.md) for an in depth guide. + +## Reverse proxy + +It is *highly* recommended to use a reverse proxy in front of DrawIt. See [REVERSE_PROXIES.md](REVERSE_PROXIES.md) for some helpful information on how to set this up. + +## Auto-starting + +If you do not use docker, you should auto start DrawIt on system boot. See [AUTO_STARTING.md](AUTO_STARTING.md) for some options on how to do this. + +# Final thoughts + +The documentation is a living source. If something seems off or incorrect, create a new issue or even better, fork and make a pull request! \ No newline at end of file diff --git a/docs/REVERSE_PROXIES.md b/docs/REVERSE_PROXIES.md new file mode 100644 index 0000000..a87ed98 --- /dev/null +++ b/docs/REVERSE_PROXIES.md @@ -0,0 +1,41 @@ +# Reverse Proxy + +This document will explain how to setup a reverse proxy for nginx/freenginx. Contributions for other reverse proxies are welcomed! + +## General prerequisites + +- A domain name pointed at your server e.g. `drawit.yourdomain.com` +- Certbot installed for Let's Encrypt certificates (`sudo apt install certbot python3-certbot-nginx`) +- Port-forwarding + +## nginx/freenginx + +### Prerequisites + +- nginx/freenginx installed e.g. `sudo apt install nginx` +- A domain name pointed at your server e.g. `drawit.yourdomain.com` +- Certbot installed for Let's Encrypt certificates (`sudo apt install certbot python3-certbot-nginx`) + +### Install the config file + +Copy the provided config and substitute your domain: + +```bash +sudo cp docs/drawit.conf /etc/nginx/sites-available/drawit.conf # OR sudo cp docs/drawit.conf /etc/nginx/conf.d/drawit.conf +sudo sed -i 's/your.domain.com/drawit.yourdomain.com/g' /etc/nginx/[sites-available|conf.d]/drawit.conf +``` + +Enable the site (older nginx versions <1.19): + +```bash +sudo ln -s /etc/nginx/sites-available/drawit.conf /etc/nginx/sites-enabled/ +``` + +Test and reload: + +```bash +sudo nginx -t # must print "syntax is ok" +sudo nginx -s reload # loads changes/new files +``` + +Visit your site `drawit.yourdomain.com` and see if it loads! \ No newline at end of file diff --git a/docs/SYSTEMD.md b/docs/SYSTEMD.md new file mode 100644 index 0000000..12e6676 --- /dev/null +++ b/docs/SYSTEMD.md @@ -0,0 +1,54 @@ +# SystemD + +There are many ways to create a systemd compatible service file. This guide is tailored to be "production ready". + +## Guide + +1. Create a new user named drawit: `sudo useradd --system --create-home --shell /usr/sbin/nologin drawit` +2. Create env directory: `sudo mkdir -p /etc/drawit` +3. Copy `.env.example` here: `sudo cp .env.example /etc/drawit/drawit.env` +4. Change directory to /opt: `cd /opt` +5. Follow instructions to [clone the repo](BUILDING.md#guide). Skip steps 4, 5, and 8. +6. Change permissions to drawit: `sudo chown -R drawit:drawit /opt/drawit /etc/drawit` +7. Create a new service file: `sudo nano /etc/systemd/system/drawit.service` +8. Paste in these contents: + +``` +[Unit] +Description=DrawIt +After=syslog.target network.target remote-fs.target nss-lookup.target network-online.target +Wants=network-online.target +Requires=nginx.service + +[Service] +Type=simple +User=drawit +Group=drawit +WorkingDirectory=/opt/drawit +Environment=NODE_ENV=production +Environment=PORT=3000 +EnvironmentFile=/etc/drawit/drawit.env +ExecStart=/usr/bin/npm run start +RestartSec=5 +KillSignal=SIGINT +TimeoutStopSec=30 +SyslogIdentifier=drawit +TimeoutSec=30 +StandardOutput=journal +StandardError=journal +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +9. If you want to use a different port, make sure to change it e.g. `Environment=PORT=10000` +10. OPTIONALLY reload system daemon: `sudo systemctl daemon-reload` +11. Enable and start the service: `sudo systemctl enable --now drawit.service` +12. Check the service for errors: `sudo systemctl status drawit.service` + +You should now be running your very own DrawIt instance! + +## Additional reading + +After you know *it works!*, you should set up a [reverse proxy](REVERSE_PROXIES.md). \ No newline at end of file diff --git a/docs/drawit.conf b/docs/drawit.conf new file mode 100644 index 0000000..98e70d2 --- /dev/null +++ b/docs/drawit.conf @@ -0,0 +1,100 @@ +# /etc/nginx/conf.d/drawit.conf +# +# Assumes: +# - drawit is listening on 127.0.0.1:3000 (PORT=3000) +# - TLS is terminated here at nginx (recommended) +# - Certbot / Let's Encrypt has already issued a certificate +# +# Enable: +# sudo nginx -t +# sudo systemctl reload nginx + +# ── Upstream ────────────────────────────────────────────────────────────────── + +upstream drawit_backend { + server 127.0.0.1:3000; + # Keep a pool of connections to the backend so nginx does not open a new + # TCP connection for every WebSocket upgrade. + #keepalive 64; +} + +server { + listen 80; + listen [::]:80; + server_name your.domain.com; + + # Let's Encrypt ACME challenge — must be reachable over plain HTTP. + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # Everything else redirects to HTTPS. + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name your.domain.com; + + # Let's Encrypt Cert location + ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem; + + # Modern TLS only — drops IE11 and very old Android. + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + # Security Headers + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; # Or DENY + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "geolocation=(self), midi=(self), sync-xhr=(self), microphone=(self), camera=(self), magnetometer=(self), gyroscope=(self), fullscreen=(self), payment=(self), interest-cohort=()"; + #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; # Only enable this when you are ready! + + # Logging + access_log /var/log/nginx/drawit_access.log; + error_log /var/log/nginx/drawit_error.log info; + # Or, disable logging altogether + #access_log off; + #error_log /dev/null crit; + + # Proxy rules + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_ssl_verify off; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 86400; + proxy_http_version 1.1; + proxy_pass http://drawit_backend; + } + + # Block sensitive files + location ~* /(data|node_modules|src)/.*\.(db|db-shm|db-wal)$ { + deny all; + } + location ~* /(node_modules|src)/.*$ { + deny all; + } + # Block everything else + # Anything else gets a 444 (faster but confusing, change to 404 if you want) from nginx before it reaches the backend. + location ~* \.(php|asp|aspx|jsp|cgi|go|env|env.local|mod|sum|yaml|yml|md|gitignore|git|github|gitea|ts|mjs)$ { + return 444; + } + +} \ No newline at end of file