initial commit
This commit is contained in:
1
config/services/__init__.py
Normal file
1
config/services/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Service configuration modules."""
|
||||
1
config/services/auditlog.py
Normal file
1
config/services/auditlog.py
Normal file
@@ -0,0 +1 @@
|
||||
AUDITLOG_INCLUDE_ALL_MODELS = True
|
||||
9
config/services/celery.py
Normal file
9
config/services/celery.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import os
|
||||
|
||||
from celery import Celery
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
||||
|
||||
app = Celery("qlockify")
|
||||
app.config_from_object("django.conf:settings", namespace="CELERY")
|
||||
app.autodiscover_tasks()
|
||||
244
config/services/logging.py
Normal file
244
config/services/logging.py
Normal file
@@ -0,0 +1,244 @@
|
||||
import logging
|
||||
import logging.config
|
||||
import time
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
|
||||
from pythonjsonlogger import jsonlogger
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parents[2]
|
||||
LOG_DIR = BASE_DIR / "logs"
|
||||
LOG_DIR.mkdir(exist_ok=True)
|
||||
|
||||
|
||||
# Custom formatter for console output (ERROR logs)
|
||||
class CustomConsoleFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
if isinstance(record.msg, dict):
|
||||
msg_dict = record.msg
|
||||
return (
|
||||
f"{record.levelname} {self.formatTime(record, self.datefmt)} "
|
||||
f"{msg_dict.get('request_method', '')} "
|
||||
f"{msg_dict.get('request_url', '')} "
|
||||
f"{msg_dict.get('status_code', '')} "
|
||||
f"{msg_dict.get('remote_addr', '')} "
|
||||
f"{msg_dict.get('duration_ms', 0)}ms "
|
||||
f"{msg_dict.get('message', record.getMessage())}"
|
||||
)
|
||||
|
||||
return super().format(record)
|
||||
|
||||
|
||||
# Custom formatter for INFO console output (simpler format)
|
||||
class CustomConsoleInfoFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
if isinstance(record.msg, dict):
|
||||
msg_dict = record.msg
|
||||
return (
|
||||
f"{record.levelname} {self.formatTime(record, self.datefmt)} "
|
||||
f"{msg_dict.get('request_method', '')} "
|
||||
f"{msg_dict.get('request_url', '')} "
|
||||
f"{msg_dict.get('status_code', '')} "
|
||||
f"{msg_dict.get('remote_addr', '')} "
|
||||
f"{msg_dict.get('duration_ms', 0)}ms"
|
||||
)
|
||||
|
||||
return super().format(record)
|
||||
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": True,
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "{levelname} {asctime} {name} {module} {funcName} {lineno} {message}",
|
||||
"style": "{",
|
||||
},
|
||||
"json": {
|
||||
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
|
||||
"format": "%(levelname)s %(asctime)s %(name)s %(module)s %(funcName)s %(lineno)d %(message)s %(pathname)s",
|
||||
},
|
||||
"request_formatter": {
|
||||
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
|
||||
"format": "%(levelname)s %(asctime)s %(name)s %(message)s %(pathname)s %(lineno)d %(request_method)s %(request_url)s %(status_code)d %(remote_addr)s %(user_agent)s %(duration_ms)d", # noqa: E501
|
||||
},
|
||||
# Custom formatters for console
|
||||
"error_console": {
|
||||
"()": CustomConsoleFormatter,
|
||||
"format": "%(levelname)s %(asctime)s %(name)s %(message)s",
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"info_console": {
|
||||
"()": CustomConsoleInfoFormatter,
|
||||
"format": "%(levelname)s %(asctime)s %(name)s %(message)s",
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
},
|
||||
"error_console": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "error_console",
|
||||
},
|
||||
"info_console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "info_console",
|
||||
},
|
||||
"file": {
|
||||
"level": "INFO",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": str(LOG_DIR / "django.log"),
|
||||
"maxBytes": 1024 * 1024 * 15, # 15MB
|
||||
"backupCount": 10,
|
||||
"formatter": "json",
|
||||
},
|
||||
"error_file": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": str(LOG_DIR / "errors.log"),
|
||||
"maxBytes": 1024 * 1024 * 15, # 15MB
|
||||
"backupCount": 5,
|
||||
"formatter": "json",
|
||||
},
|
||||
"debug_file": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": str(LOG_DIR / "debug.log"),
|
||||
"maxBytes": 1024 * 1024 * 15, # 15MB
|
||||
"backupCount": 5,
|
||||
"formatter": "verbose",
|
||||
},
|
||||
"request_info_file": {
|
||||
"level": "INFO",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": str(LOG_DIR / "info.log"),
|
||||
"maxBytes": 1024 * 1024 * 15, # 15MB
|
||||
"backupCount": 10,
|
||||
"formatter": "request_formatter",
|
||||
},
|
||||
"request_error_file": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": str(LOG_DIR / "errors.log"),
|
||||
"maxBytes": 1024 * 1024 * 15, # 15MB
|
||||
"backupCount": 10,
|
||||
"formatter": "request_formatter",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"django.request": {
|
||||
"handlers": ["error_console", "error_file"],
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
"django.server": {
|
||||
"handlers": ["error_console"],
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
"myapp": {
|
||||
"handlers": ["console", "file", "debug_file", "error_file"],
|
||||
"level": "DEBUG",
|
||||
"propagate": False,
|
||||
},
|
||||
"project.requests.info": {
|
||||
"handlers": [
|
||||
"request_info_file",
|
||||
"info_console",
|
||||
], # Use info_console for INFO
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
"project.requests.error": {
|
||||
"handlers": [
|
||||
"request_error_file",
|
||||
"error_console",
|
||||
], # Use error_console for ERROR
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
"common.exceptions": {
|
||||
"handlers": ["error_console", "error_file", "debug_file"],
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
"common.middleware": {
|
||||
"handlers": ["error_console", "error_file", "debug_file"],
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class RequestLoggingMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
self.info_logger = logging.getLogger("project.requests.info")
|
||||
self.error_logger = logging.getLogger("project.requests.error")
|
||||
|
||||
def __call__(self, request):
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
response = self.get_response(request)
|
||||
except Exception:
|
||||
duration_ms = int((time.perf_counter() - start) * 1000)
|
||||
message = f"HTTP {request.method} request to {request.get_full_path()}"
|
||||
log_data = {
|
||||
"request_method": request.method,
|
||||
"request_url": request.get_full_path(),
|
||||
"status_code": 500,
|
||||
"remote_addr": request.META.get("REMOTE_ADDR"),
|
||||
"user_agent": request.META.get("HTTP_USER_AGENT", ""),
|
||||
"duration_ms": duration_ms,
|
||||
"message": message,
|
||||
}
|
||||
self.error_logger.exception(log_data)
|
||||
raise
|
||||
|
||||
duration_ms = int((time.perf_counter() - start) * 1000)
|
||||
message = f"HTTP {request.method} request to {request.get_full_path()}"
|
||||
log_data = {
|
||||
"request_method": request.method,
|
||||
"request_url": request.get_full_path(),
|
||||
"status_code": response.status_code,
|
||||
"remote_addr": request.META.get("REMOTE_ADDR"),
|
||||
"user_agent": request.META.get("HTTP_USER_AGENT", ""),
|
||||
"duration_ms": duration_ms,
|
||||
"message": message,
|
||||
}
|
||||
|
||||
if response.status_code == 404:
|
||||
log_data["message"] = f"Not Found: {request.get_full_path()}"
|
||||
elif response.status_code == 401:
|
||||
log_data["message"] = f"Unauthorized: {request.get_full_path()}"
|
||||
|
||||
if response.status_code >= 400:
|
||||
self.error_logger.error(log_data)
|
||||
else:
|
||||
self.info_logger.info(log_data)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def get_custom_logger(name):
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
custom_handler = RotatingFileHandler(
|
||||
filename=str(LOG_DIR / f"{name}.log"),
|
||||
maxBytes=1024 * 1024 * 15, # 15MB
|
||||
backupCount=10,
|
||||
)
|
||||
custom_handler.setFormatter(jsonlogger.JsonFormatter())
|
||||
if not logger.handlers:
|
||||
logger.addHandler(custom_handler)
|
||||
return logger
|
||||
|
||||
|
||||
logging.config.dictConfig(LOGGING)
|
||||
15
config/services/storage.py
Normal file
15
config/services/storage.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import os
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
|
||||
|
||||
class UploadStorage(FileSystemStorage):
|
||||
"""
|
||||
Storage for uploaded files (images, etc.)
|
||||
Saves files under MEDIA_ROOT/uploads/
|
||||
"""
|
||||
|
||||
location = os.path.join(settings.MEDIA_ROOT, "uploads")
|
||||
base_url = urljoin(settings.MEDIA_URL, "uploads/")
|
||||
46
config/services/unfold.py
Normal file
46
config/services/unfold.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from django.conf import settings
|
||||
|
||||
UNFOLD = {
|
||||
"SITE_TITLE": "Qlockify Admin",
|
||||
"SITE_HEADER": "Qlockify Admin Panel",
|
||||
"SITE_BRANDING": "Qlockify",
|
||||
"SITE_URL": "/api/docs/",
|
||||
"SITE_SYMBOL": "speed",
|
||||
"SHOW_HISTORY": True,
|
||||
"SHOW_VIEW_ON_SITE": True,
|
||||
"ENVIRONMENT": "config.services.unfold.environment_callback",
|
||||
"COLORS": {
|
||||
"primary": {
|
||||
"50": "#f5f7ff",
|
||||
"100": "#e8edff",
|
||||
"200": "#c7d2ff",
|
||||
"300": "#a3b4ff",
|
||||
"400": "#7a8dff",
|
||||
"500": "#4f6bff",
|
||||
"600": "#3f55d6",
|
||||
"700": "#3243ab",
|
||||
"800": "#263281",
|
||||
"900": "#1b245b",
|
||||
},
|
||||
"gray": {
|
||||
"50": "#f8fafc",
|
||||
"100": "#f1f5f9",
|
||||
"200": "#e2e8f0",
|
||||
"300": "#cbd5e1",
|
||||
"400": "#94a3b8",
|
||||
"500": "#64748b",
|
||||
"600": "#475569",
|
||||
"700": "#334155",
|
||||
"800": "#1f2937",
|
||||
"900": "#0f172a",
|
||||
},
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"show_search": True,
|
||||
"show_all_applications": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def environment_callback(request):
|
||||
return ["Development", "warning"] if settings.DEBUG else ["Production", "success"]
|
||||
Reference in New Issue
Block a user