-
-
Notifications
You must be signed in to change notification settings - Fork 307
Image optimization, lazy loading, and static asset performance improvements #5252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -101,16 +101,18 @@ | |
| "dj_rest_auth.registration", | ||
| "storages", | ||
| "channels", | ||
| "silk", | ||
| ) | ||
|
|
||
| if DEBUG: | ||
| INSTALLED_APPS += ("livereload",) | ||
| INSTALLED_APPS += ("debug_toolbar", "livereload") | ||
|
|
||
|
|
||
| SOCIAL_AUTH_GITHUB_KEY = os.environ.get("GITHUB_CLIENT_ID", "blank") | ||
| SOCIAL_AUTH_GITHUB_SECRET = os.environ.get("GITHUB_CLIENT_SECRET", "blank") | ||
|
|
||
|
|
||
| MIDDLEWARE = [ | ||
| "silk.middleware.SilkyMiddleware", | ||
| "django.contrib.sessions.middleware.SessionMiddleware", | ||
| "blt.middleware.domain.DomainMiddleware", | ||
| "django.middleware.locale.LocaleMiddleware", | ||
|
|
@@ -128,8 +130,10 @@ | |
| "blt.middleware.user_visit_tracking.VisitTrackingMiddleware", | ||
| ] | ||
|
|
||
|
|
||
| if DEBUG: | ||
| MIDDLEWARE += ("livereload.middleware.LiveReloadScript",) | ||
| MIDDLEWARE += ("livereload.middleware.LiveReloadScript", "debug_toolbar.middleware.DebugToolbarMiddleware") | ||
| INTERNAL_IPS = ["127.0.0.1"] | ||
|
|
||
| BLUESKY_USERNAME = env("BLUESKY_USERNAME", default="default_username") | ||
| BLUESKY_PASSWORD = env("BLUESKY_PASSWORD", default="default_password") | ||
|
|
@@ -156,46 +160,56 @@ | |
| "SHOW_TOOLBAR_CALLBACK": lambda request: True, | ||
| } | ||
|
|
||
| INSTALLED_APPS += ("debug_toolbar",) | ||
|
|
||
| MIDDLEWARE += ("debug_toolbar.middleware.DebugToolbarMiddleware",) | ||
|
|
||
| ROOT_URLCONF = "blt.urls" | ||
|
|
||
| TEMPLATES = [ | ||
| { | ||
| "BACKEND": "django.template.backends.django.DjangoTemplates", | ||
| "DIRS": [os.path.join(BASE_DIR, "website", "templates")], | ||
| "APP_DIRS": False, | ||
| "OPTIONS": { | ||
| "debug": DEBUG, | ||
| "context_processors": [ | ||
| "django.template.context_processors.debug", | ||
| "django.template.context_processors.request", | ||
| "django.template.context_processors.media", | ||
| "django.contrib.auth.context_processors.auth", | ||
| "django.contrib.messages.context_processors.messages", | ||
| "django.template.context_processors.i18n", | ||
| ], | ||
| "loaders": ( | ||
| [ | ||
| "django.template.loaders.filesystem.Loader", | ||
| "django.template.loaders.app_directories.Loader", | ||
| ] | ||
| if DEBUG | ||
| else [ | ||
| if DEBUG: | ||
| TEMPLATES = [ | ||
| { | ||
| "BACKEND": "django.template.backends.django.DjangoTemplates", | ||
| "DIRS": [os.path.join(BASE_DIR, "website", "templates")], | ||
| "APP_DIRS": True, | ||
| "OPTIONS": { | ||
| "debug": True, | ||
| "context_processors": [ | ||
| "django.template.context_processors.debug", | ||
| "django.template.context_processors.request", | ||
| "django.template.context_processors.media", | ||
| "django.contrib.auth.context_processors.auth", | ||
| "django.contrib.messages.context_processors.messages", | ||
| "django.template.context_processors.i18n", | ||
| ], | ||
| }, | ||
| } | ||
| ] | ||
| else: | ||
| TEMPLATES = [ | ||
| { | ||
| "BACKEND": "django.template.backends.django.DjangoTemplates", | ||
| "DIRS": [os.path.join(BASE_DIR, "website", "templates")], | ||
| "APP_DIRS": False, | ||
| "OPTIONS": { | ||
| "debug": False, | ||
| "loaders": [ | ||
| ( | ||
| "django.template.loaders.cached.Loader", | ||
| [ | ||
| "django.template.loaders.filesystem.Loader", | ||
| "django.template.loaders.app_directories.Loader", | ||
| ], | ||
| ), | ||
| ] | ||
| ), | ||
| }, | ||
| }, | ||
| ] | ||
| ) | ||
| ], | ||
| "context_processors": [ | ||
| "django.template.context_processors.debug", | ||
| "django.template.context_processors.request", | ||
| "django.template.context_processors.media", | ||
| "django.contrib.auth.context_processors.auth", | ||
| "django.contrib.messages.context_processors.messages", | ||
| "django.template.context_processors.i18n", | ||
| ], | ||
| }, | ||
| } | ||
| ] | ||
|
|
||
|
|
||
| AUTHENTICATION_BACKENDS = ( | ||
| "django.contrib.auth.backends.ModelBackend", | ||
|
|
@@ -363,6 +377,7 @@ | |
| ALLOWED_HOSTS = ["*"] | ||
|
|
||
| STATIC_ROOT = os.path.join(PROJECT_ROOT, "staticfiles") | ||
| STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" | ||
|
Comment on lines
379
to
+380
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Short answer: In Django 4.2+ (including 5.2) STATICFILES_STORAGE is deprecated in favour of the new STORAGES mapping. Django will use the storage configured under STORAGES["staticfiles"] as the staticfiles backend; STATICFILES_STORAGE is only used as the deprecated fallback when no STORAGES["staticfiles"] config is provided. See the STORAGES note in the 4.2 release notes and the SETTINGS docs. [1][2] Sources
🏁 Script executed: fd -t f "settings.py" | head -5Repository: OWASP-BLT/BLT Length of output: 121 🏁 Script executed: sed -n '308,334p' blt/settings.py
sed -n '375,385p' blt/settings.pyRepository: OWASP-BLT/BLT Length of output: 1135 Remove deprecated In Django 4.2+, To enable WhiteNoise compressed static files storage, remove the deprecated setting and update both -STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"Update the GCS STORAGES block (around line 316): "staticfiles": {
- "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
+ "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
},Update the non-GCS STORAGES block (around line 331): "staticfiles": {
- "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
+ "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
},
|
||
| STATIC_URL = "/static/" | ||
|
|
||
| STATICFILES_DIRS = (os.path.join(BASE_DIR, "website", "static"),) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1261,3 +1261,9 @@ | |||||||||||||||||||||
| ] + urlpatterns | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if settings.DEBUG: | ||||||||||||||||||||||
| urlpatterns += [ | ||||||||||||||||||||||
| path("silk/", include("silk.urls", namespace="silk")), | ||||||||||||||||||||||
| path("__debug__/", include("debug_toolbar.urls")), | ||||||||||||||||||||||
| ] | ||||||||||||||||||||||
|
Comment on lines
+1264
to
+1269
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate debug_toolbar URL registration. The Remove the duplicate debug_toolbar registration and keep only the silk URLs: if settings.DEBUG:
urlpatterns += [
path("silk/", include("silk.urls", namespace="silk")),
- path("__debug__/", include("debug_toolbar.urls")),
]📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,8 @@ tweepy = "^4.15.0" | |
| better-profanity = "^0.7.0" | ||
| django-bleach = "^3.1.0" | ||
| pyjwt = {extras = ["crypto"], version = "^2.0.0"} | ||
| django-silk = "^5.0.1" | ||
| django-debug-toolbar = "^4.3.0" | ||
|
Comment on lines
+73
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Development tools added to production dependencies.
Move these to dev dependencies and remove the duplicate: pyjwt = {extras = ["crypto"], version = "^2.0.0"}
-django-silk = "^5.0.1"
-django-debug-toolbar = "^4.3.0"In the dev dependencies section, add silk: [tool.poetry.group.dev.dependencies]
black = "^25.12.0"
isort = "^5.13.2"
ruff = "^0.14.8"
pre-commit = "^4.5.0"
selenium = "^4.39.0"
webdriver-manager = "^4.0.2"
chromedriver-autoinstaller = "^0.6.4"
django-debug-toolbar = "^4.4.6"
django-livereload-server = "^0.5.1"
watchfiles = "^1.0.4"
+django-silk = "^5.0.1"
🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| [tool.poetry.group.dev.dependencies] | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||||||||||||||||
| # scripts/convert_images_to_webp.py | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import shutil | ||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from PIL import Image | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Directories to process | ||||||||||||||||||||
| SOURCE_DIRS = [ | ||||||||||||||||||||
| "website/static/images", | ||||||||||||||||||||
| "website/static/img", | ||||||||||||||||||||
| "website/static/img/browser-logos", | ||||||||||||||||||||
| ] | ||||||||||||||||||||
|
Comment on lines
+9
to
+13
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overlapping directories will cause duplicate processing.
Remove the subdirectory from SOURCE_DIRS: SOURCE_DIRS = [
"website/static/images",
"website/static/img",
- "website/static/img/browser-logos",
]📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| BACKUP_DIR = "website/static/_image_backups" | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def ensure_backup_dir(): | ||||||||||||||||||||
| Path(BACKUP_DIR).mkdir(parents=True, exist_ok=True) | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def convert_png(png_path: Path): | ||||||||||||||||||||
| try: | ||||||||||||||||||||
| webp_path = png_path.with_suffix(".webp") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Skip if already converted | ||||||||||||||||||||
| if webp_path.exists(): | ||||||||||||||||||||
| return | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Backup original | ||||||||||||||||||||
| rel = png_path.relative_to("website/static") | ||||||||||||||||||||
| backup_path = Path(BACKUP_DIR) / rel | ||||||||||||||||||||
| backup_path.parent.mkdir(parents=True, exist_ok=True) | ||||||||||||||||||||
| shutil.copy2(png_path, backup_path) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Convert to WebP | ||||||||||||||||||||
| img = Image.open(png_path).convert("RGBA") | ||||||||||||||||||||
| img.save(webp_path, "webp", quality=85, method=6) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Optimize original PNG (overwrite) | ||||||||||||||||||||
| img.save(png_path, optimize=True) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| except Exception: | ||||||||||||||||||||
| # Silent failure by design (CI-safe) | ||||||||||||||||||||
| pass | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def main(): | ||||||||||||||||||||
| ensure_backup_dir() | ||||||||||||||||||||
| for d in SOURCE_DIRS: | ||||||||||||||||||||
| p = Path(d) | ||||||||||||||||||||
| if not p.exists(): | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| for png in p.rglob("*.png"): | ||||||||||||||||||||
| convert_png(png) | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| if __name__ == "__main__": | ||||||||||||||||||||
| main() | ||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silk enabled unconditionally in production.
"silk"is added toINSTALLED_APPSoutside theDEBUGblock, meaning it will be installed and active in production. This adds unnecessary overhead and potential security exposure.Move silk inside the DEBUG condition:
📝 Committable suggestion
🤖 Prompt for AI Agents