mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-03-22 23:16:02 -04:00
Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols. - Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage. - Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
This commit is contained in:
@@ -1,28 +1,202 @@
|
||||
"""
|
||||
Django admin configuration for the Media (shared) application.
|
||||
|
||||
This module provides admin interfaces for photo management with
|
||||
bulk operations and content type linking.
|
||||
|
||||
Performance targets:
|
||||
- List views: < 10 queries
|
||||
- Page load time: < 500ms for 100 records
|
||||
"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
|
||||
from .models import Photo
|
||||
|
||||
|
||||
@admin.register(Photo)
|
||||
class PhotoAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Admin interface for Photo management.
|
||||
|
||||
Provides photo administration with:
|
||||
- Thumbnail previews in list view
|
||||
- Content type linking
|
||||
- Bulk primary photo management
|
||||
- Alt text validation warnings
|
||||
|
||||
Query optimizations:
|
||||
- select_related: content_type
|
||||
"""
|
||||
|
||||
list_display = (
|
||||
"thumbnail_preview",
|
||||
"content_type",
|
||||
"content_object",
|
||||
"caption",
|
||||
"is_primary",
|
||||
"content_type_display",
|
||||
"content_object_link",
|
||||
"caption_preview",
|
||||
"is_primary_badge",
|
||||
"has_alt_text",
|
||||
"created_at",
|
||||
)
|
||||
list_filter = ("content_type", "is_primary", "created_at")
|
||||
search_fields = ("caption", "alt_text")
|
||||
readonly_fields = ("thumbnail_preview",)
|
||||
list_select_related = ["content_type"]
|
||||
search_fields = ("caption", "alt_text", "object_id")
|
||||
readonly_fields = ("thumbnail_preview", "created_at", "updated_at")
|
||||
list_per_page = 50
|
||||
show_full_result_count = False
|
||||
ordering = ("-created_at",)
|
||||
|
||||
fieldsets = (
|
||||
(
|
||||
"Image",
|
||||
{
|
||||
"fields": ("image", "thumbnail_preview"),
|
||||
"description": "Upload and preview the photo.",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Related Object",
|
||||
{
|
||||
"fields": ("content_type", "object_id"),
|
||||
"description": "The object this photo belongs to (park, ride, etc.).",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Photo Details",
|
||||
{
|
||||
"fields": ("caption", "alt_text", "is_primary"),
|
||||
"description": "Caption and accessibility information.",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Metadata",
|
||||
{
|
||||
"fields": ("created_at", "updated_at"),
|
||||
"classes": ("collapse",),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@admin.display(description="Preview")
|
||||
def thumbnail_preview(self, obj):
|
||||
"""Display thumbnail preview of the photo."""
|
||||
if obj.image:
|
||||
return format_html(
|
||||
'<img src="{}" style="max-height: 50px; max-width: 100px;" />',
|
||||
'<img src="{}" style="max-height: 60px; max-width: 100px; '
|
||||
'border-radius: 4px; object-fit: cover;" loading="lazy" />',
|
||||
obj.image.url,
|
||||
)
|
||||
return "No image"
|
||||
return format_html('<span style="color: gray;">No image</span>')
|
||||
|
||||
thumbnail_preview.short_description = "Thumbnail"
|
||||
@admin.display(description="Type")
|
||||
def content_type_display(self, obj):
|
||||
"""Display content type in a readable format."""
|
||||
if obj.content_type:
|
||||
return f"{obj.content_type.app_label}.{obj.content_type.model}"
|
||||
return "-"
|
||||
|
||||
@admin.display(description="Object")
|
||||
def content_object_link(self, obj):
|
||||
"""Create a link to the related object's admin page."""
|
||||
try:
|
||||
content_obj = obj.content_object
|
||||
if content_obj:
|
||||
from django.urls import reverse
|
||||
|
||||
app_label = obj.content_type.app_label
|
||||
model_name = obj.content_type.model
|
||||
try:
|
||||
url = reverse(
|
||||
f"admin:{app_label}_{model_name}_change",
|
||||
args=[content_obj.pk],
|
||||
)
|
||||
return format_html(
|
||||
'<a href="{}">{}</a>',
|
||||
url,
|
||||
str(content_obj)[:30],
|
||||
)
|
||||
except Exception:
|
||||
return str(content_obj)[:30]
|
||||
return "-"
|
||||
except Exception:
|
||||
return format_html('<span style="color: red;">Not found</span>')
|
||||
|
||||
@admin.display(description="Caption")
|
||||
def caption_preview(self, obj):
|
||||
"""Display truncated caption."""
|
||||
if obj.caption:
|
||||
return obj.caption[:40] + "..." if len(obj.caption) > 40 else obj.caption
|
||||
return "-"
|
||||
|
||||
@admin.display(description="Primary")
|
||||
def is_primary_badge(self, obj):
|
||||
"""Display primary status with badge."""
|
||||
if obj.is_primary:
|
||||
return format_html(
|
||||
'<span style="background-color: green; color: white; padding: 2px 8px; '
|
||||
'border-radius: 4px; font-size: 11px;">Primary</span>'
|
||||
)
|
||||
return format_html(
|
||||
'<span style="background-color: gray; color: white; padding: 2px 8px; '
|
||||
'border-radius: 4px; font-size: 11px;">-</span>'
|
||||
)
|
||||
|
||||
@admin.display(description="Alt", boolean=True)
|
||||
def has_alt_text(self, obj):
|
||||
"""Indicate if photo has alt text for accessibility."""
|
||||
return bool(obj.alt_text)
|
||||
|
||||
@admin.action(description="Set as primary photo")
|
||||
def set_primary(self, request, queryset):
|
||||
"""Set selected photos as primary for their objects."""
|
||||
for photo in queryset:
|
||||
# Unset other primary photos for the same object
|
||||
Photo.objects.filter(
|
||||
content_type=photo.content_type,
|
||||
object_id=photo.object_id,
|
||||
is_primary=True,
|
||||
).exclude(pk=photo.pk).update(is_primary=False)
|
||||
photo.is_primary = True
|
||||
photo.save(update_fields=["is_primary"])
|
||||
self.message_user(request, f"Set {queryset.count()} photos as primary.")
|
||||
|
||||
@admin.action(description="Remove primary status")
|
||||
def remove_primary(self, request, queryset):
|
||||
"""Remove primary status from selected photos."""
|
||||
updated = queryset.update(is_primary=False)
|
||||
self.message_user(request, f"Removed primary status from {updated} photos.")
|
||||
|
||||
@admin.action(description="Flag missing alt text")
|
||||
def flag_missing_alt(self, request, queryset):
|
||||
"""List photos missing alt text for accessibility."""
|
||||
missing = queryset.filter(alt_text__isnull=True) | queryset.filter(alt_text="")
|
||||
count = missing.count()
|
||||
if count:
|
||||
self.message_user(
|
||||
request,
|
||||
f"Found {count} photos missing alt text. Please add alt text for accessibility.",
|
||||
level="WARNING",
|
||||
)
|
||||
else:
|
||||
self.message_user(request, "All selected photos have alt text.")
|
||||
|
||||
def get_actions(self, request):
|
||||
"""Add custom actions to the admin."""
|
||||
actions = super().get_actions(request)
|
||||
actions["set_primary"] = (
|
||||
self.set_primary,
|
||||
"set_primary",
|
||||
"Set as primary photo",
|
||||
)
|
||||
actions["remove_primary"] = (
|
||||
self.remove_primary,
|
||||
"remove_primary",
|
||||
"Remove primary status",
|
||||
)
|
||||
actions["flag_missing_alt"] = (
|
||||
self.flag_missing_alt,
|
||||
"flag_missing_alt",
|
||||
"Flag missing alt text",
|
||||
)
|
||||
return actions
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[GITHUB-TOKEN-REMOVED]
|
||||
@@ -1,203 +0,0 @@
|
||||
# ThrillWiki Automation Service Environment Configuration
|
||||
# Copy this file to thrillwiki-automation***REMOVED*** and customize for your environment
|
||||
#
|
||||
# Security Note: This file should have restricted permissions (600) as it may contain
|
||||
# sensitive information like GitHub Personal Access Tokens
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# PROJECT CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Base project directory (usually auto-detected)
|
||||
# PROJECT_DIR=/home/ubuntu/thrillwiki
|
||||
|
||||
# Service name for systemd integration
|
||||
# SERVICE_NAME=thrillwiki
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# GITHUB REPOSITORY CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# GitHub repository remote name
|
||||
# GITHUB_REPO=origin
|
||||
|
||||
# Branch to pull from
|
||||
# GITHUB_BRANCH=main
|
||||
|
||||
# GitHub Personal Access Token (PAT) - Required for private repositories
|
||||
# Generate at: https://github.com/settings/tokens
|
||||
# Required permissions: repo (Full control of private repositories)
|
||||
# GITHUB_TOKEN=ghp_your_personal_access_token_here
|
||||
|
||||
# GitHub token file location (alternative to GITHUB_TOKEN)
|
||||
# GITHUB_TOKEN_FILE=/home/ubuntu/thrillwiki/.github-pat
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# AUTOMATION TIMING CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Repository pull interval in seconds (default: 300 = 5 minutes)
|
||||
# PULL_INTERVAL=300
|
||||
|
||||
# Health check interval in seconds (default: 60 = 1 minute)
|
||||
# HEALTH_CHECK_INTERVAL=60
|
||||
|
||||
# Server startup timeout in seconds (default: 120 = 2 minutes)
|
||||
# STARTUP_TIMEOUT=120
|
||||
|
||||
# Restart delay after failure in seconds (default: 10)
|
||||
# RESTART_DELAY=10
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# LOGGING CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Log directory (default: project_dir/logs)
|
||||
# LOG_DIR=/home/ubuntu/thrillwiki/logs
|
||||
|
||||
# Log file path
|
||||
# LOG_[AWS-SECRET-REMOVED]proof-automation.log
|
||||
|
||||
# Maximum log file size in bytes (default: 10485760 = 10MB)
|
||||
# MAX_LOG_SIZE=10485760
|
||||
|
||||
# Lock file location to prevent multiple instances
|
||||
# LOCK_FILE=/tmp/thrillwiki-bulletproof.lock
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# DEVELOPMENT SERVER CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Server host address (default: 0.0.0.0 for all interfaces)
|
||||
# SERVER_HOST=0.0.0.0
|
||||
|
||||
# Server port (default: 8000)
|
||||
# SERVER_PORT=8000
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# DJANGO CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Django settings module
|
||||
# DJANGO_SETTINGS_MODULE=thrillwiki.settings
|
||||
|
||||
# Python path
|
||||
# PYTHONPATH=/home/ubuntu/thrillwiki
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# ADVANCED CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# GitHub authentication script location
|
||||
# GITHUB_AUTH_[AWS-SECRET-REMOVED]ithub-auth.py
|
||||
|
||||
# Enable verbose logging (true/false)
|
||||
# VERBOSE_LOGGING=false
|
||||
|
||||
# Enable debug mode for troubleshooting (true/false)
|
||||
# DEBUG_MODE=false
|
||||
|
||||
# Custom git remote URL (overrides GITHUB_REPO if set)
|
||||
# CUSTOM_GIT_REMOTE=https://github.com/username/repository.git
|
||||
|
||||
# Email notifications for critical failures (requires email configuration)
|
||||
# NOTIFICATION_EMAIL=admin@example.com
|
||||
|
||||
# Maximum consecutive failures before alerting (default: 5)
|
||||
# MAX_CONSECUTIVE_FAILURES=5
|
||||
|
||||
# Enable automatic dependency updates (true/false, default: true)
|
||||
# AUTO_UPDATE_DEPENDENCIES=true
|
||||
|
||||
# Enable automatic migrations on code changes (true/false, default: true)
|
||||
# AUTO_MIGRATE=true
|
||||
|
||||
# Enable automatic static file collection (true/false, default: true)
|
||||
# AUTO_COLLECTSTATIC=true
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# SECURITY CONFIGURATION
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# GitHub authentication method (token|ssh|https)
|
||||
# Default: token (uses GITHUB_TOKEN or GITHUB_TOKEN_FILE)
|
||||
# GITHUB_AUTH_METHOD=token
|
||||
|
||||
# SSH key path for git operations (when using ssh auth method)
|
||||
# SSH_KEY_PATH=/home/ubuntu/.ssh/***REMOVED***
|
||||
|
||||
# Git user configuration for commits
|
||||
# GIT_USER_NAME="ThrillWiki Automation"
|
||||
# GIT_USER_EMAIL="automation@thrillwiki.local"
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# MONITORING AND HEALTH CHECKS
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Health check URL to verify server is running
|
||||
# HEALTH_CHECK_URL=http://localhost:8000/health/
|
||||
|
||||
# Health check timeout in seconds
|
||||
# HEALTH_CHECK_TIMEOUT=30
|
||||
|
||||
# Enable system resource monitoring (true/false)
|
||||
# MONITOR_RESOURCES=true
|
||||
|
||||
# Memory usage threshold for warnings (in MB)
|
||||
# MEMORY_WARNING_THRESHOLD=1024
|
||||
|
||||
# CPU usage threshold for warnings (percentage)
|
||||
# CPU_WARNING_THRESHOLD=80
|
||||
|
||||
# Disk usage threshold for warnings (percentage)
|
||||
# DISK_WARNING_THRESHOLD=90
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# INTEGRATION SETTINGS
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Webhook integration (if using thrillwiki-webhook service)
|
||||
# WEBHOOK_INTEGRATION=true
|
||||
|
||||
# Slack webhook URL for notifications (optional)
|
||||
# SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook/url
|
||||
|
||||
# Discord webhook URL for notifications (optional)
|
||||
# DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/your/webhook/url
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# USAGE EXAMPLES
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# Example 1: Basic setup with GitHub PAT
|
||||
# GITHUB_TOKEN=ghp_your_token_here
|
||||
# PULL_INTERVAL=300
|
||||
# AUTO_MIGRATE=true
|
||||
|
||||
# Example 2: Enhanced monitoring setup
|
||||
# HEALTH_CHECK_INTERVAL=30
|
||||
# MONITOR_RESOURCES=true
|
||||
# NOTIFICATION_EMAIL=admin@thrillwiki.com
|
||||
# SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook
|
||||
|
||||
# Example 3: Development environment with frequent pulls
|
||||
# PULL_INTERVAL=60
|
||||
# DEBUG_MODE=true
|
||||
# VERBOSE_LOGGING=true
|
||||
# AUTO_UPDATE_DEPENDENCIES=true
|
||||
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
# INSTALLATION NOTES
|
||||
# [AWS-SECRET-REMOVED]====================================
|
||||
|
||||
# 1. Copy this file: cp thrillwiki-automation***REMOVED***.example thrillwiki-automation***REMOVED***
|
||||
# 2. Set secure permissions: chmod 600 thrillwiki-automation***REMOVED***
|
||||
# 3. Customize the settings above for your environment
|
||||
# 4. Enable the service: sudo systemctl enable thrillwiki-automation
|
||||
# 5. Start the service: sudo systemctl start thrillwiki-automation
|
||||
# 6. Check status: sudo systemctl status thrillwiki-automation
|
||||
# 7. View logs: sudo journalctl -u thrillwiki-automation -f
|
||||
|
||||
# For security, ensure only the ubuntu user can read this file:
|
||||
# sudo chown ubuntu:ubuntu thrillwiki-automation***REMOVED***
|
||||
# sudo chmod 600 thrillwiki-automation***REMOVED***
|
||||
Reference in New Issue
Block a user