django-nativemojo 0.1.10__py3-none-any.whl → 0.1.16__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_nativemojo-0.1.16.dist-info/METADATA +138 -0
- django_nativemojo-0.1.16.dist-info/RECORD +302 -0
- mojo/__init__.py +1 -1
- mojo/apps/account/management/__init__.py +5 -0
- mojo/apps/account/management/commands/__init__.py +6 -0
- mojo/apps/account/management/commands/serializer_admin.py +651 -0
- mojo/apps/account/migrations/0004_user_avatar.py +20 -0
- mojo/apps/account/migrations/0005_group_last_activity.py +18 -0
- mojo/apps/account/migrations/0006_add_device_tracking_models.py +72 -0
- mojo/apps/account/migrations/0007_delete_userdevicelocation.py +16 -0
- mojo/apps/account/migrations/0008_userdevicelocation.py +33 -0
- mojo/apps/account/migrations/0009_geolocatedip_subnet.py +18 -0
- mojo/apps/account/migrations/0010_group_avatar.py +20 -0
- mojo/apps/account/migrations/0011_user_org_registereddevice_pushconfig_and_more.py +118 -0
- mojo/apps/account/migrations/0012_remove_pushconfig_apns_key_file_and_more.py +21 -0
- mojo/apps/account/migrations/0013_pushconfig_test_mode_alter_pushconfig_apns_enabled_and_more.py +28 -0
- mojo/apps/account/migrations/0014_notificationdelivery_data_payload_and_more.py +48 -0
- mojo/apps/account/models/__init__.py +2 -0
- mojo/apps/account/models/device.py +281 -0
- mojo/apps/account/models/group.py +319 -15
- mojo/apps/account/models/member.py +29 -5
- mojo/apps/account/models/push/__init__.py +4 -0
- mojo/apps/account/models/push/config.py +112 -0
- mojo/apps/account/models/push/delivery.py +93 -0
- mojo/apps/account/models/push/device.py +66 -0
- mojo/apps/account/models/push/template.py +99 -0
- mojo/apps/account/models/user.py +369 -19
- mojo/apps/account/rest/__init__.py +2 -0
- mojo/apps/account/rest/device.py +39 -0
- mojo/apps/account/rest/group.py +9 -0
- mojo/apps/account/rest/push.py +187 -0
- mojo/apps/account/rest/user.py +100 -6
- mojo/apps/account/services/__init__.py +1 -0
- mojo/apps/account/services/push.py +363 -0
- mojo/apps/aws/migrations/0001_initial.py +206 -0
- mojo/apps/aws/migrations/0002_emaildomain_can_recv_emaildomain_can_send_and_more.py +28 -0
- mojo/apps/aws/migrations/0003_mailbox_is_domain_default_mailbox_is_system_default_and_more.py +31 -0
- mojo/apps/aws/migrations/0004_s3bucket.py +39 -0
- mojo/apps/aws/migrations/0005_alter_emaildomain_region_delete_s3bucket.py +21 -0
- mojo/apps/aws/models/__init__.py +19 -0
- mojo/apps/aws/models/email_attachment.py +99 -0
- mojo/apps/aws/models/email_domain.py +218 -0
- mojo/apps/aws/models/email_template.py +132 -0
- mojo/apps/aws/models/incoming_email.py +197 -0
- mojo/apps/aws/models/mailbox.py +288 -0
- mojo/apps/aws/models/sent_message.py +175 -0
- mojo/apps/aws/rest/__init__.py +7 -0
- mojo/apps/aws/rest/email.py +33 -0
- mojo/apps/aws/rest/email_ops.py +183 -0
- mojo/apps/aws/rest/messages.py +32 -0
- mojo/apps/aws/rest/s3.py +64 -0
- mojo/apps/aws/rest/send.py +101 -0
- mojo/apps/aws/rest/sns.py +403 -0
- mojo/apps/aws/rest/templates.py +19 -0
- mojo/apps/aws/services/__init__.py +32 -0
- mojo/apps/aws/services/email.py +390 -0
- mojo/apps/aws/services/email_ops.py +548 -0
- mojo/apps/docit/__init__.py +6 -0
- mojo/apps/docit/markdown_plugins/syntax_highlight.py +25 -0
- mojo/apps/docit/markdown_plugins/toc.py +12 -0
- mojo/apps/docit/migrations/0001_initial.py +113 -0
- mojo/apps/docit/migrations/0002_alter_book_modified_by_alter_page_modified_by.py +26 -0
- mojo/apps/docit/migrations/0003_alter_book_group.py +20 -0
- mojo/apps/docit/models/__init__.py +17 -0
- mojo/apps/docit/models/asset.py +231 -0
- mojo/apps/docit/models/book.py +227 -0
- mojo/apps/docit/models/page.py +319 -0
- mojo/apps/docit/models/page_revision.py +203 -0
- mojo/apps/docit/rest/__init__.py +10 -0
- mojo/apps/docit/rest/asset.py +17 -0
- mojo/apps/docit/rest/book.py +22 -0
- mojo/apps/docit/rest/page.py +22 -0
- mojo/apps/docit/rest/page_revision.py +17 -0
- mojo/apps/docit/services/__init__.py +11 -0
- mojo/apps/docit/services/docit.py +315 -0
- mojo/apps/docit/services/markdown.py +44 -0
- mojo/apps/fileman/README.md +8 -8
- mojo/apps/fileman/backends/base.py +76 -70
- mojo/apps/fileman/backends/filesystem.py +86 -86
- mojo/apps/fileman/backends/s3.py +409 -108
- mojo/apps/fileman/migrations/0001_initial.py +106 -0
- mojo/apps/fileman/migrations/0002_filemanager_parent_alter_filemanager_max_file_size.py +24 -0
- mojo/apps/fileman/migrations/0003_remove_file_fileman_fil_upload__c4bc35_idx_and_more.py +25 -0
- mojo/apps/fileman/migrations/0004_remove_file_original_filename_and_more.py +39 -0
- mojo/apps/fileman/migrations/0005_alter_file_upload_token.py +18 -0
- mojo/apps/fileman/migrations/0006_file_download_url_filemanager_forever_urls.py +23 -0
- mojo/apps/fileman/migrations/0007_remove_filemanager_forever_urls_and_more.py +22 -0
- mojo/apps/fileman/migrations/0008_file_category.py +18 -0
- mojo/apps/fileman/migrations/0009_rename_file_path_file_storage_file_path.py +18 -0
- mojo/apps/fileman/migrations/0010_filerendition.py +33 -0
- mojo/apps/fileman/migrations/0011_alter_filerendition_original_file.py +19 -0
- mojo/apps/fileman/models/__init__.py +1 -5
- mojo/apps/fileman/models/file.py +240 -58
- mojo/apps/fileman/models/manager.py +427 -31
- mojo/apps/fileman/models/rendition.py +118 -0
- mojo/apps/fileman/renderer/__init__.py +111 -0
- mojo/apps/fileman/renderer/audio.py +403 -0
- mojo/apps/fileman/renderer/base.py +205 -0
- mojo/apps/fileman/renderer/document.py +404 -0
- mojo/apps/fileman/renderer/image.py +222 -0
- mojo/apps/fileman/renderer/utils.py +297 -0
- mojo/apps/fileman/renderer/video.py +304 -0
- mojo/apps/fileman/rest/__init__.py +1 -18
- mojo/apps/fileman/rest/upload.py +22 -32
- mojo/apps/fileman/signals.py +58 -0
- mojo/apps/fileman/tasks.py +254 -0
- mojo/apps/fileman/utils/__init__.py +40 -16
- mojo/apps/incident/migrations/0005_incidenthistory.py +39 -0
- mojo/apps/incident/migrations/0006_alter_incident_state.py +18 -0
- mojo/apps/incident/migrations/0007_event_uid.py +18 -0
- mojo/apps/incident/migrations/0008_ticket_ticketnote.py +55 -0
- mojo/apps/incident/migrations/0009_incident_status.py +18 -0
- mojo/apps/incident/migrations/0010_event_country_code.py +18 -0
- mojo/apps/incident/migrations/0011_incident_country_code.py +18 -0
- mojo/apps/incident/migrations/0012_alter_incident_status.py +18 -0
- mojo/apps/incident/models/__init__.py +2 -0
- mojo/apps/incident/models/event.py +35 -0
- mojo/apps/incident/models/history.py +36 -0
- mojo/apps/incident/models/incident.py +3 -1
- mojo/apps/incident/models/ticket.py +62 -0
- mojo/apps/incident/reporter.py +21 -1
- mojo/apps/incident/rest/__init__.py +1 -0
- mojo/apps/incident/rest/event.py +7 -1
- mojo/apps/incident/rest/ticket.py +43 -0
- mojo/apps/jobs/__init__.py +489 -0
- mojo/apps/jobs/adapters.py +24 -0
- mojo/apps/jobs/cli.py +616 -0
- mojo/apps/jobs/daemon.py +370 -0
- mojo/apps/jobs/examples/sample_jobs.py +376 -0
- mojo/apps/jobs/examples/webhook_examples.py +203 -0
- mojo/apps/jobs/handlers/__init__.py +5 -0
- mojo/apps/jobs/handlers/webhook.py +317 -0
- mojo/apps/jobs/job_engine.py +734 -0
- mojo/apps/jobs/keys.py +203 -0
- mojo/apps/jobs/local_queue.py +363 -0
- mojo/apps/jobs/management/__init__.py +3 -0
- mojo/apps/jobs/management/commands/__init__.py +3 -0
- mojo/apps/jobs/manager.py +1327 -0
- mojo/apps/jobs/migrations/0001_initial.py +97 -0
- mojo/apps/jobs/migrations/0002_alter_job_max_retries_joblog.py +39 -0
- mojo/apps/jobs/models/__init__.py +6 -0
- mojo/apps/jobs/models/job.py +441 -0
- mojo/apps/jobs/rest/__init__.py +2 -0
- mojo/apps/jobs/rest/control.py +466 -0
- mojo/apps/jobs/rest/jobs.py +421 -0
- mojo/apps/jobs/scheduler.py +571 -0
- mojo/apps/jobs/services/__init__.py +6 -0
- mojo/apps/jobs/services/job_actions.py +465 -0
- mojo/apps/jobs/settings.py +209 -0
- mojo/apps/logit/migrations/0004_alter_log_level.py +18 -0
- mojo/apps/logit/models/log.py +7 -1
- mojo/apps/metrics/__init__.py +8 -1
- mojo/apps/metrics/redis_metrics.py +198 -0
- mojo/apps/metrics/rest/__init__.py +3 -0
- mojo/apps/metrics/rest/categories.py +266 -0
- mojo/apps/metrics/rest/helpers.py +48 -0
- mojo/apps/metrics/rest/permissions.py +99 -0
- mojo/apps/metrics/rest/values.py +277 -0
- mojo/apps/metrics/utils.py +19 -2
- mojo/decorators/auth.py +6 -1
- mojo/decorators/http.py +47 -3
- mojo/helpers/aws/__init__.py +45 -0
- mojo/helpers/aws/ec2.py +804 -0
- mojo/helpers/aws/iam.py +748 -0
- mojo/helpers/aws/inbound_email.py +309 -0
- mojo/helpers/aws/kms.py +413 -0
- mojo/helpers/aws/s3.py +451 -11
- mojo/helpers/aws/ses.py +483 -0
- mojo/helpers/aws/ses_domain.py +959 -0
- mojo/helpers/aws/sns.py +461 -0
- mojo/helpers/crypto/__init__.py +1 -1
- mojo/helpers/crypto/utils.py +15 -0
- mojo/helpers/dates.py +18 -0
- mojo/helpers/location/__init__.py +2 -0
- mojo/helpers/location/countries.py +262 -0
- mojo/helpers/location/geolocation.py +196 -0
- mojo/helpers/logit.py +37 -0
- mojo/helpers/redis/__init__.py +2 -0
- mojo/helpers/redis/adapter.py +606 -0
- mojo/helpers/redis/client.py +48 -0
- mojo/helpers/redis/pool.py +225 -0
- mojo/helpers/request.py +8 -0
- mojo/helpers/response.py +14 -2
- mojo/helpers/settings/__init__.py +2 -0
- mojo/helpers/{settings.py → settings/helper.py} +1 -37
- mojo/helpers/settings/parser.py +132 -0
- mojo/middleware/auth.py +1 -1
- mojo/middleware/cors.py +40 -0
- mojo/middleware/logging.py +131 -12
- mojo/middleware/mojo.py +10 -0
- mojo/models/rest.py +494 -65
- mojo/models/secrets.py +98 -3
- mojo/serializers/__init__.py +106 -0
- mojo/serializers/core/__init__.py +90 -0
- mojo/serializers/core/cache/__init__.py +121 -0
- mojo/serializers/core/cache/backends.py +518 -0
- mojo/serializers/core/cache/base.py +102 -0
- mojo/serializers/core/cache/disabled.py +181 -0
- mojo/serializers/core/cache/memory.py +287 -0
- mojo/serializers/core/cache/redis.py +533 -0
- mojo/serializers/core/cache/utils.py +454 -0
- mojo/serializers/core/manager.py +550 -0
- mojo/serializers/core/serializer.py +475 -0
- mojo/serializers/examples/settings.py +322 -0
- mojo/serializers/formats/csv.py +393 -0
- mojo/serializers/formats/localizers.py +509 -0
- mojo/serializers/{models.py → simple.py} +38 -15
- mojo/serializers/suggested_improvements.md +388 -0
- testit/client.py +1 -1
- testit/helpers.py +35 -4
- testit/runner.py +23 -6
- django_nativemojo-0.1.10.dist-info/METADATA +0 -96
- django_nativemojo-0.1.10.dist-info/RECORD +0 -194
- mojo/apps/metrics/rest/db.py +0 -0
- mojo/apps/notify/README.md +0 -91
- mojo/apps/notify/README_NOTIFICATIONS.md +0 -566
- mojo/apps/notify/admin.py +0 -52
- mojo/apps/notify/handlers/example_handlers.py +0 -516
- mojo/apps/notify/handlers/ses/__init__.py +0 -25
- mojo/apps/notify/handlers/ses/bounce.py +0 -0
- mojo/apps/notify/handlers/ses/complaint.py +0 -25
- mojo/apps/notify/handlers/ses/message.py +0 -86
- mojo/apps/notify/management/commands/__init__.py +0 -1
- mojo/apps/notify/management/commands/process_notifications.py +0 -370
- mojo/apps/notify/mod +0 -0
- mojo/apps/notify/models/__init__.py +0 -12
- mojo/apps/notify/models/account.py +0 -128
- mojo/apps/notify/models/attachment.py +0 -24
- mojo/apps/notify/models/bounce.py +0 -68
- mojo/apps/notify/models/complaint.py +0 -40
- mojo/apps/notify/models/inbox.py +0 -113
- mojo/apps/notify/models/inbox_message.py +0 -173
- mojo/apps/notify/models/outbox.py +0 -129
- mojo/apps/notify/models/outbox_message.py +0 -288
- mojo/apps/notify/models/template.py +0 -30
- mojo/apps/notify/providers/aws.py +0 -73
- mojo/apps/notify/rest/ses.py +0 -0
- mojo/apps/notify/utils/__init__.py +0 -2
- mojo/apps/notify/utils/notifications.py +0 -404
- mojo/apps/notify/utils/parsing.py +0 -202
- mojo/apps/notify/utils/render.py +0 -144
- mojo/apps/tasks/README.md +0 -118
- mojo/apps/tasks/__init__.py +0 -11
- mojo/apps/tasks/manager.py +0 -489
- mojo/apps/tasks/rest/__init__.py +0 -2
- mojo/apps/tasks/rest/hooks.py +0 -0
- mojo/apps/tasks/rest/tasks.py +0 -62
- mojo/apps/tasks/runner.py +0 -174
- mojo/apps/tasks/tq_handlers.py +0 -14
- mojo/helpers/aws/setup_email.py +0 -0
- mojo/helpers/redis.py +0 -10
- mojo/models/meta.py +0 -262
- mojo/ws4redis/README.md +0 -174
- mojo/ws4redis/__init__.py +0 -2
- mojo/ws4redis/client.py +0 -283
- mojo/ws4redis/connection.py +0 -327
- mojo/ws4redis/exceptions.py +0 -32
- mojo/ws4redis/redis.py +0 -183
- mojo/ws4redis/servers/base.py +0 -86
- mojo/ws4redis/servers/django.py +0 -171
- mojo/ws4redis/servers/uwsgi.py +0 -63
- mojo/ws4redis/settings.py +0 -45
- mojo/ws4redis/utf8validator.py +0 -128
- mojo/ws4redis/websocket.py +0 -403
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.16.dist-info}/LICENSE +0 -0
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.16.dist-info}/NOTICE +0 -0
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.16.dist-info}/WHEEL +0 -0
- /mojo/apps/{notify → aws}/__init__.py +0 -0
- /mojo/apps/{notify/handlers → aws/migrations}/__init__.py +0 -0
- /mojo/apps/{notify/management → docit/markdown_plugins}/__init__.py +0 -0
- /mojo/apps/{notify/providers → docit/migrations}/__init__.py +0 -0
- /mojo/apps/{notify/rest → fileman/migrations}/__init__.py +0 -0
- /mojo/{ws4redis/servers → apps/jobs/examples}/__init__.py +0 -0
- /mojo/apps/{fileman/models/render.py → jobs/migrations/__init__.py} +0 -0
- /mojo/{serializers → rest}/openapi.py +0 -0
- /mojo/{apps/fileman/rest/__init__ → serializers/formats/__init__.py} +0 -0
@@ -0,0 +1,421 @@
|
|
1
|
+
from mojo import decorators as md
|
2
|
+
from mojo.helpers.response import JsonResponse
|
3
|
+
from mojo.helpers import logit
|
4
|
+
from mojo.helpers.settings import settings
|
5
|
+
from mojo.apps.jobs.models import Job, JobEvent, JobLog
|
6
|
+
from mojo.apps.jobs.manager import get_manager
|
7
|
+
from mojo.apps.jobs import publish, cancel, status
|
8
|
+
from django.utils import timezone
|
9
|
+
from django.db.models import Q
|
10
|
+
import json
|
11
|
+
|
12
|
+
|
13
|
+
# Basic CRUD for Jobs (with RestMeta permissions)
|
14
|
+
@md.URL('job')
|
15
|
+
@md.URL('job/<str:pk>')
|
16
|
+
def on_job(request, pk=None):
|
17
|
+
"""Standard CRUD operations for jobs with automatic permission handling."""
|
18
|
+
|
19
|
+
return Job.on_rest_request(request, pk)
|
20
|
+
|
21
|
+
|
22
|
+
# Basic CRUD for Job Events
|
23
|
+
@md.URL('event')
|
24
|
+
@md.URL('event/<int:pk>')
|
25
|
+
def on_job_event(request, pk=None):
|
26
|
+
"""Standard CRUD operations for job events."""
|
27
|
+
return JobEvent.on_rest_request(request, pk)
|
28
|
+
|
29
|
+
|
30
|
+
# Basic CRUD for Job Logs
|
31
|
+
@md.URL('logs')
|
32
|
+
@md.URL('logs/<int:pk>')
|
33
|
+
def on_job_logs(request, pk=None):
|
34
|
+
"""Standard CRUD operations for job logs."""
|
35
|
+
return JobLog.on_rest_request(request, pk)
|
36
|
+
|
37
|
+
|
38
|
+
# Get job status
|
39
|
+
@md.GET('status/<str:job_id>')
|
40
|
+
@md.requires_perms('manage_jobs', 'view_jobs')
|
41
|
+
def on_get_job_status(request, job_id):
|
42
|
+
"""Get the current status of a job."""
|
43
|
+
try:
|
44
|
+
job_status = status(job_id)
|
45
|
+
|
46
|
+
if job_status is None:
|
47
|
+
return JsonResponse({
|
48
|
+
'status': False,
|
49
|
+
'error': 'Job not found'
|
50
|
+
}, status=404)
|
51
|
+
|
52
|
+
return JsonResponse({
|
53
|
+
'status': True,
|
54
|
+
'data': job_status
|
55
|
+
})
|
56
|
+
|
57
|
+
except Exception as e:
|
58
|
+
return JsonResponse({
|
59
|
+
'status': False,
|
60
|
+
'error': str(e)
|
61
|
+
}, status=400)
|
62
|
+
|
63
|
+
|
64
|
+
# Cancel a job
|
65
|
+
@md.POST('cancel')
|
66
|
+
@md.requires_perms('manage_jobs')
|
67
|
+
@md.requires_params('job_id')
|
68
|
+
def on_cancel_job(request):
|
69
|
+
"""Request cancellation of a job."""
|
70
|
+
try:
|
71
|
+
job_id = request.DATA['job_id']
|
72
|
+
result = cancel(job_id)
|
73
|
+
|
74
|
+
return JsonResponse({
|
75
|
+
'status': result,
|
76
|
+
'message': f'Job {job_id} cancellation {"requested" if result else "failed"}'
|
77
|
+
})
|
78
|
+
|
79
|
+
except Exception as e:
|
80
|
+
return JsonResponse({
|
81
|
+
'status': False,
|
82
|
+
'error': str(e)
|
83
|
+
}, status=400)
|
84
|
+
|
85
|
+
|
86
|
+
# Retry a job
|
87
|
+
@md.POST('retry')
|
88
|
+
@md.requires_perms('manage_jobs')
|
89
|
+
@md.requires_params('job_id')
|
90
|
+
def on_retry_job(request):
|
91
|
+
"""Retry a failed or canceled job."""
|
92
|
+
try:
|
93
|
+
job_id = request.DATA['job_id']
|
94
|
+
delay = request.DATA.get('delay')
|
95
|
+
|
96
|
+
# Get the job
|
97
|
+
try:
|
98
|
+
job = Job.objects.get(id=job_id)
|
99
|
+
except Job.DoesNotExist:
|
100
|
+
return JsonResponse({
|
101
|
+
'status': False,
|
102
|
+
'error': 'Job not found'
|
103
|
+
}, status=404)
|
104
|
+
|
105
|
+
# Use the service to retry
|
106
|
+
from mojo.apps.jobs.services import JobActionsService
|
107
|
+
result = JobActionsService.retry_job(job, delay=delay)
|
108
|
+
|
109
|
+
return JsonResponse(result)
|
110
|
+
|
111
|
+
except Exception as e:
|
112
|
+
return JsonResponse({
|
113
|
+
'status': False,
|
114
|
+
'error': str(e)
|
115
|
+
}, status=400)
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
# Get channel health
|
120
|
+
@md.GET('health/<str:channel>')
|
121
|
+
@md.requires_perms('manage_jobs', 'view_jobs')
|
122
|
+
def on_channel_health(request, channel):
|
123
|
+
"""Get comprehensive health metrics for a channel."""
|
124
|
+
try:
|
125
|
+
manager = get_manager()
|
126
|
+
health = manager.get_channel_health(channel)
|
127
|
+
|
128
|
+
return JsonResponse({
|
129
|
+
'status': True,
|
130
|
+
'data': health
|
131
|
+
})
|
132
|
+
|
133
|
+
except Exception as e:
|
134
|
+
return JsonResponse({
|
135
|
+
'status': False,
|
136
|
+
'error': str(e)
|
137
|
+
}, status=400)
|
138
|
+
|
139
|
+
|
140
|
+
# Get all channels health
|
141
|
+
@md.GET('health')
|
142
|
+
@md.requires_perms('manage_jobs', 'view_jobs')
|
143
|
+
def on_health_overview(request):
|
144
|
+
"""Get health overview for all configured channels."""
|
145
|
+
try:
|
146
|
+
from django.conf import settings
|
147
|
+
manager = get_manager()
|
148
|
+
|
149
|
+
channels = getattr(settings, 'JOBS_CHANNELS', ['default'])
|
150
|
+
health_data = {}
|
151
|
+
|
152
|
+
for channel in channels:
|
153
|
+
health_data[channel] = manager.get_channel_health(channel)
|
154
|
+
|
155
|
+
# Calculate aggregate stats
|
156
|
+
total_unclaimed = sum(h['messages']['unclaimed'] for h in health_data.values())
|
157
|
+
total_pending = sum(h['messages']['pending'] for h in health_data.values())
|
158
|
+
total_stuck = sum(h['messages']['stuck'] for h in health_data.values())
|
159
|
+
total_runners = sum(h['runners']['active'] for h in health_data.values())
|
160
|
+
|
161
|
+
# Determine overall status
|
162
|
+
overall_status = 'healthy'
|
163
|
+
if any(h['status'] == 'critical' for h in health_data.values()):
|
164
|
+
overall_status = 'critical'
|
165
|
+
elif any(h['status'] == 'warning' for h in health_data.values()):
|
166
|
+
overall_status = 'warning'
|
167
|
+
|
168
|
+
return JsonResponse({
|
169
|
+
'status': True,
|
170
|
+
'data': {
|
171
|
+
'overall_status': overall_status,
|
172
|
+
'totals': {
|
173
|
+
'unclaimed': total_unclaimed,
|
174
|
+
'pending': total_pending,
|
175
|
+
'stuck': total_stuck,
|
176
|
+
'runners': total_runners
|
177
|
+
},
|
178
|
+
'channels': health_data
|
179
|
+
}
|
180
|
+
})
|
181
|
+
|
182
|
+
except Exception as e:
|
183
|
+
return JsonResponse({
|
184
|
+
'status': False,
|
185
|
+
'error': str(e)
|
186
|
+
}, status=400)
|
187
|
+
|
188
|
+
|
189
|
+
# Get active runners
|
190
|
+
@md.GET('runners')
|
191
|
+
@md.requires_perms('manage_jobs', 'view_jobs')
|
192
|
+
def on_list_runners(request):
|
193
|
+
"""List all active runners with their status."""
|
194
|
+
try:
|
195
|
+
manager = get_manager()
|
196
|
+
|
197
|
+
# Optional channel filter
|
198
|
+
channel = request.DATA.get('channel')
|
199
|
+
runners = manager.get_runners(channel=channel)
|
200
|
+
|
201
|
+
# Set id field for each runner
|
202
|
+
for r in runners:
|
203
|
+
r["id"] = r["runner_id"]
|
204
|
+
|
205
|
+
return JsonResponse({
|
206
|
+
'status': True,
|
207
|
+
'count': len(runners),
|
208
|
+
'data': runners
|
209
|
+
})
|
210
|
+
|
211
|
+
except Exception as e:
|
212
|
+
return JsonResponse({
|
213
|
+
'status': False,
|
214
|
+
'error': str(e)
|
215
|
+
}, status=400)
|
216
|
+
|
217
|
+
|
218
|
+
# Ping a specific runner
|
219
|
+
@md.POST('runners/ping')
|
220
|
+
@md.requires_perms('manage_jobs')
|
221
|
+
@md.requires_params('runner_id')
|
222
|
+
def on_ping_runner(request):
|
223
|
+
"""Ping a specific runner to check if it's responsive."""
|
224
|
+
try:
|
225
|
+
manager = get_manager()
|
226
|
+
runner_id = request.DATA['runner_id']
|
227
|
+
timeout = float(request.DATA.get('timeout', 2.0))
|
228
|
+
|
229
|
+
result = manager.ping(runner_id, timeout=timeout)
|
230
|
+
|
231
|
+
return JsonResponse({
|
232
|
+
'status': True,
|
233
|
+
'runner_id': runner_id,
|
234
|
+
'responsive': result
|
235
|
+
})
|
236
|
+
|
237
|
+
except Exception as e:
|
238
|
+
return JsonResponse({
|
239
|
+
'status': False,
|
240
|
+
'error': str(e)
|
241
|
+
}, status=400)
|
242
|
+
|
243
|
+
|
244
|
+
# Shutdown a runner
|
245
|
+
@md.POST('runners/shutdown')
|
246
|
+
@md.requires_perms('manage_jobs')
|
247
|
+
@md.requires_params('runner_id')
|
248
|
+
def on_shutdown_runner(request):
|
249
|
+
"""Request a runner to shutdown gracefully."""
|
250
|
+
try:
|
251
|
+
manager = get_manager()
|
252
|
+
runner_id = request.DATA['runner_id']
|
253
|
+
graceful = request.DATA.get('graceful', True)
|
254
|
+
|
255
|
+
manager.shutdown(runner_id, graceful=bool(graceful))
|
256
|
+
|
257
|
+
return JsonResponse({
|
258
|
+
'status': True,
|
259
|
+
'message': f'Shutdown command sent to runner {runner_id}'
|
260
|
+
})
|
261
|
+
|
262
|
+
except Exception as e:
|
263
|
+
return JsonResponse({
|
264
|
+
'status': False,
|
265
|
+
'error': str(e)
|
266
|
+
}, status=400)
|
267
|
+
|
268
|
+
|
269
|
+
# Broadcast command to all runners
|
270
|
+
@md.POST('runners/broadcast')
|
271
|
+
@md.requires_perms('manage_jobs')
|
272
|
+
@md.requires_params('command')
|
273
|
+
def on_broadcast_command(request):
|
274
|
+
"""Broadcast a command to all runners."""
|
275
|
+
try:
|
276
|
+
manager = get_manager()
|
277
|
+
command = request.DATA['command']
|
278
|
+
data = request.DATA.get('data', {})
|
279
|
+
timeout = float(request.DATA.get('timeout', 2.0))
|
280
|
+
|
281
|
+
# Validate command
|
282
|
+
valid_commands = ['status', 'shutdown', 'pause', 'resume', 'reload']
|
283
|
+
if command not in valid_commands:
|
284
|
+
return JsonResponse({
|
285
|
+
'status': False,
|
286
|
+
'error': f'Invalid command. Must be one of: {", ".join(valid_commands)}'
|
287
|
+
}, status=400)
|
288
|
+
|
289
|
+
responses = manager.broadcast_command(command, data=data, timeout=timeout)
|
290
|
+
|
291
|
+
return JsonResponse({
|
292
|
+
'status': True,
|
293
|
+
'command': command,
|
294
|
+
'responses_count': len(responses),
|
295
|
+
'responses': responses
|
296
|
+
})
|
297
|
+
|
298
|
+
except Exception as e:
|
299
|
+
return JsonResponse({
|
300
|
+
'status': False,
|
301
|
+
'error': str(e)
|
302
|
+
}, status=400)
|
303
|
+
|
304
|
+
|
305
|
+
# Get system stats
|
306
|
+
@md.GET('stats')
|
307
|
+
@md.requires_perms('manage_jobs', 'view_jobs')
|
308
|
+
def on_system_stats(request):
|
309
|
+
"""Get overall system statistics."""
|
310
|
+
try:
|
311
|
+
manager = get_manager()
|
312
|
+
stats = manager.get_stats()
|
313
|
+
|
314
|
+
return JsonResponse({
|
315
|
+
'status': True,
|
316
|
+
'data': stats
|
317
|
+
})
|
318
|
+
|
319
|
+
except Exception as e:
|
320
|
+
return JsonResponse({
|
321
|
+
'status': False,
|
322
|
+
'error': str(e)
|
323
|
+
}, status=400)
|
324
|
+
|
325
|
+
|
326
|
+
|
327
|
+
@md.POST('test')
|
328
|
+
@md.requires_perms('manage_jobs')
|
329
|
+
def on_system_test(request):
|
330
|
+
from mojo.apps import jobs
|
331
|
+
jobs.publish(
|
332
|
+
"mojo.apps.jobs.examples.sample_jobs.send_email",
|
333
|
+
{
|
334
|
+
"recipients": ["user@example.com"],
|
335
|
+
"subject": "Test Email",
|
336
|
+
"body": "This is a test email."
|
337
|
+
},
|
338
|
+
delay=30
|
339
|
+
)
|
340
|
+
|
341
|
+
jobs.publish(
|
342
|
+
"mojo.apps.jobs.examples.sample_jobs.simulate_long_job",
|
343
|
+
{
|
344
|
+
"delay": 15
|
345
|
+
},
|
346
|
+
channel='priority'
|
347
|
+
)
|
348
|
+
return JsonResponse({
|
349
|
+
'status': True,
|
350
|
+
'message': 'Test job should be running.'
|
351
|
+
})
|
352
|
+
|
353
|
+
|
354
|
+
@md.POST('tests')
|
355
|
+
@md.requires_perms('manage_jobs')
|
356
|
+
def on_system_tests(request):
|
357
|
+
from mojo.apps import jobs
|
358
|
+
import random
|
359
|
+
|
360
|
+
base_job_list = [
|
361
|
+
{
|
362
|
+
"func": "mojo.apps.jobs.examples.sample_jobs.send_email",
|
363
|
+
"payload": {
|
364
|
+
"recipients": ["user@example.com"],
|
365
|
+
"subject": "Test Email",
|
366
|
+
"body": "This is a test email."
|
367
|
+
},
|
368
|
+
"channel": 'email'
|
369
|
+
},
|
370
|
+
{
|
371
|
+
"func": "mojo.apps.jobs.examples.sample_jobs.process_file_upload",
|
372
|
+
"payload": {
|
373
|
+
"file_path": "/path/to/file"
|
374
|
+
},
|
375
|
+
"channel": 'priority'
|
376
|
+
},
|
377
|
+
{
|
378
|
+
"func": "mojo.apps.jobs.examples.sample_jobs.process_file_upload",
|
379
|
+
"payload": {
|
380
|
+
"file_error_path": "/path/to/file"
|
381
|
+
},
|
382
|
+
"channel": 'default'
|
383
|
+
}
|
384
|
+
]
|
385
|
+
|
386
|
+
fetch_job = {
|
387
|
+
"func": "mojo.apps.jobs.examples.sample_jobs.fetch_external_api",
|
388
|
+
"payload": {
|
389
|
+
"url": "https://nativemojo.com/"
|
390
|
+
},
|
391
|
+
"channel": 'webhooks'
|
392
|
+
}
|
393
|
+
|
394
|
+
job_list = []
|
395
|
+
channels = settings.get("JOBS_CHANNELS", ["email"])
|
396
|
+
for channel in channels:
|
397
|
+
j = random.choice(base_job_list)
|
398
|
+
j["channel"] = channel
|
399
|
+
job_list.append(j)
|
400
|
+
|
401
|
+
job_list.append(fetch_job)
|
402
|
+
# lets schedule some jobs as well
|
403
|
+
for i in range(10):
|
404
|
+
j = random.choice(base_job_list)
|
405
|
+
j = j.copy()
|
406
|
+
j["delay"] = random.randint(30, 300)
|
407
|
+
job_list.append(j)
|
408
|
+
|
409
|
+
for i in range(50):
|
410
|
+
j = random.choice(base_job_list)
|
411
|
+
job_list.append(j.copy())
|
412
|
+
|
413
|
+
|
414
|
+
|
415
|
+
|
416
|
+
for jd in job_list:
|
417
|
+
jobs.publish(**jd)
|
418
|
+
return JsonResponse({
|
419
|
+
'status': True,
|
420
|
+
'message': 'Test job should be running.'
|
421
|
+
})
|