1 Commits

Author SHA1 Message Date
codex_a
71d0e71834 fix: use plain StaticFilesStorage in dev settings
All checks were successful
CI / nightly-e2e (pull_request) Has been skipped
CI / pr-e2e (pull_request) Successful in 1m4s
CI / ci (pull_request) Successful in 1m23s
CompressedManifestStaticFilesStorage requires collectstatic to generate a
manifest before it can serve anything. Dev containers never run collectstatic
so every static asset 404s. Override to StaticFilesStorage in dev so Django
serves files directly from STATICFILES_DIRS and app static directories.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-28 20:44:43 +00:00
10 changed files with 5 additions and 177 deletions

View File

@@ -2,9 +2,6 @@ name: CI
on: on:
pull_request: pull_request:
push:
branches:
- main
schedule: schedule:
- cron: "0 2 * * *" - cron: "0 2 * * *"
@@ -191,15 +188,3 @@ jobs:
- name: Remove CI image - name: Remove CI image
if: always() if: always()
run: docker image rm -f "$CI_IMAGE" || true run: docker image rm -f "$CI_IMAGE" || true
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Deploy to lintel-prod-01
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.PROD_SSH_HOST }}
username: deploy
key: ${{ secrets.PROD_SSH_KEY }}
script: bash /srv/sum/nohype/app/deploy/deploy.sh

View File

@@ -4,11 +4,9 @@ DEBUG = True
INTERNAL_IPS = ["127.0.0.1"] INTERNAL_IPS = ["127.0.0.1"]
# Drop WhiteNoise in dev — it serves from STATIC_ROOT which is empty without # Use plain static file storage in dev — CompressedManifestStaticFilesStorage
# collectstatic, so it 404s every asset. Django's runserver serves static and # (set in base.py) requires collectstatic to have been run and will 404 on
# media files natively when DEBUG=True (via django.contrib.staticfiles + the # every asset otherwise.
# media URL pattern in urls.py).
MIDDLEWARE = [m for m in MIDDLEWARE if m != "whitenoise.middleware.WhiteNoiseMiddleware"]
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage" STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
try: try:

View File

@@ -2,16 +2,8 @@ from .base import * # noqa
DEBUG = False DEBUG = False
# Behind Caddy: trust the forwarded proto header so Django knows it's HTTPS.
# SECURE_SSL_REDIRECT is intentionally off — Caddy handles HTTPS redirects
# before the request reaches Django; enabling it here causes redirect loops.
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
USE_X_FORWARDED_HOST = True USE_X_FORWARDED_HOST = True
SECURE_SSL_REDIRECT = False SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True
CSRF_TRUSTED_ORIGINS = [
"https://nohypeai.net",
"https://www.nohypeai.net",
]

View File

@@ -1,5 +1,3 @@
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from django.views.generic import RedirectView from django.views.generic import RedirectView
@@ -23,6 +21,3 @@ urlpatterns = [
path("admin/", RedirectView.as_view(url="/cms/", permanent=False)), path("admin/", RedirectView.as_view(url="/cms/", permanent=False)),
path("", include(wagtail_urls)), path("", include(wagtail_urls)),
] ]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -1,23 +0,0 @@
nohypeai.net, www.nohypeai.net {
encode gzip zstd
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy "geolocation=(), microphone=(), camera=()"
X-Forwarded-Proto https
}
handle_path /static/* {
root * /srv/sum/nohype/static
file_server
}
handle_path /media/* {
root * /srv/sum/nohype/media
file_server
}
reverse_proxy localhost:8001
}

View File

@@ -1,33 +0,0 @@
#!/usr/bin/env bash
# Deploy script for No Hype AI — runs on lintel-prod-01 as deploy user.
# Called by CI after a successful push to main.
set -euo pipefail
SITE_DIR=/srv/sum/nohype
APP_DIR=${SITE_DIR}/app
cd "${SITE_DIR}"
echo "==> Pulling latest code"
git -C "${APP_DIR}" pull origin main
echo "==> Updating compose file"
cp "${APP_DIR}/docker-compose.prod.yml" "${SITE_DIR}/docker-compose.prod.yml"
echo "==> Ensuring static/media directories exist"
mkdir -p "${SITE_DIR}/static" "${SITE_DIR}/media"
echo "==> Rebuilding and recreating web container"
docker compose -f "${SITE_DIR}/docker-compose.prod.yml" up -d --no-deps --build --force-recreate web
echo "==> Waiting for health check"
for i in $(seq 1 30); do
if curl -fsS -H "Host: nohypeai.net" http://localhost:8001/ >/dev/null 2>&1; then
echo "==> Site is up"
exit 0
fi
sleep 3
done
echo "ERROR: site did not come up after 90s" >&2
docker compose -f "${SITE_DIR}/docker-compose.prod.yml" logs --tail=50 web
exit 1

View File

@@ -1,22 +0,0 @@
#!/bin/sh
set -e
python manage.py tailwind install --no-input
python manage.py tailwind build
python manage.py migrate --noinput
python manage.py collectstatic --noinput
# Set Wagtail site hostname from first entry in ALLOWED_HOSTS
python manage.py shell -c "
from wagtail.models import Site
import os
hostname = os.environ.get('ALLOWED_HOSTS', 'localhost').split(',')[0].strip()
Site.objects.update(hostname=hostname, port=443, site_name='No Hype AI')
"
exec gunicorn config.wsgi:application \
--workers 3 \
--bind 0.0.0.0:8000 \
--access-logfile - \
--error-logfile - \
--capture-output

View File

@@ -1,26 +0,0 @@
[Unit]
Description=No Hype AI (Docker Compose)
Requires=docker.service
After=docker.service network-online.target
[Service]
Type=simple
User=deploy
Group=www-data
WorkingDirectory=/srv/sum/nohype
ExecStartPre=docker compose -f docker-compose.prod.yml pull --ignore-pull-failures
ExecStart=docker compose -f docker-compose.prod.yml up --build
ExecStop=docker compose -f docker-compose.prod.yml down
Restart=always
RestartSec=10
TimeoutStartSec=300
TimeoutStopSec=30
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sum-nohype
[Install]
WantedBy=multi-user.target

View File

@@ -1,36 +0,0 @@
services:
web:
build: app
working_dir: /app
command: /app/deploy/entrypoint.prod.sh
env_file: .env
environment:
DJANGO_SETTINGS_MODULE: config.settings.production
volumes:
- /srv/sum/nohype/static:/app/staticfiles
- /srv/sum/nohype/media:/app/media
ports:
- "127.0.0.1:8001:8000"
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
env_file: .env
environment:
POSTGRES_DB: nohype
POSTGRES_USER: nohype
volumes:
- nohype_pg:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U nohype -d nohype"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
restart: unless-stopped
volumes:
nohype_pg:

View File

@@ -3,9 +3,7 @@ services:
build: . build: .
working_dir: /app working_dir: /app
command: > command: >
sh -c "python manage.py tailwind install --no-input && sh -c "python manage.py migrate --noinput &&
python manage.py tailwind build &&
python manage.py migrate --noinput &&
python manage.py seed_e2e_content && python manage.py seed_e2e_content &&
python manage.py runserver 0.0.0.0:8000" python manage.py runserver 0.0.0.0:8000"
volumes: volumes: