- Python 78.8%
- HTML 18.3%
- Shell 1.9%
- Dockerfile 0.9%
| antibooking | ||
| core | ||
| infra | ||
| static | ||
| templates | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| build_css.sh | ||
| docker-compose.yml | ||
| Dockerfile | ||
| manage.py | ||
| README.md | ||
| requirements.txt | ||
| TODO.md | ||
Antibooking
Piattaforma web indipendente per mettere in contatto direttamente artisti e promoter/locali per concerti dal vivo nel circuito indipendente.
- Artisti: profilo con bio, generi, città, embed audio/video, scheda tecnica e press kit in PDF, calendario delle disponibilità.
- Promoter/locali: profilo con capienza, impianto audio e vitto/alloggio, ricerca degli artisti per genere, città e disponibilità last minute.
conceptz: https://pad.cisti.org/p/antibooking
stack
- python 3.12 e django 5.2 (SQLite in modalità WAL)
- django templates (SSR), HTMX per ricerca e filtri, Tailwind CSS per lo stile (binario standalone, niente Node.js)
- Gunicorn e WhiteNoise in produzione, Docker Compose per il deploy
- Reverse proxy HTTPS esterno all'app
sviluppo locale
setup
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
nel .env locale imposta DJANGO_SECRET_KEY (una stringa casuale, ad esempio python3 -c "import secrets; print(secrets.token_urlsafe(50))") e DJANGO_DEBUG=True. Poi:
./build_css.sh # genera static/css/output.css
python manage.py migrate
python manage.py createsuperuser # opzionale, per l'admin
python manage.py runserver
Il sito è su http://127.0.0.1:8000/, l'admin su http://127.0.0.1:8000/admin/.
variabili d'ambiente
| variabile | uso | default |
|---|---|---|
DJANGO_SECRET_KEY |
chiave segreta (obbligatoria, l'app non parte senza) | — |
DJANGO_DEBUG |
True solo in locale |
False |
DJANGO_ALLOWED_HOSTS |
host accettati, separati da virgola | vuoto |
DJANGO_CSRF_TRUSTED_ORIGINS |
origini https per i form in produzione | vuoto |
DJANGO_DEFAULT_FROM_EMAIL |
mittente delle email | Antibooking <noreply@antibooking.local> |
EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_USE_TLS |
SMTP | se EMAIL_HOST è vuoto, le email vanno in console |
dati di prova
python manage.py seed_test_data
crea 10 artisti (artista01@example.com … artista10@example.com) e 3 promoter (promoter01@example.com … promoter03@example.com), tutti con password test-password-123. I link audio e video sono video YouTube pubblici e generici, usati solo per provare gli embed. Il comando è idempotente e rifiuta di girare se DJANGO_DEBUG non è True.
test
python manage.py test core
comandi utili
python manage.py shell # shell Django con i modelli già importati
python manage.py makemigrations core # dopo una modifica ai modelli
python manage.py check # controlli di sistema
python manage.py showmigrations core # stato delle migrazioni
debug
- Con
DJANGO_DEBUG=Truegli errori mostrano il traceback completo nel browser. Non impostarlo mai sul server. - Le email di contatto (
/contatta/<id>/) non partono davvero: compaiono nel terminale dove girarunserver. - Per ispezionare il database usa
python manage.py dbshelloppure un browser SQLite sul filedata/db.sqlite3. Con WAL attivo, chiudi il server prima di copiare il file a mano.
errori frequenti:
| sintomo | causa probabile |
|---|---|
DJANGO_SECRET_KEY non è impostata |
manca il .env o la variabile |
unable to open database file |
cartella data/ mancante o non scrivibile |
400 Bad Request con dominio corretto |
host assente in DJANGO_ALLOWED_HOSTS |
403 CSRF verification failed in produzione |
origine https assente in DJANGO_CSRF_TRUSTED_ORIGINS |
429 sul form di contatto |
più di 2 messaggi in un minuto dalla stessa sessione: aspetta un minuto |
| pagina senza stile | manca static/css/output.css: esegui ./build_css.sh |
produzione
Antibooking ha un unico deploy di produzione. La gestione del server, degli accessi e del proxy resta separata da questo README; qui sono raccolti i comandi dell'applicazione, da eseguire nella directory del progetto con Docker Compose disponibile.
L'app gira in due container:
web: Gunicorn con l'applicazione, sulla rete privata del progetto, senza porte pubblicate.antibooking-frontend: Nginx che serve/media/in sola lettura e inoltra il resto aweb. È l'unico servizio collegato alla reteproxy_default, condivisa con il reverse proxy.
Il reverse proxy esterno termina HTTPS e inoltra il dominio a antibooking-frontend:80, passando X-Forwarded-Proto. I file statici sono serviti da WhiteNoise. Il Compose non pubblica alcuna porta sull'host.
configurazione
cp .env.example .env && chmod 600 .env
nano .env
imposta almeno:
DJANGO_SECRET_KEY=<stringa casuale nuova, diversa da quella locale>
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=antibooking.example.org
DJANGO_CSRF_TRUSTED_ORIGINS=https://antibooking.example.org
DJANGO_DEFAULT_FROM_EMAIL=Antibooking <noreply@example.org>
EMAIL_HOST=<server SMTP>
EMAIL_PORT=587
EMAIL_HOST_USER=<utente SMTP>
EMAIL_HOST_PASSWORD=<password SMTP>
EMAIL_USE_TLS=True
crea le cartelle prima del primo avvio, altrimenti Docker le crea di proprietà di root:
mkdir -p data media
sudo chown -R 1000:1000 data media
Il container usa UID/GID 1000:1000. Verifica anche che l'utente che esegue i backup possa leggere il database e scrivere in data/, senza allargare indiscriminatamente i permessi.
primo avvio
docker compose build # esegue build_css.sh e collectstatic
docker compose run --rm web python manage.py migrate
docker compose run --rm web python manage.py createsuperuser
docker compose up -d
docker compose exec web python manage.py check --deploy
pre deploy: prova login, form, invio email, CSS e caricamento e apertura degli upload. Valuta HSTS solo dopo aver verificato HTTPS. Con il redirect HTTP→HTTPS fatto dal proxy, check --deploy segnala ancora security.W008; finché HSTS non è attivo, segnala anche security.W004. Sono entrambi attesi.
backup
Lo script richiede sqlite3 sull'host. Provalo con lo stesso utente che eseguira' i backup programmati:
bash infra/backup.sh
I backup finiscono in data/backup_YYYY-MM-DD.sqlite3, ne restano 7. Programma l'esecuzione sul server e copia i backup anche fuori dal server. Salva separatamente gli upload in media/, il .env e la configurazione di deploy, proteggendo le copie che contengono credenziali. Prova anche il ripristino.
procedure operative
Eseguire dalla directory del progetto sul server.
Aggiornare il sito:
bash infra/backup.sh
git pull --ff-only
docker compose up -d --build
docker compose exec web python manage.py migrate
Diagnosi:
docker compose ps
docker compose logs -f web
docker compose exec web python manage.py check --deploy
Ripristino da backup (con il sito fermo):
docker compose stop
cp data/backup_YYYY-MM-DD.sqlite3 data/db.sqlite3
rm -f data/db.sqlite3-wal data/db.sqlite3-shm
sudo chown 1000:1000 data/db.sqlite3
docker compose start
Non riavviare gli altri progetti del server per aggiornare Antibooking.
test
python manage.py test core
struttura repo
antibooking/ configurazione Django (settings, urls, wsgi/asgi)
core/
models.py User, Artist, Promoter, Availability
backends.py autenticazione con email
forms.py ricerca, login, registrazione, profili, disponibilità, contatto
views.py search, dettagli, profilo, contatto (con limite di invio)
validators.py whitelist dei domini per gli embed
templatetags/
embed_tags.py filtro generate_embed (regex per Mirlo, Bandcamp, Spotify, YouTube)
management/commands/
seed_test_data.py dati di prova (solo con DEBUG)
admin.py backoffice
templates/ base.html, pagine in templates/core/
static/src/input.css sorgente Tailwind
static/vendor/ htmx.min.js servito localmente
build_css.sh build del CSS con il binario standalone di Tailwind
infra/backup.sh backup giornaliero di SQLite
Dockerfile immagine dell'app (Gunicorn, uid 1000)
docker-compose.yml servizi web (Gunicorn) e antibooking-frontend (Nginx)
infra/nginx/antibooking.conf configurazione Nginx del frontend: media e proxy verso web
note
- Il login avviene con email e password; lo username è generato automaticamente alla registrazione.
- L'email reale non compare mai nell'HTML: per contattare un utente si usa il form su
/contatta/<id>/, che invia il messaggio viasend_mail. - Gli embed non sono salvati come HTML: il filtro ricostruisce l'iframe solo dagli ID estratti da URL validati, con
sandboxattivo. - Le immagini caricate finiscono in
media/. - Le decisioni aperte e i punti da sistemare prima del deploy sono in
TODO.md.