django-cfg 1.2.17__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.17.dist-info → django_cfg-1.2.18.dist-info}/METADATA +52 -1
- {django_cfg-1.2.17.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.17.dist-info → django_cfg-1.2.18.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.18.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.18.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# Generated by Django 5.2.6 on 2025-09-23 08:14
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
dependencies = [
|
8
|
+
("maintenance", "0001_initial"),
|
9
|
+
]
|
10
|
+
|
11
|
+
operations = [
|
12
|
+
migrations.AddField(
|
13
|
+
model_name="cloudflaresite",
|
14
|
+
name="maintenance_url",
|
15
|
+
field=models.URLField(
|
16
|
+
blank=True,
|
17
|
+
help_text="URL to redirect to during maintenance mode. If empty, uses default maintenance page.",
|
18
|
+
max_length=500,
|
19
|
+
),
|
20
|
+
),
|
21
|
+
]
|
@@ -1,27 +1,29 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Simplified maintenance models.
|
3
3
|
|
4
|
-
|
4
|
+
Clean imports for the 4 models, properly decomposed.
|
5
|
+
Uses lazy imports to avoid Django initialization issues.
|
5
6
|
"""
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
# Lazy import registry to avoid Django initialization issues
|
9
|
+
_LAZY_IMPORTS = {
|
10
|
+
'CloudflareApiKey': ('django_cfg.apps.maintenance.models.cloudflare_api_key', 'CloudflareApiKey'),
|
11
|
+
'CloudflareSite': ('django_cfg.apps.maintenance.models.cloudflare_site', 'CloudflareSite'),
|
12
|
+
'MaintenanceLog': ('django_cfg.apps.maintenance.models.maintenance_log', 'MaintenanceLog'),
|
13
|
+
'ScheduledMaintenance': ('django_cfg.apps.maintenance.models.scheduled_maintenance', 'ScheduledMaintenance'),
|
14
|
+
}
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# Monitoring models
|
22
|
-
'MonitoringTarget',
|
23
|
-
'HealthCheckResult',
|
16
|
+
|
17
|
+
def __getattr__(name: str):
|
18
|
+
"""Lazy import mechanism to avoid Django initialization issues."""
|
19
|
+
if name in _LAZY_IMPORTS:
|
20
|
+
module_path, attr_name = _LAZY_IMPORTS[name]
|
21
|
+
|
22
|
+
import importlib
|
23
|
+
module = importlib.import_module(module_path)
|
24
|
+
return getattr(module, attr_name)
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
27
|
+
|
28
|
+
|
29
|
+
__all__ = list(_LAZY_IMPORTS.keys())
|
@@ -0,0 +1,109 @@
|
|
1
|
+
"""
|
2
|
+
CloudflareApiKey model.
|
3
|
+
|
4
|
+
Store Cloudflare API keys for different accounts/domains.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.db import models
|
8
|
+
from django.core.exceptions import ValidationError
|
9
|
+
|
10
|
+
|
11
|
+
class CloudflareApiKey(models.Model):
|
12
|
+
"""
|
13
|
+
Store Cloudflare API keys for different domains/accounts.
|
14
|
+
|
15
|
+
Allows managing multiple Cloudflare accounts with different API tokens.
|
16
|
+
"""
|
17
|
+
|
18
|
+
# Basic info
|
19
|
+
name = models.CharField(
|
20
|
+
max_length=100,
|
21
|
+
help_text="Friendly name for this API key (e.g., 'Production', 'Staging')"
|
22
|
+
)
|
23
|
+
description = models.TextField(
|
24
|
+
blank=True,
|
25
|
+
help_text="Optional description of what this key is used for"
|
26
|
+
)
|
27
|
+
|
28
|
+
# Cloudflare credentials
|
29
|
+
api_token = models.CharField(
|
30
|
+
max_length=255,
|
31
|
+
help_text="Cloudflare API token"
|
32
|
+
)
|
33
|
+
account_id = models.CharField(
|
34
|
+
max_length=32,
|
35
|
+
blank=True,
|
36
|
+
help_text="Cloudflare Account ID (auto-discovered if empty)"
|
37
|
+
)
|
38
|
+
|
39
|
+
# Settings
|
40
|
+
is_active = models.BooleanField(
|
41
|
+
default=True,
|
42
|
+
help_text="Whether this API key is active"
|
43
|
+
)
|
44
|
+
is_default = models.BooleanField(
|
45
|
+
default=False,
|
46
|
+
help_text="Whether this is the default API key to use"
|
47
|
+
)
|
48
|
+
|
49
|
+
# Timestamps
|
50
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
51
|
+
updated_at = models.DateTimeField(auto_now=True)
|
52
|
+
last_used_at = models.DateTimeField(
|
53
|
+
null=True,
|
54
|
+
blank=True,
|
55
|
+
help_text="When this API key was last used"
|
56
|
+
)
|
57
|
+
|
58
|
+
class Meta:
|
59
|
+
ordering = ['-is_default', 'name']
|
60
|
+
verbose_name = "Cloudflare API Key"
|
61
|
+
verbose_name_plural = "Cloudflare API Keys"
|
62
|
+
indexes = [
|
63
|
+
models.Index(fields=['is_active']),
|
64
|
+
models.Index(fields=['is_default']),
|
65
|
+
]
|
66
|
+
|
67
|
+
def __str__(self) -> str:
|
68
|
+
status = "🔑" if self.is_active else "🔒"
|
69
|
+
default = " (Default)" if self.is_default else ""
|
70
|
+
return f"{status} {self.name}{default}"
|
71
|
+
|
72
|
+
def clean(self) -> None:
|
73
|
+
"""Validate model data."""
|
74
|
+
super().clean()
|
75
|
+
|
76
|
+
# Validate API token format (basic check)
|
77
|
+
if not self.api_token.strip():
|
78
|
+
raise ValidationError({'api_token': 'API token cannot be empty'})
|
79
|
+
|
80
|
+
# Validate account_id format if provided
|
81
|
+
if self.account_id and len(self.account_id) != 32:
|
82
|
+
raise ValidationError({'account_id': 'Account ID must be 32 characters'})
|
83
|
+
|
84
|
+
def save(self, *args, **kwargs):
|
85
|
+
"""Override save to ensure only one default key."""
|
86
|
+
if self.is_default:
|
87
|
+
# Set all other keys to non-default
|
88
|
+
self.__class__.objects.filter(is_default=True).exclude(pk=self.pk).update(is_default=False)
|
89
|
+
|
90
|
+
super().save(*args, **kwargs)
|
91
|
+
|
92
|
+
@classmethod
|
93
|
+
def get_default(cls):
|
94
|
+
"""Get the default API key."""
|
95
|
+
try:
|
96
|
+
return cls.objects.filter(is_active=True, is_default=True).first()
|
97
|
+
except cls.DoesNotExist:
|
98
|
+
return None
|
99
|
+
|
100
|
+
@classmethod
|
101
|
+
def get_active_keys(cls):
|
102
|
+
"""Get all active API keys."""
|
103
|
+
return cls.objects.filter(is_active=True)
|
104
|
+
|
105
|
+
def mark_used(self):
|
106
|
+
"""Mark this key as recently used."""
|
107
|
+
from django.utils import timezone
|
108
|
+
self.last_used_at = timezone.now()
|
109
|
+
self.save(update_fields=['last_used_at'])
|
@@ -0,0 +1,125 @@
|
|
1
|
+
"""
|
2
|
+
CloudflareSite model.
|
3
|
+
|
4
|
+
Simplified model for storing Cloudflare sites for synchronization and maintenance.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.db import models
|
8
|
+
from django.core.validators import URLValidator
|
9
|
+
from django.core.exceptions import ValidationError
|
10
|
+
from django.utils import timezone
|
11
|
+
import re
|
12
|
+
from django_cfg.config import get_maintenance_url
|
13
|
+
|
14
|
+
class CloudflareSite(models.Model):
|
15
|
+
"""
|
16
|
+
Store Cloudflare sites for synchronization and maintenance.
|
17
|
+
|
18
|
+
Simplified from 30+ fields to just 11 essential fields.
|
19
|
+
"""
|
20
|
+
|
21
|
+
# Basic info
|
22
|
+
name = models.CharField(
|
23
|
+
max_length=100,
|
24
|
+
help_text="Friendly site name for identification"
|
25
|
+
)
|
26
|
+
domain = models.CharField(
|
27
|
+
max_length=255,
|
28
|
+
unique=True,
|
29
|
+
help_text="Domain name (e.g., vamcar.com)"
|
30
|
+
)
|
31
|
+
|
32
|
+
# Cloudflare IDs (auto-discovered during sync)
|
33
|
+
zone_id = models.CharField(
|
34
|
+
max_length=32,
|
35
|
+
unique=True,
|
36
|
+
help_text="Cloudflare Zone ID"
|
37
|
+
)
|
38
|
+
account_id = models.CharField(
|
39
|
+
max_length=32,
|
40
|
+
help_text="Cloudflare Account ID"
|
41
|
+
)
|
42
|
+
api_key = models.ForeignKey(
|
43
|
+
'CloudflareApiKey',
|
44
|
+
on_delete=models.PROTECT,
|
45
|
+
help_text="API key to use for this site"
|
46
|
+
)
|
47
|
+
|
48
|
+
# Simple status
|
49
|
+
maintenance_active = models.BooleanField(
|
50
|
+
default=False,
|
51
|
+
help_text="Whether maintenance mode is currently active"
|
52
|
+
)
|
53
|
+
maintenance_url = models.URLField(
|
54
|
+
max_length=500,
|
55
|
+
blank=True,
|
56
|
+
help_text="URL to redirect to during maintenance mode. If empty, uses default maintenance page."
|
57
|
+
)
|
58
|
+
is_active = models.BooleanField(
|
59
|
+
default=True,
|
60
|
+
help_text="Whether this site is active in our system"
|
61
|
+
)
|
62
|
+
|
63
|
+
# Timestamps
|
64
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
65
|
+
updated_at = models.DateTimeField(auto_now=True)
|
66
|
+
last_maintenance_at = models.DateTimeField(
|
67
|
+
null=True,
|
68
|
+
blank=True,
|
69
|
+
help_text="When maintenance was last activated"
|
70
|
+
)
|
71
|
+
|
72
|
+
# Custom manager
|
73
|
+
from ..managers.cloudflare_site_manager import CloudflareSiteManager
|
74
|
+
objects = CloudflareSiteManager()
|
75
|
+
|
76
|
+
class Meta:
|
77
|
+
ordering = ['name']
|
78
|
+
verbose_name = "Cloudflare Site"
|
79
|
+
verbose_name_plural = "Cloudflare Sites"
|
80
|
+
indexes = [
|
81
|
+
models.Index(fields=['domain']),
|
82
|
+
models.Index(fields=['maintenance_active']),
|
83
|
+
models.Index(fields=['is_active']),
|
84
|
+
]
|
85
|
+
|
86
|
+
def __str__(self) -> str:
|
87
|
+
status_emoji = "🔧" if self.maintenance_active else "🟢"
|
88
|
+
return f"{status_emoji} {self.name} ({self.domain})"
|
89
|
+
|
90
|
+
def enable_maintenance(self) -> None:
|
91
|
+
"""Enable maintenance mode for this site."""
|
92
|
+
self.maintenance_active = True
|
93
|
+
self.last_maintenance_at = timezone.now()
|
94
|
+
self.save(update_fields=['maintenance_active', 'last_maintenance_at', 'updated_at'])
|
95
|
+
|
96
|
+
def disable_maintenance(self) -> None:
|
97
|
+
"""Disable maintenance mode for this site."""
|
98
|
+
self.maintenance_active = False
|
99
|
+
self.save(update_fields=['maintenance_active', 'updated_at'])
|
100
|
+
|
101
|
+
def get_maintenance_url(self) -> str:
|
102
|
+
"""Get the maintenance URL for this site."""
|
103
|
+
if self.maintenance_url:
|
104
|
+
return self.maintenance_url
|
105
|
+
else:
|
106
|
+
# Default maintenance page with site parameter
|
107
|
+
return get_maintenance_url(self.domain)
|
108
|
+
|
109
|
+
def clean(self) -> None:
|
110
|
+
"""Validate model data."""
|
111
|
+
super().clean()
|
112
|
+
|
113
|
+
# Validate domain format
|
114
|
+
domain_pattern = re.compile(
|
115
|
+
r'^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?'
|
116
|
+
r'(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$'
|
117
|
+
)
|
118
|
+
if not domain_pattern.match(self.domain):
|
119
|
+
raise ValidationError({'domain': 'Invalid domain format'})
|
120
|
+
|
121
|
+
# Validate zone_id format (Cloudflare zone IDs are 32 chars)
|
122
|
+
if len(self.zone_id) != 32:
|
123
|
+
raise ValidationError({'zone_id': 'Zone ID must be 32 characters'})
|
124
|
+
|
125
|
+
|
@@ -0,0 +1,131 @@
|
|
1
|
+
"""
|
2
|
+
MaintenanceLog model.
|
3
|
+
|
4
|
+
Simple log of all maintenance operations.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.db import models
|
8
|
+
from .cloudflare_site import CloudflareSite
|
9
|
+
|
10
|
+
|
11
|
+
class MaintenanceLog(models.Model):
|
12
|
+
"""
|
13
|
+
Simple log of all maintenance operations.
|
14
|
+
|
15
|
+
Tracks enable/disable operations with success/failure status.
|
16
|
+
"""
|
17
|
+
|
18
|
+
class Action(models.TextChoices):
|
19
|
+
ENABLE = "enable", "Enable Maintenance"
|
20
|
+
DISABLE = "disable", "Disable Maintenance"
|
21
|
+
ERROR = "error", "Error"
|
22
|
+
SYNC = "sync", "Sync from Cloudflare"
|
23
|
+
|
24
|
+
class Status(models.TextChoices):
|
25
|
+
SUCCESS = "success", "Success"
|
26
|
+
FAILED = "failed", "Failed"
|
27
|
+
PENDING = "pending", "Pending"
|
28
|
+
|
29
|
+
# Related site
|
30
|
+
site = models.ForeignKey(
|
31
|
+
CloudflareSite,
|
32
|
+
on_delete=models.CASCADE,
|
33
|
+
related_name='logs',
|
34
|
+
help_text="Site this log entry belongs to"
|
35
|
+
)
|
36
|
+
|
37
|
+
# Operation details
|
38
|
+
action = models.CharField(
|
39
|
+
max_length=20,
|
40
|
+
choices=Action.choices,
|
41
|
+
help_text="What action was performed"
|
42
|
+
)
|
43
|
+
status = models.CharField(
|
44
|
+
max_length=20,
|
45
|
+
choices=Status.choices,
|
46
|
+
help_text="Result of the operation"
|
47
|
+
)
|
48
|
+
|
49
|
+
# Additional info
|
50
|
+
reason = models.TextField(
|
51
|
+
blank=True,
|
52
|
+
help_text="Why maintenance was enabled/disabled"
|
53
|
+
)
|
54
|
+
error_message = models.TextField(
|
55
|
+
blank=True,
|
56
|
+
help_text="Error details if operation failed"
|
57
|
+
)
|
58
|
+
cloudflare_response = models.JSONField(
|
59
|
+
null=True,
|
60
|
+
blank=True,
|
61
|
+
help_text="Full Cloudflare API response for debugging"
|
62
|
+
)
|
63
|
+
|
64
|
+
# Timing
|
65
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
66
|
+
duration_seconds = models.IntegerField(
|
67
|
+
null=True,
|
68
|
+
blank=True,
|
69
|
+
help_text="How long the operation took"
|
70
|
+
)
|
71
|
+
|
72
|
+
# Custom manager
|
73
|
+
from ..managers.maintenance_log_manager import MaintenanceLogManager
|
74
|
+
objects = MaintenanceLogManager()
|
75
|
+
|
76
|
+
class Meta:
|
77
|
+
ordering = ['-created_at']
|
78
|
+
verbose_name = "Maintenance Log"
|
79
|
+
verbose_name_plural = "Maintenance Logs"
|
80
|
+
indexes = [
|
81
|
+
models.Index(fields=['site', '-created_at']),
|
82
|
+
models.Index(fields=['action', '-created_at']),
|
83
|
+
models.Index(fields=['status', '-created_at']),
|
84
|
+
]
|
85
|
+
|
86
|
+
def __str__(self) -> str:
|
87
|
+
status_emoji = {
|
88
|
+
self.Status.SUCCESS: "✅",
|
89
|
+
self.Status.FAILED: "❌",
|
90
|
+
self.Status.PENDING: "⏳"
|
91
|
+
}.get(self.status, "❓")
|
92
|
+
|
93
|
+
return f"{status_emoji} {self.get_action_display()} - {self.site.domain}"
|
94
|
+
|
95
|
+
@classmethod
|
96
|
+
def log_success(cls, site: CloudflareSite, action: str, reason: str = "",
|
97
|
+
duration_seconds: int = None, cloudflare_response: dict = None) -> 'MaintenanceLog':
|
98
|
+
"""Log successful operation."""
|
99
|
+
return cls.objects.create(
|
100
|
+
site=site,
|
101
|
+
action=action,
|
102
|
+
status=cls.Status.SUCCESS,
|
103
|
+
reason=reason,
|
104
|
+
duration_seconds=duration_seconds,
|
105
|
+
cloudflare_response=cloudflare_response
|
106
|
+
)
|
107
|
+
|
108
|
+
@classmethod
|
109
|
+
def log_failure(cls, site: CloudflareSite, action: str, error_message: str,
|
110
|
+
reason: str = "", cloudflare_response: dict = None) -> 'MaintenanceLog':
|
111
|
+
"""Log failed operation."""
|
112
|
+
return cls.objects.create(
|
113
|
+
site=site,
|
114
|
+
action=action,
|
115
|
+
status=cls.Status.FAILED,
|
116
|
+
reason=reason,
|
117
|
+
error_message=error_message,
|
118
|
+
cloudflare_response=cloudflare_response
|
119
|
+
)
|
120
|
+
|
121
|
+
@classmethod
|
122
|
+
def log_pending(cls, site: CloudflareSite, action: str, reason: str = "") -> 'MaintenanceLog':
|
123
|
+
"""Log pending operation."""
|
124
|
+
return cls.objects.create(
|
125
|
+
site=site,
|
126
|
+
action=action,
|
127
|
+
status=cls.Status.PENDING,
|
128
|
+
reason=reason
|
129
|
+
)
|
130
|
+
|
131
|
+
|