Snapshot: alpha-2026-9

This commit is contained in:
j3d1 2026-09-02 21:40:28 +02:00
parent 9acf5a97e2
commit d00b5c7961
241 changed files with 85546 additions and 2409 deletions

View file

@ -7,10 +7,11 @@ ENV PYTHONUNBUFFERED 1
# Set work directory
WORKDIR /code
RUN mkdir /git
# Install dependencies
COPY requirements.txt /code/
RUN pip install --no-cache-dir -r requirements.txt
RUN touch /mnt/db.sqlite3 && touch /mnt/testdata.py && mkdir /mnt/userfiles
# Run the application
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000", "--insecure"]
CMD ["sh", "-c", "python manage.py migrate && python manage.py runserver 0.0.0.0:8000 --insecure"]

View file

@ -3,11 +3,13 @@ FROM node:14
# Set work directory
WORKDIR /app
RUN mkdir /git
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package.json ./
COPY extras/ ./extras/
RUN npm install
CMD [ "npm", "run", "dev", "--", "--host"]

View file

@ -1,14 +1 @@
FROM nginx:bookworm
# snakeoil for localhost
RUN apt-get update && \
apt-get install -y openssl && \
openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 && \
openssl rsa -passin pass:x -in server.pass.key -out server.key && \
rm server.pass.key && \
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=localhost" && \
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt &&\
mv server.crt /etc/nginx/nginx.crt && \
mv server.key /etc/nginx/nginx.key \

View file

@ -3,6 +3,9 @@ import socketserver
import urllib.parse
import dnslib
import base64
import socket
UPSTREAM_DNS = ("8.8.8.8", 53)
try:
@ -11,6 +14,13 @@ try:
if record["name"] == qname and record["type"] == qtype and "value" in record:
return record["value"]
def resolve_recursive(raw_query):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.settimeout(5)
sock.sendto(raw_query, UPSTREAM_DNS)
data, _ = sock.recvfrom(4096)
return data
class DnsHttpRequestHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
@ -40,15 +50,16 @@ try:
print("SRV record")
reply = dnslib.SRV(record["priority"], record["weight"], record["port"], record["target"])
response.add_answer(dnslib.RR(dns.q.qname, dns.q.qtype, rdata=reply))
pack = response.pack()
else:
response.header.rcode = dnslib.RCODE.NXDOMAIN
print(f"Recursively resolving {dns.q.qname}")
pack = resolve_recursive(raw)
print(response)
self.send_response(200)
self.send_header("Content-type", "application/dns-message")
self.end_headers()
pack = response.pack()
self.wfile.write(pack)
return
except Exception as e:

View file

@ -0,0 +1,110 @@
version: '3.8'
name: dev
services:
backend-a:
build:
context: ../../backend/
dockerfile: ../deploy/dev/Dockerfile.backend
environment:
TOOLSHED_DB_PATH: /mnt/db.sqlite3
TOOLSHED_USERFILES_PATH: /mnt/userfiles
TOOLSHED_SETUP_PATH: /mnt/testdata.py
volumes:
- ../../backend:/code
- ../../.git:/git:ro
- ./instance_a/a.env:/code/.env
- ./instance_a/testdata.py:/mnt/testdata.py
- ./instance_a/a.sqlite3:/mnt/db.sqlite3
- ./instance_a/userfiles:/mnt/userfiles
expose:
- 8000
command: bash -c "python configure.py; python configure.py testdata; python manage.py runserver 0.0.0.0:8000 --insecure"
backend-b:
build:
context: ../../backend/
dockerfile: ../deploy/dev/Dockerfile.backend
environment:
TOOLSHED_DB_PATH: /mnt/db.sqlite3
TOOLSHED_USERFILES_PATH: /mnt/userfiles
TOOLSHED_SETUP_PATH: /mnt/testdata.py
volumes:
- ../../backend:/code
- ../../.git:/git:ro
- ./instance_b/b.env:/code/.env
- ./instance_b/testdata.py:/mnt/testdata.py
- ./instance_b/b.sqlite3:/mnt/db.sqlite3
- ./instance_b/userfiles:/mnt/userfiles
expose:
- 8000
command: bash -c "python configure.py; python configure.py testdata; python manage.py runserver 0.0.0.0:8000 --insecure"
frontend:
build:
context: ../../frontend/
dockerfile: ../deploy/dev/Dockerfile.frontend
volumes:
- ../../frontend:/app
- ../../.git:/git:ro
- /app/node_modules
expose:
- 5173
command: bash -c "npm install && npm run dev -- --host"
wiki:
build:
context: ../../
dockerfile: deploy/dev/Dockerfile.wiki
volumes:
- ../../mkdocs.yml:/wiki/mkdocs.yml
- ../../docs:/wiki/docs
expose:
- 8001
command: mkdocs serve --dev-addr=0.0.0.0:8001
proxy-a:
build:
context: ./
dockerfile: Dockerfile.proxy
volumes:
- ./instance_a/nginx-a.dev.conf:/etc/nginx/nginx.conf:ro
- ./instance_a/dns.json:/var/www/dns.json:ro
- ./instance_a/domains.json:/var/www/domains.json:ro
- ./instance_a/userfiles:/var/www/userfiles:ro
# A stable, CA-signed cert (see frontend/.local/make_localhost.sh) covering localhost plus
# every loopback IP a dev proxy binds to below, instead of Dockerfile.proxy generating a
# fresh throwaway self-signed one on every image build - that regenerated cert invalidated
# any trust exception you'd added in your browser on the previous build. Trust
# frontend/.local/RootCA.crt once (see docs/development.md) and it keeps working across
# rebuilds.
- ../../frontend/.local/localhost.crt:/etc/nginx/nginx.crt:ro
- ../../frontend/.local/localhost.key:/etc/nginx/nginx.key:ro
ports:
- "127.0.0.1:8080:8080"
- "127.0.0.3:5353:5353"
proxy-b:
build:
context: ./
dockerfile: Dockerfile.proxy
volumes:
- ./instance_b/nginx-b.dev.conf:/etc/nginx/nginx.conf:ro
- ./instance_b/userfiles:/var/www/userfiles:ro
- ../../frontend/.local/localhost.crt:/etc/nginx/nginx.crt:ro
- ../../frontend/.local/localhost.key:/etc/nginx/nginx.key:ro
ports:
- "127.0.0.2:8080:8080"
dns:
build:
context: ./
dockerfile: Dockerfile.dns
volumes:
- ./zone.json:/dns/zone.json
expose:
- 8053
networks:
default:
aliases:
- toolshed-dns

View file

@ -2,6 +2,8 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG=True
SERVE_X_ACCEL_REDIRECT=False
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY='e*lm&*!j0_stqaiod$1zob(vs@aq6+n-i$1%!rek)_v9n^ue$3'

View file

@ -1,6 +1,8 @@
events {}
http {
client_max_body_size 128M;
upstream backend {
server backend-a:8000;
}
@ -14,7 +16,7 @@ http {
}
upstream dns {
server dns:8053;
server toolshed-dns:8053;
}
server {
@ -45,8 +47,34 @@ http {
proxy_pass http://backend;
}
location /media {
proxy_set_header Host $host:$server_port;
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 $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port;
proxy_pass http://backend;
}
location /djangoadmin {
proxy_set_header Host $host:$server_port;
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 $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port;
proxy_pass http://backend;
}
location /docs {
proxy_pass http://backend/docs;
proxy_set_header Host $host:$server_port;
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 $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port;
proxy_pass http://backend;
}
location /static {
@ -80,17 +108,35 @@ http {
# DoH server
server {
listen 5353 ssl;
server_name localhost;
server_name localhost 127.0.0.3;
ssl_certificate /etc/nginx/nginx.crt;
ssl_certificate_key /etc/nginx/nginx.key;
location /dns-query {
proxy_pass http://dns;
# allow any origin
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
# Ensure CORS headers are present even when nginx generates 5xx responses.
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept, Content-Type, Origin, User-Agent' always;
add_header 'Access-Control-Expose-Headers' 'Content-Type' always;
error_page 500 502 503 504 = @doh_error;
location /dns-query {
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept, Content-Type, Origin, User-Agent' always;
add_header 'Access-Control-Max-Age' 86400 always;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain; charset=utf-8';
return 204;
}
proxy_pass http://dns;
}
location @doh_error {
default_type text/plain;
return 502 'DoH upstream unavailable';
}
}
}

View file

@ -1,6 +1,8 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG=True
SERVE_X_ACCEL_REDIRECT=True
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY='7ccxjje%q@@0*z+r&-$fy3(rj9n)%$!sk-k++-&rb=_u(wpjbe'

View file

@ -1,6 +1,8 @@
events {}
http {
client_max_body_size 128M;
upstream backend {
server backend-b:8000;
}
@ -35,6 +37,28 @@ http {
proxy_pass http://backend;
}
location /media {
proxy_set_header Host $host:$server_port;
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 $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port;
proxy_pass http://backend;
}
location /redirect_media/ {
internal;
alias /var/www/userfiles/;
# This location serves the file directly, bypassing Django (and therefore
# django-cors-headers) entirely - it's the target of the X-Accel-Redirect
# response from files/media_urls.py, used when SERVE_X_ACCEL_REDIRECT=True.
# CORS headers must be added here explicitly since nothing else will.
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Accept, Content-Type, Origin, User-Agent' always;
}
location /docs {
proxy_pass http://backend/docs;
}

View file

@ -21,4 +21,4 @@
"target": "127.0.0.2."
}
}
]
]