django-cfg 1.2.15__py3-none-any.whl → 1.2.17__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/maintenance/README.md +305 -0
- django_cfg/apps/maintenance/__init__.py +27 -0
- django_cfg/apps/maintenance/admin/__init__.py +28 -0
- django_cfg/apps/maintenance/admin/deployments_admin.py +251 -0
- django_cfg/apps/maintenance/admin/events_admin.py +374 -0
- django_cfg/apps/maintenance/admin/monitoring_admin.py +215 -0
- django_cfg/apps/maintenance/admin/sites_admin.py +464 -0
- django_cfg/apps/maintenance/apps.py +105 -0
- django_cfg/apps/maintenance/management/__init__.py +0 -0
- django_cfg/apps/maintenance/management/commands/__init__.py +0 -0
- django_cfg/apps/maintenance/management/commands/maintenance.py +375 -0
- django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +168 -0
- django_cfg/apps/maintenance/managers/__init__.py +20 -0
- django_cfg/apps/maintenance/managers/deployments.py +287 -0
- django_cfg/apps/maintenance/managers/events.py +374 -0
- django_cfg/apps/maintenance/managers/monitoring.py +301 -0
- django_cfg/apps/maintenance/managers/sites.py +335 -0
- django_cfg/apps/maintenance/migrations/0001_initial.py +939 -0
- django_cfg/apps/maintenance/migrations/__init__.py +0 -0
- django_cfg/apps/maintenance/models/__init__.py +27 -0
- django_cfg/apps/maintenance/models/cloudflare.py +316 -0
- django_cfg/apps/maintenance/models/maintenance.py +334 -0
- django_cfg/apps/maintenance/models/monitoring.py +393 -0
- django_cfg/apps/maintenance/models/sites.py +419 -0
- django_cfg/apps/maintenance/serializers/__init__.py +60 -0
- django_cfg/apps/maintenance/serializers/actions.py +310 -0
- django_cfg/apps/maintenance/serializers/base.py +44 -0
- django_cfg/apps/maintenance/serializers/deployments.py +209 -0
- django_cfg/apps/maintenance/serializers/events.py +210 -0
- django_cfg/apps/maintenance/serializers/monitoring.py +278 -0
- django_cfg/apps/maintenance/serializers/sites.py +213 -0
- django_cfg/apps/maintenance/services/README.md +168 -0
- django_cfg/apps/maintenance/services/__init__.py +21 -0
- django_cfg/apps/maintenance/services/cloudflare_client.py +441 -0
- django_cfg/apps/maintenance/services/dns_manager.py +497 -0
- django_cfg/apps/maintenance/services/maintenance_manager.py +504 -0
- django_cfg/apps/maintenance/services/site_sync.py +448 -0
- django_cfg/apps/maintenance/services/sync_command_service.py +330 -0
- django_cfg/apps/maintenance/services/worker_manager.py +264 -0
- django_cfg/apps/maintenance/signals.py +38 -0
- django_cfg/apps/maintenance/urls.py +36 -0
- django_cfg/apps/maintenance/views/__init__.py +18 -0
- django_cfg/apps/maintenance/views/base.py +61 -0
- django_cfg/apps/maintenance/views/deployments.py +175 -0
- django_cfg/apps/maintenance/views/events.py +204 -0
- django_cfg/apps/maintenance/views/monitoring.py +213 -0
- django_cfg/apps/maintenance/views/sites.py +338 -0
- django_cfg/apps/urls.py +5 -1
- django_cfg/core/config.py +34 -3
- django_cfg/core/generation.py +15 -10
- django_cfg/models/cloudflare.py +316 -0
- django_cfg/models/revolution.py +1 -1
- django_cfg/models/tasks.py +1 -1
- django_cfg/modules/base.py +12 -5
- django_cfg/modules/django_unfold/dashboard.py +16 -1
- {django_cfg-1.2.15.dist-info → django_cfg-1.2.17.dist-info}/METADATA +2 -1
- {django_cfg-1.2.15.dist-info → django_cfg-1.2.17.dist-info}/RECORD +61 -13
- {django_cfg-1.2.15.dist-info → django_cfg-1.2.17.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.15.dist-info → django_cfg-1.2.17.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.15.dist-info → django_cfg-1.2.17.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,213 @@
|
|
1
|
+
"""
|
2
|
+
Site management serializers.
|
3
|
+
|
4
|
+
Serializers for CloudflareSite and SiteGroup models.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rest_framework import serializers
|
8
|
+
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
|
9
|
+
|
10
|
+
from ..models import CloudflareSite, SiteGroup
|
11
|
+
from .base import UserSerializer
|
12
|
+
|
13
|
+
|
14
|
+
@extend_schema_serializer(
|
15
|
+
examples=[
|
16
|
+
OpenApiExample(
|
17
|
+
'Production Site',
|
18
|
+
summary='A production Cloudflare site',
|
19
|
+
description='Example of a production site configuration',
|
20
|
+
value={
|
21
|
+
'name': 'My Production Site',
|
22
|
+
'domain': 'example.com',
|
23
|
+
'zone_id': 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',
|
24
|
+
'account_id': 'z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4',
|
25
|
+
'api_token': 'your-cloudflare-api-token',
|
26
|
+
'environment': 'production',
|
27
|
+
'project': 'main-website',
|
28
|
+
'tags': ['critical', 'ecommerce']
|
29
|
+
}
|
30
|
+
),
|
31
|
+
OpenApiExample(
|
32
|
+
'Staging Site',
|
33
|
+
summary='A staging environment site',
|
34
|
+
description='Example of a staging site configuration',
|
35
|
+
value={
|
36
|
+
'name': 'Staging Environment',
|
37
|
+
'domain': 'staging.example.com',
|
38
|
+
'zone_id': 'b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7',
|
39
|
+
'account_id': 'y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3',
|
40
|
+
'api_token': 'your-cloudflare-api-token',
|
41
|
+
'environment': 'staging',
|
42
|
+
'project': 'main-website',
|
43
|
+
'tags': ['testing']
|
44
|
+
}
|
45
|
+
)
|
46
|
+
]
|
47
|
+
)
|
48
|
+
class CloudflareSiteSerializer(serializers.ModelSerializer):
|
49
|
+
"""Serializer for CloudflareSite model with full details."""
|
50
|
+
|
51
|
+
owner = UserSerializer(read_only=True)
|
52
|
+
maintenance_duration = serializers.ReadOnlyField()
|
53
|
+
health_check_endpoint = serializers.ReadOnlyField()
|
54
|
+
is_production = serializers.ReadOnlyField()
|
55
|
+
|
56
|
+
class Meta:
|
57
|
+
model = CloudflareSite
|
58
|
+
fields = [
|
59
|
+
'id', 'name', 'domain', 'zone_id', 'account_id', 'api_token',
|
60
|
+
'environment', 'project', 'tags', 'owner', 'current_status',
|
61
|
+
'maintenance_active', 'monitoring_enabled', 'worker_name',
|
62
|
+
'health_check_url', 'check_interval', 'failure_threshold',
|
63
|
+
'maintenance_duration', 'health_check_endpoint', 'is_production',
|
64
|
+
'last_maintenance_at', 'last_status_check', 'created_at', 'updated_at'
|
65
|
+
]
|
66
|
+
read_only_fields = [
|
67
|
+
'id', 'owner', 'current_status', 'maintenance_duration',
|
68
|
+
'health_check_endpoint', 'is_production', 'last_maintenance_at',
|
69
|
+
'last_status_check', 'created_at', 'updated_at'
|
70
|
+
]
|
71
|
+
extra_kwargs = {
|
72
|
+
'api_token': {'write_only': True},
|
73
|
+
'zone_id': {
|
74
|
+
'help_text': 'Cloudflare Zone ID (32 characters)',
|
75
|
+
'min_length': 32,
|
76
|
+
'max_length': 32
|
77
|
+
},
|
78
|
+
'account_id': {
|
79
|
+
'help_text': 'Cloudflare Account ID (32 characters)',
|
80
|
+
'min_length': 32,
|
81
|
+
'max_length': 32
|
82
|
+
},
|
83
|
+
'name': {
|
84
|
+
'help_text': 'Human-readable name for the site'
|
85
|
+
},
|
86
|
+
'domain': {
|
87
|
+
'help_text': 'Domain name (without protocol or www)'
|
88
|
+
},
|
89
|
+
'environment': {
|
90
|
+
'help_text': 'Environment type (production, staging, development, testing)'
|
91
|
+
},
|
92
|
+
'project': {
|
93
|
+
'help_text': 'Project name for grouping sites'
|
94
|
+
},
|
95
|
+
'tags': {
|
96
|
+
'help_text': 'List of tags for categorization'
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
class CloudflareSiteCreateSerializer(serializers.ModelSerializer):
|
102
|
+
"""Serializer for creating CloudflareSite with validation."""
|
103
|
+
|
104
|
+
class Meta:
|
105
|
+
model = CloudflareSite
|
106
|
+
fields = [
|
107
|
+
'name', 'domain', 'zone_id', 'account_id', 'api_token',
|
108
|
+
'environment', 'project', 'tags', 'monitoring_enabled',
|
109
|
+
'worker_name', 'health_check_url', 'check_interval', 'failure_threshold'
|
110
|
+
]
|
111
|
+
extra_kwargs = {
|
112
|
+
'api_token': {'write_only': True},
|
113
|
+
'zone_id': {
|
114
|
+
'help_text': 'Cloudflare Zone ID (32 characters)',
|
115
|
+
'min_length': 32,
|
116
|
+
'max_length': 32
|
117
|
+
},
|
118
|
+
'account_id': {
|
119
|
+
'help_text': 'Cloudflare Account ID (32 characters)',
|
120
|
+
'min_length': 32,
|
121
|
+
'max_length': 32
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
def validate_domain(self, value):
|
126
|
+
"""Validate domain format."""
|
127
|
+
if value.startswith(('http://', 'https://')):
|
128
|
+
raise serializers.ValidationError(
|
129
|
+
"Domain should not include protocol (http:// or https://)"
|
130
|
+
)
|
131
|
+
if value.startswith('www.'):
|
132
|
+
raise serializers.ValidationError(
|
133
|
+
"Domain should not include www prefix"
|
134
|
+
)
|
135
|
+
return value.lower()
|
136
|
+
|
137
|
+
def validate_zone_id(self, value):
|
138
|
+
"""Validate zone_id format."""
|
139
|
+
if len(value) != 32:
|
140
|
+
raise serializers.ValidationError(
|
141
|
+
"Zone ID must be exactly 32 characters long"
|
142
|
+
)
|
143
|
+
return value
|
144
|
+
|
145
|
+
def validate_account_id(self, value):
|
146
|
+
"""Validate account_id format."""
|
147
|
+
if len(value) != 32:
|
148
|
+
raise serializers.ValidationError(
|
149
|
+
"Account ID must be exactly 32 characters long"
|
150
|
+
)
|
151
|
+
return value
|
152
|
+
|
153
|
+
|
154
|
+
class CloudflareSiteListSerializer(serializers.ModelSerializer):
|
155
|
+
"""Lightweight serializer for site lists."""
|
156
|
+
|
157
|
+
owner = serializers.StringRelatedField(read_only=True)
|
158
|
+
is_production = serializers.ReadOnlyField()
|
159
|
+
|
160
|
+
class Meta:
|
161
|
+
model = CloudflareSite
|
162
|
+
fields = [
|
163
|
+
'id', 'name', 'domain', 'environment', 'project', 'tags',
|
164
|
+
'owner', 'current_status', 'maintenance_active', 'is_production',
|
165
|
+
'last_maintenance_at', 'created_at'
|
166
|
+
]
|
167
|
+
read_only_fields = fields
|
168
|
+
|
169
|
+
|
170
|
+
class SiteGroupSerializer(serializers.ModelSerializer):
|
171
|
+
"""Serializer for SiteGroup model."""
|
172
|
+
|
173
|
+
owner = UserSerializer(read_only=True)
|
174
|
+
sites = CloudflareSiteListSerializer(many=True, read_only=True)
|
175
|
+
sites_count = serializers.ReadOnlyField()
|
176
|
+
active_sites_count = serializers.ReadOnlyField()
|
177
|
+
maintenance_sites_count = serializers.ReadOnlyField()
|
178
|
+
|
179
|
+
class Meta:
|
180
|
+
model = SiteGroup
|
181
|
+
fields = [
|
182
|
+
'id', 'name', 'description', 'owner', 'sites', 'sites_count',
|
183
|
+
'active_sites_count', 'maintenance_sites_count', 'created_at', 'updated_at'
|
184
|
+
]
|
185
|
+
read_only_fields = [
|
186
|
+
'id', 'owner', 'sites', 'sites_count', 'active_sites_count',
|
187
|
+
'maintenance_sites_count', 'created_at', 'updated_at'
|
188
|
+
]
|
189
|
+
extra_kwargs = {
|
190
|
+
'name': {
|
191
|
+
'help_text': 'Name of the site group'
|
192
|
+
},
|
193
|
+
'description': {
|
194
|
+
'help_text': 'Optional description of the group purpose'
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
|
199
|
+
class SiteGroupCreateSerializer(serializers.ModelSerializer):
|
200
|
+
"""Serializer for creating SiteGroup."""
|
201
|
+
|
202
|
+
class Meta:
|
203
|
+
model = SiteGroup
|
204
|
+
fields = ['name', 'description']
|
205
|
+
|
206
|
+
def validate_name(self, value):
|
207
|
+
"""Validate group name uniqueness for user."""
|
208
|
+
user = self.context['request'].user
|
209
|
+
if SiteGroup.objects.filter(owner=user, name=value).exists():
|
210
|
+
raise serializers.ValidationError(
|
211
|
+
"You already have a group with this name"
|
212
|
+
)
|
213
|
+
return value
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# Maintenance Services
|
2
|
+
|
3
|
+
Modern Cloudflare services using the official `cloudflare` Python library.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
This package provides clean, type-safe, and reliable integration with Cloudflare API v4 for maintenance mode management.
|
8
|
+
|
9
|
+
## Services
|
10
|
+
|
11
|
+
### CloudflareClient
|
12
|
+
Core client with rate limiting, retry logic, and error handling.
|
13
|
+
|
14
|
+
```python
|
15
|
+
from django_cfg.apps.maintenance.services import CloudflareClient
|
16
|
+
from django_cfg.models.cloudflare import CloudflareConfig
|
17
|
+
|
18
|
+
config = CloudflareConfig(api_token="your_token", domain="example.com")
|
19
|
+
client = CloudflareClient(config)
|
20
|
+
|
21
|
+
# List zones
|
22
|
+
zones = client.list_zones()
|
23
|
+
|
24
|
+
# Create DNS record
|
25
|
+
record = client.create_dns_record(
|
26
|
+
zone_id="zone_id",
|
27
|
+
record_type="A",
|
28
|
+
name="api.example.com",
|
29
|
+
content="1.2.3.4"
|
30
|
+
)
|
31
|
+
```
|
32
|
+
|
33
|
+
### SiteSyncService
|
34
|
+
Synchronizes Django CloudflareSite models with Cloudflare zones.
|
35
|
+
|
36
|
+
```python
|
37
|
+
from django_cfg.apps.maintenance.services import SiteSyncService
|
38
|
+
|
39
|
+
sync_service = SiteSyncService(config)
|
40
|
+
|
41
|
+
# Sync all sites for a user
|
42
|
+
stats = sync_service.sync_user_sites(
|
43
|
+
user=user,
|
44
|
+
dry_run=False,
|
45
|
+
force_update=True
|
46
|
+
)
|
47
|
+
```
|
48
|
+
|
49
|
+
### MaintenanceManager
|
50
|
+
Manages maintenance mode using Cloudflare Workers.
|
51
|
+
|
52
|
+
```python
|
53
|
+
from django_cfg.apps.maintenance.services import MaintenanceManager
|
54
|
+
|
55
|
+
manager = MaintenanceManager(config)
|
56
|
+
|
57
|
+
# Enable maintenance mode
|
58
|
+
result = manager.enable_maintenance(
|
59
|
+
site=site,
|
60
|
+
maintenance_event=event,
|
61
|
+
custom_message="We'll be back soon!"
|
62
|
+
)
|
63
|
+
|
64
|
+
# Disable maintenance mode
|
65
|
+
result = manager.disable_maintenance(site=site)
|
66
|
+
```
|
67
|
+
|
68
|
+
### WorkerManager
|
69
|
+
High-level Workers management.
|
70
|
+
|
71
|
+
```python
|
72
|
+
from django_cfg.apps.maintenance.services import WorkerManager
|
73
|
+
|
74
|
+
worker_manager = WorkerManager(config)
|
75
|
+
|
76
|
+
# Deploy a Worker
|
77
|
+
result = worker_manager.deploy_worker(
|
78
|
+
account_id="account_id",
|
79
|
+
script_name="my-worker",
|
80
|
+
script_content=worker_code,
|
81
|
+
routes=[{"zone_id": "zone_id", "pattern": "example.com/*"}]
|
82
|
+
)
|
83
|
+
```
|
84
|
+
|
85
|
+
### DNSManager
|
86
|
+
DNS operations with validation and bulk operations.
|
87
|
+
|
88
|
+
```python
|
89
|
+
from django_cfg.apps.maintenance.services import DNSManager
|
90
|
+
|
91
|
+
dns_manager = DNSManager(config)
|
92
|
+
|
93
|
+
# Create DNS record
|
94
|
+
result = dns_manager.create_dns_record(
|
95
|
+
zone_id="zone_id",
|
96
|
+
record_type="A",
|
97
|
+
name="api.example.com",
|
98
|
+
content="1.2.3.4"
|
99
|
+
)
|
100
|
+
|
101
|
+
# Bulk create records
|
102
|
+
result = dns_manager.bulk_create_records(
|
103
|
+
zone_id="zone_id",
|
104
|
+
records=[
|
105
|
+
{"record_type": "A", "name": "api", "content": "1.2.3.4"},
|
106
|
+
{"record_type": "CNAME", "name": "www", "content": "example.com"}
|
107
|
+
]
|
108
|
+
)
|
109
|
+
```
|
110
|
+
|
111
|
+
## Features
|
112
|
+
|
113
|
+
- **Official Library**: Uses the official `cloudflare` Python library
|
114
|
+
- **Rate Limiting**: Built-in rate limiting and retry logic
|
115
|
+
- **Error Handling**: Comprehensive error handling with fallbacks
|
116
|
+
- **Type Safety**: Full type hints and validation
|
117
|
+
- **Bulk Operations**: Support for bulk DNS and Worker operations
|
118
|
+
- **Logging**: Detailed logging for debugging and monitoring
|
119
|
+
- **Django Integration**: Seamless integration with Django models
|
120
|
+
|
121
|
+
## Admin Integration
|
122
|
+
|
123
|
+
The services are integrated with Django Admin through Unfold actions:
|
124
|
+
|
125
|
+
- **Sync with Cloudflare**: Sync site DNS records
|
126
|
+
- **Validate Configuration**: Validate site configuration against Cloudflare
|
127
|
+
- **Bulk Operations**: Enable/disable maintenance for multiple sites
|
128
|
+
|
129
|
+
## Management Commands
|
130
|
+
|
131
|
+
```bash
|
132
|
+
# Sync sites with Cloudflare
|
133
|
+
python manage.py sync_cloudflare --user admin@example.com --api-token your_token
|
134
|
+
|
135
|
+
# Dry run
|
136
|
+
python manage.py sync_cloudflare --user admin@example.com --api-token your_token --dry-run
|
137
|
+
```
|
138
|
+
|
139
|
+
## Requirements
|
140
|
+
|
141
|
+
- `cloudflare>=3.0.0`
|
142
|
+
- Django 5.2+
|
143
|
+
- Python 3.10+
|
144
|
+
|
145
|
+
## Migration from Old Services
|
146
|
+
|
147
|
+
The old services in `services_old/` used manual HTTP requests. The new services provide:
|
148
|
+
|
149
|
+
1. **Better Error Handling**: Automatic retries and rate limiting
|
150
|
+
2. **Type Safety**: Full type hints and validation
|
151
|
+
3. **Official Support**: Uses the official Cloudflare library
|
152
|
+
4. **Modern Architecture**: Clean, maintainable code structure
|
153
|
+
5. **Better Testing**: Easier to mock and test
|
154
|
+
|
155
|
+
## Configuration
|
156
|
+
|
157
|
+
Services use `CloudflareConfig` from `django_cfg.models.cloudflare`:
|
158
|
+
|
159
|
+
```python
|
160
|
+
from django_cfg.models.cloudflare import CloudflareConfig
|
161
|
+
|
162
|
+
config = CloudflareConfig(
|
163
|
+
api_token="your_cloudflare_api_token",
|
164
|
+
domain="your_domain.com"
|
165
|
+
)
|
166
|
+
```
|
167
|
+
|
168
|
+
For production, store API tokens securely and implement proper token rotation.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
"""
|
2
|
+
Modern Cloudflare services using official cloudflare library.
|
3
|
+
|
4
|
+
Provides clean, type-safe, and reliable integration with Cloudflare API v4.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .cloudflare_client import CloudflareClient
|
8
|
+
from .maintenance_manager import MaintenanceManager
|
9
|
+
from .site_sync import SiteSyncService
|
10
|
+
from .worker_manager import WorkerManager
|
11
|
+
from .dns_manager import DNSManager
|
12
|
+
from .sync_command_service import SyncCommandService
|
13
|
+
|
14
|
+
__all__ = [
|
15
|
+
'CloudflareClient',
|
16
|
+
'MaintenanceManager',
|
17
|
+
'SiteSyncService',
|
18
|
+
'WorkerManager',
|
19
|
+
'DNSManager',
|
20
|
+
'SyncCommandService',
|
21
|
+
]
|