Jewpizza
A serious website for a serious domain name.
Install / Use
/learn @dtcooper/JewpizzaREADME
[jew.pizza][jewpizza-url] Website ✡️🍕
[![][jewpizza-badge]][jewpizza-url]
[![][stack-badge]][stack-url]
[![][license-badge]][license-url]
[][hurd-url]
[![][last-commit-badge]][last-commit-url]
[![][stars-badge]][stars-url]
Here's the code for the website that powers [jew.pizza][jewpizza-url], my personal website. Built using the [CLANG! THUD! IT'S A DARN JEW'S PANDA!][stack-url] stack.
Preface
I can't imagine why on earth in a million years you'd want to run this code. So, these instructions are mostly for me — in case of sudden amnesia or coming back to this project after a period of neglect.
Stack — CLANG! THUD! IT'S A DARN JEW'S PANDA!
It's built using the wildly popular and extremely common CLANG! THUD! IT'S A DARN JEW'S PANDA! stack, ie,
- C is for [Compose][docker-compose-url], ie Docker Compose, a multi-container orchestration tool;
- L is for [Liquidsoap][liquidsoap-url], a fantastic scripting language for declaratively describing audio streams;
- A is for [Alpine Linux][alpine-linux-url], a lightweight [Linux][linux-url] distribution perfect for containers, based on [musl libc][musl-url] and [BusyBox][busybox-url];
- N is for [nginx][nginx-url], a web server and [reverse proxy][reverse-proxy-url] using the [jonasal/nginx-certbot][nginx-certbot-url] container as its base (for [HTTPS][https-url]), using [embedded Lua][nginx-lua-url];
- G is for [Gunicorn][gunicorn-url] to run the web app via [Python][python-url]'s [Web Server Gateway Interface][wsgi-url];
- T is for [Tailwind CSS][tailwind-url], a utility-first CSS framework;
- H is for [huey][huey-url], a lightweight asynchronous task queue for [Python][python-url];
- U is for [Umami][umami-url] to get insights via web analytics;
- D is for [Docker][docker-url] to run all this crap in containers;
- I for [Icecast][icecast-url], a streaming media server for listeners to connect (using [Karl Heyes's fork][icecast-kh-url]);
- T for [Twilio][twilio-url], an API to send programmable communications — in this case, [text messages (SMS)][sms-url];
- S for [S3][amazon-s3-url], ie Amazon S3 Cloud Object Storage, to store large audio files (I use the compatible [DigitalOcean Spaces][digitalocean-spaces-url]);
- A is for [AlpineJS][alpinejs-url], a lightweight, reactive front-end [JavaScript][javascript-url] framework;
- D is for [Django][django-url], a [Python][python-url] back-end web framework;
- A is for Actions, ie [GitHub Actions][github-actions-url], to continuously and automatically build, test and deploy this code;
- R is for [Redis][redis-url], a data store and message broker;
- N is for [Navigo][navigo-url] that provides a simple [SPA][spa-url] router;
- J is for [Jinja][jinja-url] templating — like [Django][django-url]'s, but less sucky;
- E is for [esbuild][esbuild-url], a fast [JavaScript][javascript-url] bundler;
- W is for [WaveSurfer.js][wavesurfer-url], a front-end audio player with waveform visualizations;
- S is for [Server-Sent Events (SSE)][sse-url], to send realtime messages to the browser;
- P is for [PostgresSQL][postgres-url], a [SQL][sql-url] database;
- A for [autoheal][autoheal-url], ie Docker Autoheal, a tool to monitor and restart unhealthy docker containers;
- N is for [Nchan][nchan-url], an [nginx][nginx-url] module managing [EventSource][eventsource-url] ([SSE][sse-url]) clients;
- D is for [daisyUI][daisyui-url], a lightweight UI component framework on top of [Tailwind CSS][tailwind-url]; and
- A is for [audiowaveform][audiowaveform-url], The [BBC][bbc-url]'s offline rendering tool for generating waveforms for [WaveSurfer.js][wavesurfer-url].
CLANG! THUD! IT'S A DARN JEW'S PANDA! A very well-known acronym in the engineering world, probably. I definitely didn't just make this up as a joke.
Prerequisites
Everything runs with [Docker][docker-url] and [Docker Compose][docker-compose-url], including [nginx][nginx-url]. This can be deployed on any [Linux][linux-url] machine.
Installing, compiling, running, and maintaining the motley crew of technologies that make up the [CLANG! THUD! IT'S A DARN JEW'S PANDA!][stack-url] stack in both prod and dev environments would be an absolute nightmare. With [Docker][docker-url] and [Docker Compose][docker-compose-url], that can be done in just a couple of commands. It even works with [Docker Desktop on macOS][docker-for-mac-url]!
To install these on [Debian][debian-url]/[Ubuntu][ubuntu-url],
# Install Docker
curl -fsSL https://get.docker.com | sh
# Install latest Compose
sudo mkdir -p /usr/local/lib/docker/cli-plugins/
sudo curl -fsSL -o /usr/local/lib/docker/cli-plugins/docker-compose \
"$(curl -fsSL https://api.github.com/repos/docker/compose/releases/latest | grep browser_download_url | cut -d '"' -f 4 | grep -i "$(uname -s)-$(arch)$")"
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
Initial Setup
Clone, then copy and edit the .env file, and optionally copy over the
[Docker Compose][docker-compose-url] dev overrides.
git clone https://github.com/dtcooper/jewpizza.git
# Edit me, make sure you set SECRET_KEY!
cp .env.sample .env
# Needed for development only (when DEBUG=1)
ln -s docker-compose.dev.yml docker-compose.override.yml
Assuming you set DOMAIN_NAME=local.jew.pizza for local development, you'll
want to properly point your system's DNS that way. For example, add the following
to /etc/hosts.
# jew.pizza local development
127.0.0.1 local.jew.pizza
127.0.0.1 analytics.local.jew.pizza umami.local.jew.pizza
127.0.0.1 etc.local.jew.pizza
127.0.0.1 priv.local.jew.pizza
127.0.0.1 radio.local.jew.pizza play.local.jew.pizza listen.local.jew.pizza
127.0.0.1 www.local.jew.pizza
If you set NGINX_USE_LOCAL_CERTIFICATE_AUTHORITY=1 (and you should for
local development), you'll want to install the phony certificate authority's
root certificate located at <project-dir>/local-certificate-authority/caCert.pem.
It expires every 30 days.
Running
Development Mode (DEBUG=1 with docker-compose.dev.yml symlinked)
Build and start containers,
make build
docker compose up
The development app server will run at http://localhost:8000/, or if you've
set a DOMAIN_NAME and your /etc/hosts to work properly with it, navigate to
that. For example, https://local.jew.pizza/. You'll need to install the phony
certificate authority's root certificate (see above).
Miscellaneous Development Operations
# Django management command
docker compose run --rm app ./manage.py
# Run shell in app container (make shell)
docker compose run --rm app bash
# Pre commit checks + reformatting (not required)
make pre-commit
Faster Start Time (SSL Certificate Generation)
The entropy daemon [haveged][haveged-url] is a nice-to-have to provide your system with randomness to speed up SSL certificate generation. On [Debian][debian-url]/[Ubuntu][ubuntu-url], it can be installed via the following,
sudo apt-get install haveged
Production Mode (DEBUG=0)
Pull (or build) containers and run docker compose up in daemon mode (-d).
Set all appropriate variables in the .env file, making note to properly
configure the following,
- An SMTP server — works with [SendGrid][sendgrid-url]
- [Twilio][twilio-url] account SID and auth token
- [DigitalOcean][digitalocean-url] [Spaces][digitalocean-spaces-url], along with an API key, taking note to run the domain name's DNS off of [DigitalOcean][digitalocean-url]. This is necessary for wildcard certificates from [Certbot][certbot-url]/[Lets Encrypt][letsencrypt-url].
docker compose pull
# Or optionally build the containers via: make build
docker compose up -d
Change Passwords
Make sure to change these insecure passwords not sent in the .env file,
- Django:
dave:cooper - Umami:
dave:cooper
Automatic Deploying
You can automatically deploy the code one of two ways via [GitHub Actions][github-actions-url],
- Include the string
[deploy](or🚀) in your HEAD commit message and push; or - Trigger the [Deploy Workflow][deploy-workflow-url] manually.
License
This project is licensed under the [MIT License][mit-license-url] — see the [LICENSE][license-url] file for details.
Final Note
...and remember kids, have fun!
