fix: admin 500 after login (jazzmin static manifest bug)
- add app.storage.ManifestStaticFilesStorage tolerating directory static
refs (jazzmin renders {% static 'vendor/bootswatch' %}), still strict
for genuinely missing files
- point STORAGES staticfiles backend at it
- guard empty EMAIL_PORT/EMAIL_USE_TLS env values in production settings
- route admin under /backend/, use sqlite in production, add create_admin
- add pillow and django-tinymce deps
This commit is contained in:
9 files changed
+107
-25
No files matched your search
@@ -0,0 +1,29 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Create a superuser with pre-defined credentials"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
User = get_user_model()
|
||||
username = "udhamsilnepal@admin"
|
||||
password = "this_is_pass@udhamsilnepal"
|
||||
email = "info@udhamsilnepal.com.np"
|
||||
|
||||
if not User.objects.filter(username=username).exists():
|
||||
User.objects.create_superuser(
|
||||
username=username, email=email, password=password
|
||||
)
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f"Successfully created superuser: {username}")
|
||||
)
|
||||
else:
|
||||
user = User.objects.get(username=username)
|
||||
user.set_password(password)
|
||||
user.is_superuser = True
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f"Updated existing superuser: {username}")
|
||||
)
|
||||
@@ -115,7 +115,7 @@ STORAGES = {
|
||||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||
},
|
||||
"staticfiles": {
|
||||
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
|
||||
"BACKEND": "app.storage.ManifestStaticFilesStorage",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -3,52 +3,76 @@
|
||||
from .base import * # noqa: F401, F403
|
||||
|
||||
DEBUG = False
|
||||
SECRET_KEY = env('SECRET_KEY')
|
||||
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[])
|
||||
SECRET_KEY = env("SECRET_KEY")
|
||||
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[])
|
||||
|
||||
# Database
|
||||
# Using individual database parameters
|
||||
# DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.mysql',
|
||||
# 'PORT': env('DB_PORT', default='3306'),
|
||||
# 'NAME': env('DB_NAME'),
|
||||
# 'USER': env('DB_USER'),
|
||||
# 'PASSWORD': env('DB_PASSWORD'),
|
||||
# 'HOST': env('DB_HOST'),
|
||||
# }
|
||||
# }
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'PORT': env('DB_PORT', default='3306'),
|
||||
'NAME': env('DB_NAME'),
|
||||
'USER': env('DB_USER'),
|
||||
'PASSWORD': env('DB_PASSWORD'),
|
||||
'HOST': env('DB_HOST'),
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
"OPTIONS": {
|
||||
"transaction_mode": "IMMEDIATE",
|
||||
"timeout": 5, # seconds
|
||||
"init_command": """
|
||||
PRAGMA journal_mode=WAL;
|
||||
PRAGMA synchronous=NORMAL;
|
||||
PRAGMA mmap_size = 134217728;
|
||||
PRAGMA journal_size_limit = 27103364;
|
||||
PRAGMA cache_size=2000;
|
||||
""",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# CORS settings for production
|
||||
# Filter out empty strings from FRONTEND_URL
|
||||
CORS_ALLOWED_ORIGINS = env.list('FRONTEND_URL', default=[])
|
||||
CORS_ALLOWED_ORIGINS = env.list("FRONTEND_URL", default=[])
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
# Additional CORS security for production
|
||||
CORS_ALLOW_PRIVATE_NETWORK = False
|
||||
|
||||
# Email settings
|
||||
# Empty environment values (e.g. `EMAIL_PORT=` in .env) must fall back to the
|
||||
# defaults instead of crashing `env.int`/`env.bool` at settings import time.
|
||||
email_host = env("EMAIL_HOST", default="") or "smtp.gmail.com"
|
||||
email_port = int(env("EMAIL_PORT", default="") or 587)
|
||||
email_use_tls = env.bool("EMAIL_USE_TLS") if env("EMAIL_USE_TLS", default="") else True
|
||||
|
||||
MAILERS = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.mail.backends.smtp.EmailBackend',
|
||||
'OPTIONS': {
|
||||
'host': env('EMAIL_HOST', default='smtp.gmail.com'),
|
||||
'port': env.int('EMAIL_PORT', default=587),
|
||||
'use_tls': env.bool('EMAIL_USE_TLS', default=True),
|
||||
'username': env('EMAIL_HOST_USER', default=''),
|
||||
'password': env('EMAIL_HOST_PASSWORD', default=''),
|
||||
"default": {
|
||||
"BACKEND": "django.core.mail.backends.smtp.EmailBackend",
|
||||
"OPTIONS": {
|
||||
"host": email_host,
|
||||
"port": email_port,
|
||||
"use_tls": email_use_tls,
|
||||
"username": env("EMAIL_HOST_USER", default=""),
|
||||
"password": env("EMAIL_HOST_PASSWORD", default=""),
|
||||
},
|
||||
},
|
||||
}
|
||||
DEFAULT_FROM_EMAIL = env('EMAIL_HOST_USER', default='')
|
||||
SERVER_EMAIL = env('EMAIL_HOST_USER', default='')
|
||||
DEFAULT_FROM_EMAIL = env("EMAIL_HOST_USER", default="")
|
||||
SERVER_EMAIL = env("EMAIL_HOST_USER", default="")
|
||||
|
||||
# Security settings for production
|
||||
SECURE_SSL_REDIRECT = True
|
||||
SECURE_HSTS_SECONDS = 31536000
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||
SECURE_HSTS_PRELOAD = True
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||
|
||||
# Session settings
|
||||
SESSION_COOKIE_SECURE = True
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import os
|
||||
|
||||
from whitenoise.storage import CompressedManifestStaticFilesStorage
|
||||
|
||||
|
||||
class ManifestStaticFilesStorage(CompressedManifestStaticFilesStorage):
|
||||
"""WhiteNoise manifest storage that tolerates references to static directories.
|
||||
|
||||
django-jazzmin renders ``{% static 'vendor/bootswatch' %}`` (a directory) for
|
||||
its client-side theme base path. Directories have no manifest entry, so the
|
||||
stock ``ManifestStaticFilesStorage`` raises ``ValueError`` and every admin
|
||||
page 500s after login. Genuinely missing *files* still raise as usual.
|
||||
"""
|
||||
|
||||
def stored_name(self, name):
|
||||
try:
|
||||
return super().stored_name(name)
|
||||
except ValueError:
|
||||
_, extension = os.path.splitext(name)
|
||||
if not extension:
|
||||
return name
|
||||
raise
|
||||
+7
-2
@@ -4,10 +4,11 @@ from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path, re_path
|
||||
from django.views.static import serve
|
||||
|
||||
from app.views import index
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("backend/", admin.site.urls),
|
||||
path("tinymce/", include("tinymce.urls")),
|
||||
path("api/", include("app.api.urls")),
|
||||
]
|
||||
@@ -18,5 +19,9 @@ urlpatterns += [
|
||||
serve,
|
||||
{"document_root": settings.MEDIA_ROOT},
|
||||
),
|
||||
re_path(r"^(?!(?:api|admin|tinymce|media|static)(?:/|$)).*$", index, name="index"),
|
||||
re_path(
|
||||
r"^(?!(?:api|backend|admin|tinymce|media|static)(?:/|$)).*$",
|
||||
index,
|
||||
name="index",
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Whitespace-only changes.
@@ -8,3 +8,5 @@ mysqlclient>=2.2.8
|
||||
django-vite>=3.1.0
|
||||
gunicorn>=26.2.0
|
||||
django-filter>=26.1
|
||||
pillow>=12.3.0
|
||||
django-tinymce>=5.0.0
|
||||
+1
-1
@@ -1 +1 @@
|
||||
VITE_API_BASE_URL=http://localhost:8000/api
|
||||
VITE_API_BASE_URL=https://udhamsilnepal.com.np/api
|
||||
Reference in new issue
Block a user