django-cfg 1.2.16__py3-none-any.whl → 1.2.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/models/__init__.py +68 -0
- django_cfg/apps/accounts/models/activity.py +34 -0
- django_cfg/apps/accounts/models/auth.py +50 -0
- django_cfg/apps/accounts/models/base.py +8 -0
- django_cfg/apps/accounts/models/choices.py +32 -0
- django_cfg/apps/accounts/models/integrations.py +75 -0
- django_cfg/apps/accounts/models/registration.py +52 -0
- django_cfg/apps/accounts/models/user.py +80 -0
- django_cfg/apps/maintenance/__init__.py +53 -24
- django_cfg/apps/maintenance/admin/__init__.py +7 -18
- django_cfg/apps/maintenance/admin/api_key_admin.py +185 -0
- django_cfg/apps/maintenance/admin/log_admin.py +156 -0
- django_cfg/apps/maintenance/admin/scheduled_admin.py +390 -0
- django_cfg/apps/maintenance/admin/site_admin.py +448 -0
- django_cfg/apps/maintenance/apps.py +9 -96
- django_cfg/apps/maintenance/management/commands/maintenance.py +193 -307
- django_cfg/apps/maintenance/management/commands/process_scheduled_maintenance.py +241 -0
- django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +152 -111
- django_cfg/apps/maintenance/managers/__init__.py +7 -12
- django_cfg/apps/maintenance/managers/cloudflare_site_manager.py +192 -0
- django_cfg/apps/maintenance/managers/maintenance_log_manager.py +151 -0
- django_cfg/apps/maintenance/migrations/0001_initial.py +145 -705
- django_cfg/apps/maintenance/migrations/0002_cloudflaresite_maintenance_url.py +21 -0
- django_cfg/apps/maintenance/models/__init__.py +23 -21
- django_cfg/apps/maintenance/models/cloudflare_api_key.py +109 -0
- django_cfg/apps/maintenance/models/cloudflare_site.py +125 -0
- django_cfg/apps/maintenance/models/maintenance_log.py +131 -0
- django_cfg/apps/maintenance/models/scheduled_maintenance.py +307 -0
- django_cfg/apps/maintenance/services/__init__.py +37 -16
- django_cfg/apps/maintenance/services/bulk_operations_service.py +400 -0
- django_cfg/apps/maintenance/services/maintenance_service.py +230 -0
- django_cfg/apps/maintenance/services/scheduled_maintenance_service.py +381 -0
- django_cfg/apps/maintenance/services/site_sync_service.py +390 -0
- django_cfg/apps/maintenance/utils/__init__.py +12 -0
- django_cfg/apps/maintenance/utils/retry_utils.py +109 -0
- django_cfg/config.py +3 -0
- django_cfg/core/config.py +4 -6
- django_cfg/modules/django_unfold/dashboard.py +4 -5
- {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/METADATA +52 -1
- {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/RECORD +45 -55
- django_cfg/apps/maintenance/README.md +0 -305
- django_cfg/apps/maintenance/admin/deployments_admin.py +0 -251
- django_cfg/apps/maintenance/admin/events_admin.py +0 -374
- django_cfg/apps/maintenance/admin/monitoring_admin.py +0 -215
- django_cfg/apps/maintenance/admin/sites_admin.py +0 -464
- django_cfg/apps/maintenance/managers/deployments.py +0 -287
- django_cfg/apps/maintenance/managers/events.py +0 -374
- django_cfg/apps/maintenance/managers/monitoring.py +0 -301
- django_cfg/apps/maintenance/managers/sites.py +0 -335
- django_cfg/apps/maintenance/models/cloudflare.py +0 -316
- django_cfg/apps/maintenance/models/maintenance.py +0 -334
- django_cfg/apps/maintenance/models/monitoring.py +0 -393
- django_cfg/apps/maintenance/models/sites.py +0 -419
- django_cfg/apps/maintenance/serializers/__init__.py +0 -60
- django_cfg/apps/maintenance/serializers/actions.py +0 -310
- django_cfg/apps/maintenance/serializers/base.py +0 -44
- django_cfg/apps/maintenance/serializers/deployments.py +0 -209
- django_cfg/apps/maintenance/serializers/events.py +0 -210
- django_cfg/apps/maintenance/serializers/monitoring.py +0 -278
- django_cfg/apps/maintenance/serializers/sites.py +0 -213
- django_cfg/apps/maintenance/services/README.md +0 -168
- django_cfg/apps/maintenance/services/cloudflare_client.py +0 -441
- django_cfg/apps/maintenance/services/dns_manager.py +0 -497
- django_cfg/apps/maintenance/services/maintenance_manager.py +0 -504
- django_cfg/apps/maintenance/services/site_sync.py +0 -448
- django_cfg/apps/maintenance/services/sync_command_service.py +0 -330
- django_cfg/apps/maintenance/services/worker_manager.py +0 -264
- django_cfg/apps/maintenance/signals.py +0 -38
- django_cfg/apps/maintenance/urls.py +0 -36
- django_cfg/apps/maintenance/views/__init__.py +0 -18
- django_cfg/apps/maintenance/views/base.py +0 -61
- django_cfg/apps/maintenance/views/deployments.py +0 -175
- django_cfg/apps/maintenance/views/events.py +0 -204
- django_cfg/apps/maintenance/views/monitoring.py +0 -213
- django_cfg/apps/maintenance/views/sites.py +0 -338
- django_cfg/models/cloudflare.py +0 -316
- /django_cfg/apps/accounts/{models.py → __models.py} +0 -0
- {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/licenses/LICENSE +0 -0
django_cfg/__init__.py
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
"""
|
2
|
+
Django CFG Accounts Models
|
3
|
+
|
4
|
+
This module provides all the models for the accounts app.
|
5
|
+
Models are organized by functionality:
|
6
|
+
|
7
|
+
- base: Base utilities and functions
|
8
|
+
- user: User model and related functionality
|
9
|
+
- registration: Registration and source tracking models
|
10
|
+
- auth: Authentication models (OTP, etc.)
|
11
|
+
- activity: User activity tracking models
|
12
|
+
- integrations: Third-party integrations models (Twilio, etc.)
|
13
|
+
"""
|
14
|
+
|
15
|
+
# Import base utilities
|
16
|
+
from .base import user_avatar_path
|
17
|
+
|
18
|
+
# Import choices
|
19
|
+
from .choices import (
|
20
|
+
ActivityType,
|
21
|
+
TwilioResponseType,
|
22
|
+
TwilioServiceType,
|
23
|
+
)
|
24
|
+
|
25
|
+
# Import user models
|
26
|
+
from .user import CustomUser
|
27
|
+
|
28
|
+
# Import registration models
|
29
|
+
from .registration import (
|
30
|
+
RegistrationSource,
|
31
|
+
UserRegistrationSource,
|
32
|
+
)
|
33
|
+
|
34
|
+
# Import authentication models
|
35
|
+
from .auth import OTPSecret
|
36
|
+
|
37
|
+
# Import activity models
|
38
|
+
from .activity import UserActivity
|
39
|
+
|
40
|
+
# Import integration models
|
41
|
+
from .integrations import TwilioResponse
|
42
|
+
|
43
|
+
# Export all models
|
44
|
+
__all__ = [
|
45
|
+
# Base utilities
|
46
|
+
'user_avatar_path',
|
47
|
+
|
48
|
+
# Choices
|
49
|
+
'ActivityType',
|
50
|
+
'TwilioResponseType',
|
51
|
+
'TwilioServiceType',
|
52
|
+
|
53
|
+
# User models
|
54
|
+
'CustomUser',
|
55
|
+
|
56
|
+
# Registration models
|
57
|
+
'RegistrationSource',
|
58
|
+
'UserRegistrationSource',
|
59
|
+
|
60
|
+
# Authentication models
|
61
|
+
'OTPSecret',
|
62
|
+
|
63
|
+
# Activity models
|
64
|
+
'UserActivity',
|
65
|
+
|
66
|
+
# Integration models
|
67
|
+
'TwilioResponse',
|
68
|
+
]
|
@@ -0,0 +1,34 @@
|
|
1
|
+
"""
|
2
|
+
User activity tracking models.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.db import models
|
6
|
+
|
7
|
+
from .choices import ActivityType
|
8
|
+
|
9
|
+
|
10
|
+
class UserActivity(models.Model):
|
11
|
+
"""
|
12
|
+
User activity log.
|
13
|
+
"""
|
14
|
+
|
15
|
+
user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='activities')
|
16
|
+
activity_type = models.CharField(max_length=20, choices=ActivityType.choices)
|
17
|
+
description = models.TextField(blank=True)
|
18
|
+
ip_address = models.GenericIPAddressField(null=True, blank=True)
|
19
|
+
user_agent = models.TextField(blank=True)
|
20
|
+
|
21
|
+
# Related objects (generic foreign key could be used here)
|
22
|
+
object_id = models.PositiveIntegerField(null=True, blank=True)
|
23
|
+
object_type = models.CharField(max_length=50, blank=True)
|
24
|
+
|
25
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
26
|
+
|
27
|
+
class Meta:
|
28
|
+
app_label = 'django_cfg_accounts'
|
29
|
+
verbose_name = 'User Activity'
|
30
|
+
verbose_name_plural = 'User Activities'
|
31
|
+
ordering = ['-created_at']
|
32
|
+
|
33
|
+
def __str__(self):
|
34
|
+
return f"{self.user.username} - {self.get_activity_type_display()}"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
"""
|
2
|
+
Authentication models (OTP, etc.).
|
3
|
+
"""
|
4
|
+
|
5
|
+
import random
|
6
|
+
import string
|
7
|
+
from datetime import timedelta
|
8
|
+
|
9
|
+
from django.db import models
|
10
|
+
from django.utils import timezone
|
11
|
+
|
12
|
+
|
13
|
+
class OTPSecret(models.Model):
|
14
|
+
"""Stores One-Time Passwords for authentication."""
|
15
|
+
|
16
|
+
email = models.EmailField(db_index=True)
|
17
|
+
secret = models.CharField(max_length=6)
|
18
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
19
|
+
expires_at = models.DateTimeField()
|
20
|
+
is_used = models.BooleanField(default=False)
|
21
|
+
|
22
|
+
def save(self, *args, **kwargs):
|
23
|
+
if not self.expires_at:
|
24
|
+
self.expires_at = timezone.now() + timedelta(minutes=10)
|
25
|
+
super().save(*args, **kwargs)
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def generate_otp(length=6):
|
29
|
+
"""Generate random numeric OTP."""
|
30
|
+
return "".join(random.choices(string.digits, k=length))
|
31
|
+
|
32
|
+
@property
|
33
|
+
def is_valid(self):
|
34
|
+
"""Check if OTP is still valid."""
|
35
|
+
return not self.is_used and timezone.now() < self.expires_at
|
36
|
+
|
37
|
+
def mark_used(self):
|
38
|
+
"""Mark OTP as used."""
|
39
|
+
self.is_used = True
|
40
|
+
self.save(update_fields=["is_used"])
|
41
|
+
|
42
|
+
def __str__(self):
|
43
|
+
return f"OTP for {self.email}"
|
44
|
+
|
45
|
+
class Meta:
|
46
|
+
app_label = 'django_cfg_accounts'
|
47
|
+
ordering = ["-created_at"]
|
48
|
+
indexes = [
|
49
|
+
models.Index(fields=["email", "is_used", "expires_at"]),
|
50
|
+
]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"""
|
2
|
+
Model choices constants for accounts app.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.db import models
|
6
|
+
|
7
|
+
|
8
|
+
class ActivityType(models.TextChoices):
|
9
|
+
"""User activity types."""
|
10
|
+
LOGIN = 'login', 'Login'
|
11
|
+
LOGOUT = 'logout', 'Logout'
|
12
|
+
OTP_REQUESTED = 'otp_requested', 'OTP Requested'
|
13
|
+
OTP_VERIFIED = 'otp_verified', 'OTP Verified'
|
14
|
+
PROFILE_UPDATED = 'profile_updated', 'Profile Updated'
|
15
|
+
REGISTRATION = 'registration', 'Registration'
|
16
|
+
|
17
|
+
|
18
|
+
class TwilioResponseType(models.TextChoices):
|
19
|
+
"""Twilio response types."""
|
20
|
+
API_SEND = 'api_send', 'API Send Request'
|
21
|
+
API_VERIFY = 'api_verify', 'API Verify Request'
|
22
|
+
WEBHOOK_STATUS = 'webhook_status', 'Webhook Status Update'
|
23
|
+
WEBHOOK_DELIVERY = 'webhook_delivery', 'Webhook Delivery Report'
|
24
|
+
|
25
|
+
|
26
|
+
class TwilioServiceType(models.TextChoices):
|
27
|
+
"""Twilio service types."""
|
28
|
+
WHATSAPP = 'whatsapp', 'WhatsApp'
|
29
|
+
SMS = 'sms', 'SMS'
|
30
|
+
VOICE = 'voice', 'Voice'
|
31
|
+
EMAIL = 'email', 'Email'
|
32
|
+
VERIFY = 'verify', 'Verify API'
|
@@ -0,0 +1,75 @@
|
|
1
|
+
"""
|
2
|
+
Third-party integrations models (Twilio, etc.).
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.db import models
|
6
|
+
|
7
|
+
from .choices import TwilioResponseType, TwilioServiceType
|
8
|
+
|
9
|
+
|
10
|
+
class TwilioResponse(models.Model):
|
11
|
+
"""Model for storing Twilio API responses and webhook data."""
|
12
|
+
|
13
|
+
response_type = models.CharField(max_length=20, choices=TwilioResponseType.choices)
|
14
|
+
service_type = models.CharField(max_length=10, choices=TwilioServiceType.choices)
|
15
|
+
|
16
|
+
# Twilio identifiers
|
17
|
+
message_sid = models.CharField(max_length=34, blank=True, help_text="Twilio Message SID")
|
18
|
+
verification_sid = models.CharField(max_length=34, blank=True, help_text="Twilio Verification SID")
|
19
|
+
|
20
|
+
# Request/Response data
|
21
|
+
request_data = models.JSONField(default=dict, help_text="Original request parameters")
|
22
|
+
response_data = models.JSONField(default=dict, help_text="Twilio API response")
|
23
|
+
|
24
|
+
# Status and error handling
|
25
|
+
status = models.CharField(max_length=20, blank=True, help_text="Message/Verification status")
|
26
|
+
error_code = models.CharField(max_length=10, blank=True, help_text="Twilio error code")
|
27
|
+
error_message = models.TextField(blank=True, help_text="Error description")
|
28
|
+
|
29
|
+
# Contact information
|
30
|
+
to_number = models.CharField(max_length=20, blank=True, help_text="Recipient phone/email")
|
31
|
+
from_number = models.CharField(max_length=20, blank=True, help_text="Sender phone/email")
|
32
|
+
|
33
|
+
# Pricing
|
34
|
+
price = models.DecimalField(max_digits=10, decimal_places=6, null=True, blank=True)
|
35
|
+
price_unit = models.CharField(max_length=3, blank=True, help_text="Currency code")
|
36
|
+
|
37
|
+
# Timestamps
|
38
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
39
|
+
updated_at = models.DateTimeField(auto_now=True)
|
40
|
+
twilio_created_at = models.DateTimeField(null=True, blank=True, help_text="Timestamp from Twilio")
|
41
|
+
|
42
|
+
# Relations
|
43
|
+
otp_secret = models.ForeignKey(
|
44
|
+
'OTPSecret',
|
45
|
+
on_delete=models.SET_NULL,
|
46
|
+
null=True,
|
47
|
+
blank=True,
|
48
|
+
related_name='twilio_responses',
|
49
|
+
help_text="Related OTP if applicable"
|
50
|
+
)
|
51
|
+
|
52
|
+
class Meta:
|
53
|
+
app_label = 'django_cfg_accounts'
|
54
|
+
verbose_name = 'Twilio Response'
|
55
|
+
verbose_name_plural = 'Twilio Responses'
|
56
|
+
ordering = ['-created_at']
|
57
|
+
indexes = [
|
58
|
+
models.Index(fields=['message_sid']),
|
59
|
+
models.Index(fields=['verification_sid']),
|
60
|
+
models.Index(fields=['status', 'created_at']),
|
61
|
+
models.Index(fields=['response_type', 'service_type']),
|
62
|
+
]
|
63
|
+
|
64
|
+
def __str__(self):
|
65
|
+
return f"{self.get_response_type_display()} - {self.get_service_type_display()}"
|
66
|
+
|
67
|
+
@property
|
68
|
+
def has_error(self):
|
69
|
+
"""Check if response has error."""
|
70
|
+
return bool(self.error_code or self.error_message)
|
71
|
+
|
72
|
+
@property
|
73
|
+
def is_successful(self):
|
74
|
+
"""Check if response is successful."""
|
75
|
+
return not self.has_error and self.status in ['sent', 'delivered', 'approved']
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
Registration and source tracking models.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.db import models
|
6
|
+
from urllib.parse import urlparse
|
7
|
+
|
8
|
+
|
9
|
+
class RegistrationSource(models.Model):
|
10
|
+
"""Model for tracking user registration sources/projects."""
|
11
|
+
url = models.URLField(unique=True, help_text="Source URL (e.g., https://unrealon.com)")
|
12
|
+
name = models.CharField(max_length=100, blank=True, help_text="Display name for the source")
|
13
|
+
description = models.TextField(blank=True, help_text="Optional description")
|
14
|
+
is_active = models.BooleanField(default=True, help_text="Whether this source is active")
|
15
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
16
|
+
updated_at = models.DateTimeField(auto_now=True)
|
17
|
+
|
18
|
+
def __str__(self):
|
19
|
+
return self.name or self.get_domain()
|
20
|
+
|
21
|
+
def get_domain(self):
|
22
|
+
"""Extract domain from URL."""
|
23
|
+
try:
|
24
|
+
parsed = urlparse(self.url)
|
25
|
+
return parsed.netloc
|
26
|
+
except:
|
27
|
+
return self.url
|
28
|
+
|
29
|
+
def get_display_name(self):
|
30
|
+
"""Get display name or domain."""
|
31
|
+
return self.name or self.get_domain()
|
32
|
+
|
33
|
+
class Meta:
|
34
|
+
app_label = 'django_cfg_accounts'
|
35
|
+
verbose_name = "Registration Source"
|
36
|
+
verbose_name_plural = "Registration Sources"
|
37
|
+
ordering = ['-created_at']
|
38
|
+
|
39
|
+
|
40
|
+
class UserRegistrationSource(models.Model):
|
41
|
+
"""Many-to-many relationship between users and registration sources."""
|
42
|
+
user = models.ForeignKey('CustomUser', on_delete=models.CASCADE, related_name='user_registration_sources')
|
43
|
+
source = models.ForeignKey(RegistrationSource, on_delete=models.CASCADE, related_name='user_registration_sources')
|
44
|
+
first_registration = models.BooleanField(default=True, help_text="Whether this was the first registration from this source")
|
45
|
+
registration_date = models.DateTimeField(auto_now_add=True)
|
46
|
+
|
47
|
+
class Meta:
|
48
|
+
app_label = 'django_cfg_accounts'
|
49
|
+
unique_together = ['user', 'source']
|
50
|
+
verbose_name = "User Registration Source"
|
51
|
+
verbose_name_plural = "User Registration Sources"
|
52
|
+
ordering = ['-registration_date']
|
@@ -0,0 +1,80 @@
|
|
1
|
+
"""
|
2
|
+
User model and related functionality.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Optional, List
|
6
|
+
from django.contrib.auth.models import AbstractUser
|
7
|
+
from django.db import models
|
8
|
+
|
9
|
+
from ..managers.user_manager import UserManager
|
10
|
+
from .base import user_avatar_path
|
11
|
+
|
12
|
+
|
13
|
+
class CustomUser(AbstractUser):
|
14
|
+
"""Simplified user model for OTP-only authentication."""
|
15
|
+
|
16
|
+
email = models.EmailField(unique=True)
|
17
|
+
|
18
|
+
# Profile fields
|
19
|
+
first_name = models.CharField(max_length=50, blank=True)
|
20
|
+
last_name = models.CharField(max_length=50, blank=True)
|
21
|
+
company = models.CharField(max_length=100, blank=True)
|
22
|
+
phone = models.CharField(max_length=20, blank=True)
|
23
|
+
position = models.CharField(max_length=100, blank=True)
|
24
|
+
avatar = models.ImageField(upload_to=user_avatar_path, blank=True, null=True)
|
25
|
+
|
26
|
+
# Profile metadata
|
27
|
+
updated_at = models.DateTimeField(auto_now=True)
|
28
|
+
|
29
|
+
# Managers
|
30
|
+
objects: UserManager = UserManager()
|
31
|
+
|
32
|
+
USERNAME_FIELD = "email"
|
33
|
+
REQUIRED_FIELDS = ["username"]
|
34
|
+
|
35
|
+
def __str__(self):
|
36
|
+
return self.email
|
37
|
+
|
38
|
+
@property
|
39
|
+
def is_admin(self) -> bool:
|
40
|
+
return self.is_superuser
|
41
|
+
|
42
|
+
@property
|
43
|
+
def full_name(self) -> str:
|
44
|
+
"""Get user's full name."""
|
45
|
+
return self.__class__.objects.get_full_name(self)
|
46
|
+
|
47
|
+
@property
|
48
|
+
def initials(self) -> str:
|
49
|
+
"""Get user's initials for avatar fallback."""
|
50
|
+
return self.__class__.objects.get_initials(self)
|
51
|
+
|
52
|
+
@property
|
53
|
+
def display_username(self) -> str:
|
54
|
+
"""Get formatted username for display."""
|
55
|
+
return self.__class__.objects.get_display_username(self)
|
56
|
+
|
57
|
+
@property
|
58
|
+
def unanswered_messages_count(self) -> int:
|
59
|
+
"""Get count of unanswered messages for the user."""
|
60
|
+
return self.__class__.objects.get_unanswered_messages_count(self)
|
61
|
+
|
62
|
+
def get_sources(self) -> List['RegistrationSource']:
|
63
|
+
"""Get all sources associated with this user."""
|
64
|
+
from .registration import RegistrationSource
|
65
|
+
return RegistrationSource.objects.filter(user_registration_sources__user=self)
|
66
|
+
|
67
|
+
@property
|
68
|
+
def primary_source(self) -> Optional['RegistrationSource']:
|
69
|
+
"""Get the first source where user registered."""
|
70
|
+
from .registration import UserRegistrationSource
|
71
|
+
user_source = UserRegistrationSource.objects.filter(
|
72
|
+
user=self,
|
73
|
+
first_registration=True
|
74
|
+
).first()
|
75
|
+
return user_source.source if user_source else None
|
76
|
+
|
77
|
+
class Meta:
|
78
|
+
app_label = 'django_cfg_accounts'
|
79
|
+
verbose_name = "User"
|
80
|
+
verbose_name_plural = "Users"
|
@@ -1,27 +1,56 @@
|
|
1
1
|
"""
|
2
|
-
Django-CFG Maintenance Application
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
- Rich Django admin interface
|
13
|
-
- Scheduled maintenance support
|
14
|
-
- Full audit trail and logging
|
15
|
-
|
16
|
-
Example Usage:
|
17
|
-
# Zero-config setup
|
18
|
-
CLOUDFLARE_API_TOKEN = "your_token"
|
19
|
-
CLOUDFLARE_DOMAIN = "example.com"
|
20
|
-
|
21
|
-
# Multi-site management
|
22
|
-
sites = multi_site_manager.sites(user)
|
23
|
-
await sites.production().enable_maintenance()
|
24
|
-
await sites.filter(project='client-a').disable_maintenance()
|
2
|
+
Simplified Django-CFG Maintenance Application.
|
3
|
+
|
4
|
+
A simple maintenance mode management app with Cloudflare integration.
|
5
|
+
Refactored from 119 files to ~10 files following KISS principles.
|
6
|
+
|
7
|
+
Proper structure:
|
8
|
+
- models/ - CloudflareSite and MaintenanceLog models
|
9
|
+
- services/ - MaintenanceService for Cloudflare operations
|
10
|
+
- admin/ - Simple admin interfaces
|
11
|
+
- management/commands/ - CLI commands
|
25
12
|
"""
|
26
13
|
|
27
|
-
|
14
|
+
__version__ = "2.0.0"
|
15
|
+
__author__ = "Django-CFG Team"
|
16
|
+
|
17
|
+
# Lazy import registry to avoid Django initialization issues
|
18
|
+
_LAZY_IMPORTS = {
|
19
|
+
# Models
|
20
|
+
'CloudflareApiKey': ('django_cfg.apps.maintenance.models', 'CloudflareApiKey'),
|
21
|
+
'CloudflareSite': ('django_cfg.apps.maintenance.models', 'CloudflareSite'),
|
22
|
+
'MaintenanceLog': ('django_cfg.apps.maintenance.models', 'MaintenanceLog'),
|
23
|
+
'ScheduledMaintenance': ('django_cfg.apps.maintenance.models', 'ScheduledMaintenance'),
|
24
|
+
|
25
|
+
# Services
|
26
|
+
'MaintenanceService': ('django_cfg.apps.maintenance.services', 'MaintenanceService'),
|
27
|
+
'SiteSyncService': ('django_cfg.apps.maintenance.services', 'SiteSyncService'),
|
28
|
+
'BulkOperationsService': ('django_cfg.apps.maintenance.services', 'BulkOperationsService'),
|
29
|
+
'ScheduledMaintenanceService': ('django_cfg.apps.maintenance.services', 'ScheduledMaintenanceService'),
|
30
|
+
'bulk_operations': ('django_cfg.apps.maintenance.services', 'bulk_operations'),
|
31
|
+
'scheduled_maintenance_service': ('django_cfg.apps.maintenance.services', 'scheduled_maintenance_service'),
|
32
|
+
'enable_maintenance_for_domain': ('django_cfg.apps.maintenance.services', 'enable_maintenance_for_domain'),
|
33
|
+
'disable_maintenance_for_domain': ('django_cfg.apps.maintenance.services', 'disable_maintenance_for_domain'),
|
34
|
+
'sync_site_from_cloudflare': ('django_cfg.apps.maintenance.services', 'sync_site_from_cloudflare'),
|
35
|
+
'enable_maintenance_for_domains': ('django_cfg.apps.maintenance.services', 'enable_maintenance_for_domains'),
|
36
|
+
'disable_maintenance_for_domains': ('django_cfg.apps.maintenance.services', 'disable_maintenance_for_domains'),
|
37
|
+
'bulk_sync_all_sites': ('django_cfg.apps.maintenance.services', 'bulk_sync_all_sites'),
|
38
|
+
'get_maintenance_status_report': ('django_cfg.apps.maintenance.services', 'get_maintenance_status_report'),
|
39
|
+
'schedule_maintenance_for_sites': ('django_cfg.apps.maintenance.services', 'schedule_maintenance_for_sites'),
|
40
|
+
'process_scheduled_maintenances': ('django_cfg.apps.maintenance.services', 'process_scheduled_maintenances'),
|
41
|
+
}
|
42
|
+
|
43
|
+
|
44
|
+
def __getattr__(name: str):
|
45
|
+
"""Lazy import mechanism to avoid Django initialization issues."""
|
46
|
+
if name in _LAZY_IMPORTS:
|
47
|
+
module_path, attr_name = _LAZY_IMPORTS[name]
|
48
|
+
|
49
|
+
import importlib
|
50
|
+
module = importlib.import_module(module_path)
|
51
|
+
return getattr(module, attr_name)
|
52
|
+
|
53
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
54
|
+
|
55
|
+
|
56
|
+
__all__ = list(_LAZY_IMPORTS.keys())
|
@@ -1,28 +1,17 @@
|
|
1
1
|
"""
|
2
2
|
Maintenance admin interfaces.
|
3
3
|
|
4
|
-
|
5
|
-
Following django-cfg patterns with Unfold optimization.
|
4
|
+
Decomposed admin interfaces with Unfold styling and action buttons.
|
6
5
|
"""
|
7
6
|
|
8
|
-
|
9
|
-
from .
|
10
|
-
from .
|
11
|
-
from .
|
12
|
-
from .deployments_admin import CloudflareDeploymentAdmin
|
7
|
+
from .api_key_admin import CloudflareApiKeyAdmin
|
8
|
+
from .site_admin import CloudflareSiteAdmin
|
9
|
+
from .log_admin import MaintenanceLogAdmin
|
10
|
+
from .scheduled_admin import ScheduledMaintenanceAdmin
|
13
11
|
|
14
12
|
__all__ = [
|
15
|
-
|
13
|
+
'CloudflareApiKeyAdmin',
|
16
14
|
'CloudflareSiteAdmin',
|
17
|
-
'SiteGroupAdmin',
|
18
|
-
|
19
|
-
# Maintenance tracking
|
20
|
-
'MaintenanceEventAdmin',
|
21
15
|
'MaintenanceLogAdmin',
|
22
|
-
|
23
|
-
# Monitoring
|
24
|
-
'MonitoringTargetAdmin',
|
25
|
-
|
26
|
-
# Cloudflare integration
|
27
|
-
'CloudflareDeploymentAdmin',
|
16
|
+
'ScheduledMaintenanceAdmin',
|
28
17
|
]
|