mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-14 12:08:04 +00:00
- Merge all robohash optimization approaches into ivatar/robohash.py - Remove feature flags and make optimization the default behavior - Eliminate multiple files (robohash_cached.py, robohash_optimized.py, robohash_fast.py) - Simplify implementation while maintaining excellent performance - Focus on result caching for maximum impact with minimal complexity Performance achievements: - 3.2x faster robohash generation overall (84ms → 26ms) - 133x faster with cache hits (0.61ms average) - 66.7% cache hit rate in typical usage - Reduced maintenance overhead with single implementation file - 100% visual compatibility maintained This consolidation makes robohash optimization the standard behavior without feature flags, providing significant performance improvements while keeping the codebase clean and maintainable.
339 lines
10 KiB
Python
339 lines
10 KiB
Python
"""
|
|
Configuration overrides for settings.py
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from django.urls import reverse_lazy
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.contrib.messages import constants as message_constants
|
|
from ivatar.settings import BASE_DIR
|
|
|
|
from ivatar.settings import MIDDLEWARE
|
|
from ivatar.settings import INSTALLED_APPS
|
|
from ivatar.settings import TEMPLATES
|
|
|
|
ADMIN_USERS = []
|
|
ALLOWED_HOSTS = ["*"]
|
|
|
|
INSTALLED_APPS.extend(
|
|
[
|
|
"django_extensions",
|
|
"django_openid_auth",
|
|
"bootstrap4",
|
|
"anymail",
|
|
"ivatar",
|
|
"ivatar.ivataraccount",
|
|
"ivatar.tools",
|
|
]
|
|
)
|
|
|
|
MIDDLEWARE.extend(
|
|
[
|
|
"ivatar.middleware.CustomLocaleMiddleware",
|
|
]
|
|
)
|
|
|
|
# Add OpenTelemetry middleware only if feature flag is enabled
|
|
# Note: This will be checked at runtime, not at import time
|
|
MIDDLEWARE.insert(
|
|
0,
|
|
"ivatar.middleware.MultipleProxyMiddleware",
|
|
)
|
|
|
|
AUTHENTICATION_BACKENDS = (
|
|
# Enable this to allow LDAP authentication.
|
|
# See INSTALL for more information.
|
|
# 'django_auth_ldap.backend.LDAPBackend',
|
|
"django_openid_auth.auth.OpenIDBackend",
|
|
"ivatar.ivataraccount.auth.FedoraOpenIdConnect",
|
|
"django.contrib.auth.backends.ModelBackend",
|
|
)
|
|
|
|
TEMPLATES[0]["DIRS"].extend(
|
|
[
|
|
os.path.join(BASE_DIR, "templates"),
|
|
]
|
|
)
|
|
TEMPLATES[0]["OPTIONS"]["context_processors"].append(
|
|
"ivatar.context_processors.basepage",
|
|
)
|
|
|
|
OPENID_CREATE_USERS = True
|
|
OPENID_UPDATE_DETAILS_FROM_SREG = True
|
|
SOCIAL_AUTH_JSONFIELD_ENABLED = True
|
|
# Fedora authentication (OIDC). You need to set these two values to use it.
|
|
SOCIAL_AUTH_FEDORA_KEY = None # Also known as client_id
|
|
SOCIAL_AUTH_FEDORA_SECRET = None # Also known as client_secret
|
|
|
|
SITE_NAME = os.environ.get("SITE_NAME", "libravatar")
|
|
IVATAR_VERSION = "1.8.0"
|
|
|
|
SCHEMAROOT = "https://www.libravatar.org/schemas/export/0.2"
|
|
|
|
SECURE_BASE_URL = os.environ.get(
|
|
"SECURE_BASE_URL", "https://avatars.linux-kernel.at/avatar/"
|
|
)
|
|
BASE_URL = os.environ.get("BASE_URL", "http://avatars.linux-kernel.at/avatar/")
|
|
|
|
LOGIN_REDIRECT_URL = reverse_lazy("profile")
|
|
MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294
|
|
|
|
MAX_NUM_PHOTOS = 5
|
|
MAX_NUM_UNCONFIRMED_EMAILS = 5
|
|
MAX_PHOTO_SIZE = 10485760 # in bytes
|
|
MAX_PIXELS = 7000
|
|
AVATAR_MAX_SIZE = 512
|
|
JPEG_QUALITY = 85
|
|
|
|
# Robohash Performance Optimization
|
|
# Enable optimized robohash implementation for 6-22x performance improvement
|
|
ROBOHASH_OPTIMIZATION_ENABLED = True
|
|
|
|
# Robohash Configuration
|
|
# Maximum number of robot parts to cache in memory (each ~50-200KB)
|
|
ROBOHASH_CACHE_SIZE = 150 # ~10-30MB total cache size
|
|
|
|
# Pagan Avatar Optimization
|
|
# Maximum number of pagan Avatar objects to cache in memory (each ~100-500KB)
|
|
PAGAN_CACHE_SIZE = 100 # ~10-50MB total cache size
|
|
|
|
# I'm not 100% sure if single character domains are possible
|
|
# under any tld... so MIN_LENGTH_EMAIL/_URL, might be +1
|
|
MIN_LENGTH_URL = 11 # eg. http://a.io
|
|
MAX_LENGTH_URL = 255 # MySQL can't handle more than that (LP: 1018682)
|
|
MIN_LENGTH_EMAIL = 6 # eg. x@x.xx
|
|
MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294
|
|
|
|
BOOTSTRAP4 = {
|
|
"include_jquery": False,
|
|
"javascript_in_head": False,
|
|
"css_url": {
|
|
"href": "/static/css/bootstrap.min.css",
|
|
"integrity": "sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB",
|
|
"crossorigin": "anonymous",
|
|
},
|
|
"javascript_url": {
|
|
"url": "/static/js/bootstrap.min.js",
|
|
"integrity": "",
|
|
"crossorigin": "anonymous",
|
|
},
|
|
"popper_url": {
|
|
"url": "/static/js/popper.min.js",
|
|
"integrity": "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49",
|
|
"crossorigin": "anonymous",
|
|
},
|
|
}
|
|
|
|
if "EMAIL_BACKEND" in os.environ:
|
|
EMAIL_BACKEND = os.environ["EMAIL_BACKEND"] # pragma: no cover
|
|
else:
|
|
if "test" in sys.argv or "collectstatic" in sys.argv:
|
|
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
else:
|
|
try:
|
|
ANYMAIL = { # pragma: no cover
|
|
"MAILGUN_API_KEY": os.environ["IVATAR_MAILGUN_API_KEY"],
|
|
"MAILGUN_SENDER_DOMAIN": os.environ["IVATAR_MAILGUN_SENDER_DOMAIN"],
|
|
}
|
|
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" # pragma: no cover
|
|
except Exception: # pragma: nocover # pylint: disable=broad-except
|
|
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
|
|
|
SERVER_EMAIL = os.environ.get("SERVER_EMAIL", "ivatar@mg.linux-kernel.at")
|
|
DEFAULT_FROM_EMAIL = os.environ.get("DEFAULT_FROM_EMAIL", "ivatar@mg.linux-kernel.at")
|
|
|
|
try:
|
|
from ivatar.settings import DATABASES
|
|
except ImportError: # pragma: no cover
|
|
DATABASES = [] # pragma: no cover
|
|
|
|
if "default" not in DATABASES:
|
|
DATABASES["default"] = { # pragma: no cover
|
|
"ENGINE": "django.db.backends.sqlite3",
|
|
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
|
}
|
|
|
|
if "MYSQL_DATABASE" in os.environ:
|
|
DATABASES["default"] = { # pragma: no cover
|
|
"ENGINE": "django.db.backends.mysql",
|
|
"NAME": os.environ["MYSQL_DATABASE"],
|
|
"USER": os.environ["MYSQL_USER"],
|
|
"PASSWORD": os.environ["MYSQL_PASSWORD"],
|
|
"HOST": "mysql",
|
|
}
|
|
|
|
if "POSTGRESQL_DATABASE" in os.environ:
|
|
DATABASES["default"] = { # pragma: no cover
|
|
"ENGINE": "django.db.backends.postgresql",
|
|
"NAME": os.environ["POSTGRESQL_DATABASE"],
|
|
"USER": os.environ["POSTGRESQL_USER"],
|
|
"PASSWORD": os.environ["POSTGRESQL_PASSWORD"],
|
|
"HOST": "postgresql",
|
|
}
|
|
|
|
# CI/CD config has different naming
|
|
if "POSTGRES_DB" in os.environ:
|
|
DATABASES["default"] = { # pragma: no cover
|
|
"ENGINE": "django.db.backends.postgresql",
|
|
"NAME": os.environ["POSTGRES_DB"],
|
|
"USER": os.environ["POSTGRES_USER"],
|
|
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
|
|
"HOST": os.environ["POSTGRES_HOST"],
|
|
# Let Django use its default test database naming
|
|
# "TEST": {
|
|
# "NAME": os.environ["POSTGRES_DB"],
|
|
# },
|
|
}
|
|
|
|
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
|
|
|
|
USE_X_FORWARDED_HOST = True
|
|
ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS = [
|
|
"avatars.linux-kernel.at",
|
|
"localhost",
|
|
]
|
|
|
|
DEFAULT_AVATAR_SIZE = 80
|
|
|
|
# Default settings for Gravatar proxy and redirect behavior
|
|
# These can be overridden by URL parameters
|
|
DEFAULT_GRAVATARPROXY = True
|
|
DEFAULT_GRAVATARREDIRECT = False
|
|
FORCEDEFAULT = False
|
|
|
|
LANGUAGES = (
|
|
("de", _("Deutsch")),
|
|
("en", _("English")),
|
|
("ca", _("Català")),
|
|
("cs", _("Česky")),
|
|
("es", _("Español")),
|
|
("eu", _("Basque")),
|
|
("fr", _("Français")),
|
|
("it", _("Italiano")),
|
|
("ja", _("日本語")),
|
|
("nl", _("Nederlands")),
|
|
("pt", _("Português")),
|
|
("ru", _("Русский")),
|
|
("sq", _("Shqip")),
|
|
("tr", _("Türkçe")),
|
|
("uk", _("Українська")),
|
|
)
|
|
|
|
MESSAGE_TAGS = {
|
|
message_constants.DEBUG: "debug",
|
|
message_constants.INFO: "info",
|
|
message_constants.SUCCESS: "success",
|
|
message_constants.WARNING: "warning",
|
|
message_constants.ERROR: "danger",
|
|
}
|
|
|
|
CACHES = {
|
|
"default": {
|
|
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
|
|
"LOCATION": [
|
|
"127.0.0.1:11211",
|
|
],
|
|
# "OPTIONS": {"MAX_ENTRIES": 1000000},
|
|
},
|
|
"filesystem": {
|
|
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
|
|
"LOCATION": "/var/tmp/ivatar_cache",
|
|
"TIMEOUT": 900, # 15 minutes
|
|
"OPTIONS": {"MAX_ENTRIES": 1000000},
|
|
},
|
|
}
|
|
|
|
# This is 5 minutes caching for generated/resized images,
|
|
# so the sites don't hit ivatar so much - it's what's set in the HTTP header
|
|
CACHE_IMAGES_MAX_AGE = 5 * 60
|
|
|
|
CACHE_RESPONSE = True
|
|
|
|
# Trusted URLs for default redirection
|
|
TRUSTED_DEFAULT_URLS = [
|
|
{"schemes": ["https"], "host_equals": "ui-avatars.com", "path_prefix": "/api/"},
|
|
{
|
|
"schemes": ["http", "https"],
|
|
"host_equals": "gravatar.com",
|
|
"path_prefix": "/avatar/",
|
|
},
|
|
{
|
|
"schemes": ["http", "https"],
|
|
"host_suffix": ".gravatar.com",
|
|
"path_prefix": "/avatar/",
|
|
},
|
|
{
|
|
"schemes": ["http", "https"],
|
|
"host_equals": "www.gravatar.org",
|
|
"path_prefix": "/avatar/",
|
|
},
|
|
{
|
|
"schemes": ["https"],
|
|
"host_equals": "avatars.dicebear.com",
|
|
"path_prefix": "/api/",
|
|
},
|
|
{
|
|
"schemes": ["https"],
|
|
"host_equals": "api.dicebear.com",
|
|
"path_prefix": "/",
|
|
},
|
|
{
|
|
"schemes": ["https"],
|
|
"host_equals": "badges.fedoraproject.org",
|
|
"path_prefix": "/static/img/",
|
|
},
|
|
{
|
|
"schemes": ["http"],
|
|
"host_equals": "www.planet-libre.org",
|
|
"path_prefix": "/themes/planetlibre/images/",
|
|
},
|
|
{"schemes": ["https"], "host_equals": "www.azuracast.com", "path_prefix": "/img/"},
|
|
{
|
|
"schemes": ["https"],
|
|
"host_equals": "reps.mozilla.org",
|
|
"path_prefix": "/static/base/img/remo/",
|
|
},
|
|
]
|
|
|
|
URL_TIMEOUT = 10
|
|
|
|
|
|
def map_legacy_config(trusted_url):
|
|
"""
|
|
For backward compability with the legacy configuration
|
|
for trusting URLs. Adapts them to fit the new config.
|
|
"""
|
|
if isinstance(trusted_url, str):
|
|
return {"url_prefix": trusted_url}
|
|
|
|
return trusted_url
|
|
|
|
|
|
# Backward compability for legacy behavior
|
|
TRUSTED_DEFAULT_URLS = list(map(map_legacy_config, TRUSTED_DEFAULT_URLS))
|
|
|
|
# Bluesky settings
|
|
BLUESKY_IDENTIFIER = os.environ.get("BLUESKY_IDENTIFIER", None)
|
|
BLUESKY_APP_PASSWORD = os.environ.get("BLUESKY_APP_PASSWORD", None)
|
|
|
|
# File upload security settings
|
|
FILE_UPLOAD_MAX_MEMORY_SIZE = 5 * 1024 * 1024 # 5MB
|
|
DATA_UPLOAD_MAX_MEMORY_SIZE = 5 * 1024 * 1024 # 5MB
|
|
FILE_UPLOAD_PERMISSIONS = 0o644
|
|
|
|
# Enhanced file upload security
|
|
ENABLE_FILE_SECURITY_VALIDATION = True
|
|
ENABLE_EXIF_SANITIZATION = True
|
|
ENABLE_MALICIOUS_CONTENT_SCAN = True
|
|
|
|
# Avatar optimization settings
|
|
PAGAN_CACHE_SIZE = 1000 # Number of pagan avatars to cache
|
|
|
|
# Logging configuration - can be overridden in local config
|
|
# Example: LOGS_DIR = "/var/log/ivatar" # For production deployments
|
|
|
|
# This MUST BE THE LAST!
|
|
if os.path.isfile(os.path.join(BASE_DIR, "config_local.py")):
|
|
from config_local import * # noqa # flake8: noqa # NOQA # pragma: no cover
|