django-nativemojo 0.1.15__py3-none-any.whl → 0.1.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_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/METADATA +3 -2
- django_nativemojo-0.1.17.dist-info/RECORD +302 -0
- mojo/__init__.py +1 -1
- mojo/apps/account/management/commands/serializer_admin.py +121 -1
- 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 +279 -0
- mojo/apps/account/models/group.py +294 -8
- mojo/apps/account/models/member.py +14 -1
- 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 +190 -17
- mojo/apps/account/rest/__init__.py +2 -0
- mojo/apps/account/rest/device.py +39 -0
- mojo/apps/account/rest/group.py +8 -0
- mojo/apps/account/rest/push.py +187 -0
- mojo/apps/account/rest/user.py +95 -5
- 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 +6 -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/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/backends/s3.py +209 -0
- mojo/apps/fileman/models/file.py +45 -9
- mojo/apps/fileman/models/manager.py +269 -3
- 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 +1 -0
- mojo/apps/incident/models/event.py +35 -0
- mojo/apps/incident/models/incident.py +2 -0
- mojo/apps/incident/models/ticket.py +62 -0
- mojo/apps/incident/reporter.py +21 -3
- mojo/apps/incident/rest/__init__.py +1 -0
- 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/models/log.py +3 -0
- 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 +17 -0
- mojo/decorators/http.py +40 -1
- mojo/helpers/aws/__init__.py +11 -7
- mojo/helpers/aws/inbound_email.py +309 -0
- mojo/helpers/aws/kms.py +413 -0
- mojo/helpers/aws/ses_domain.py +959 -0
- mojo/helpers/crypto/__init__.py +1 -1
- mojo/helpers/crypto/utils.py +15 -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 +8 -0
- mojo/middleware/auth.py +1 -1
- mojo/middleware/cors.py +40 -0
- mojo/middleware/logging.py +131 -12
- mojo/middleware/mojo.py +5 -0
- mojo/models/rest.py +271 -57
- mojo/models/secrets.py +86 -0
- mojo/serializers/__init__.py +16 -10
- 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/{manager.py → core/manager.py} +53 -4
- mojo/serializers/core/serializer.py +475 -0
- mojo/serializers/{advanced/formats → formats}/csv.py +116 -139
- mojo/serializers/suggested_improvements.md +388 -0
- testit/client.py +1 -1
- testit/helpers.py +14 -0
- testit/runner.py +23 -6
- django_nativemojo-0.1.15.dist-info/RECORD +0 -234
- 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/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 -44
- mojo/apps/tasks/manager.py +0 -644
- mojo/apps/tasks/rest/__init__.py +0 -2
- mojo/apps/tasks/rest/hooks.py +0 -0
- mojo/apps/tasks/rest/tasks.py +0 -76
- mojo/apps/tasks/runner.py +0 -439
- mojo/apps/tasks/task.py +0 -99
- mojo/apps/tasks/tq_handlers.py +0 -132
- mojo/helpers/crypto/__pycache__/hash.cpython-310.pyc +0 -0
- mojo/helpers/crypto/__pycache__/sign.cpython-310.pyc +0 -0
- mojo/helpers/crypto/__pycache__/utils.cpython-310.pyc +0 -0
- mojo/helpers/redis.py +0 -10
- mojo/models/meta.py +0 -262
- mojo/serializers/advanced/README.md +0 -363
- mojo/serializers/advanced/__init__.py +0 -247
- mojo/serializers/advanced/formats/__init__.py +0 -28
- mojo/serializers/advanced/formats/excel.py +0 -516
- mojo/serializers/advanced/formats/json.py +0 -239
- mojo/serializers/advanced/formats/response.py +0 -485
- mojo/serializers/advanced/serializer.py +0 -568
- mojo/serializers/optimized.py +0 -618
- {django_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/LICENSE +0 -0
- {django_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/NOTICE +0 -0
- {django_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/WHEEL +0 -0
- /mojo/apps/{notify → aws/migrations}/__init__.py +0 -0
- /mojo/apps/{notify/handlers → docit/markdown_plugins}/__init__.py +0 -0
- /mojo/apps/{notify/management → docit/migrations}/__init__.py +0 -0
- /mojo/apps/{notify/providers → jobs/examples}/__init__.py +0 -0
- /mojo/apps/{notify/rest → jobs/migrations}/__init__.py +0 -0
- /mojo/{serializers → rest}/openapi.py +0 -0
- /mojo/serializers/{settings_example.py → examples/settings.py} +0 -0
- /mojo/{apps/notify/handlers/ses/bounce.py → serializers/formats/__init__.py} +0 -0
- /mojo/serializers/{advanced/formats → formats}/localizers.py +0 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-08-27 18:23
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
import django.db.models.deletion
|
5
|
+
import mojo.models.rest
|
6
|
+
|
7
|
+
|
8
|
+
class Migration(migrations.Migration):
|
9
|
+
|
10
|
+
initial = True
|
11
|
+
|
12
|
+
dependencies = [
|
13
|
+
]
|
14
|
+
|
15
|
+
operations = [
|
16
|
+
migrations.CreateModel(
|
17
|
+
name='EmailAttachment',
|
18
|
+
fields=[
|
19
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
20
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
21
|
+
('modified', models.DateTimeField(auto_now=True, db_index=True)),
|
22
|
+
('filename', models.CharField(blank=True, help_text='Original filename (if provided by the sender)', max_length=512, null=True)),
|
23
|
+
('content_type', models.CharField(blank=True, help_text='MIME content type (e.g., application/pdf)', max_length=255, null=True)),
|
24
|
+
('size_bytes', models.IntegerField(default=0, help_text='Size of the stored attachment in bytes (approximate)')),
|
25
|
+
('stored_as', models.CharField(help_text='Storage reference (e.g., s3://bucket/key)', max_length=512)),
|
26
|
+
('metadata', models.JSONField(blank=True, default=dict, help_text='Arbitrary metadata (e.g., content-id, part headers)')),
|
27
|
+
],
|
28
|
+
options={
|
29
|
+
'db_table': 'aws_email_attachment',
|
30
|
+
'ordering': ['-created', 'id'],
|
31
|
+
},
|
32
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
33
|
+
),
|
34
|
+
migrations.CreateModel(
|
35
|
+
name='EmailDomain',
|
36
|
+
fields=[
|
37
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
38
|
+
('mojo_secrets', models.TextField(blank=True, default=None, null=True)),
|
39
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
40
|
+
('modified', models.DateTimeField(auto_now=True, db_index=True)),
|
41
|
+
('name', models.CharField(db_index=True, max_length=255, unique=True)),
|
42
|
+
('region', models.CharField(default='us-west-2', help_text='AWS region for SES operations', max_length=64)),
|
43
|
+
('status', models.CharField(db_index=True, default='pending', help_text='High-level status: pending, verified, error (free-form)', max_length=32)),
|
44
|
+
('receiving_enabled', models.BooleanField(default=False, help_text='When true, domain-level catch-all receiving is enabled via SES receipt rules')),
|
45
|
+
('s3_inbound_bucket', models.CharField(blank=True, help_text='S3 bucket for inbound emails (required if receiving_enabled)', max_length=255, null=True)),
|
46
|
+
('s3_inbound_prefix', models.CharField(blank=True, default='', help_text='S3 prefix for inbound emails (e.g., inbound/example.com/)', max_length=255)),
|
47
|
+
('dns_mode', models.CharField(default='manual', help_text='DNS automation mode: manual | route53 | godaddy', max_length=32)),
|
48
|
+
('sns_topic_bounce_arn', models.CharField(blank=True, help_text='SNS topic ARN for SES bounce notifications', max_length=512, null=True)),
|
49
|
+
('sns_topic_complaint_arn', models.CharField(blank=True, help_text='SNS topic ARN for SES complaint notifications', max_length=512, null=True)),
|
50
|
+
('sns_topic_delivery_arn', models.CharField(blank=True, help_text='SNS topic ARN for SES delivery notifications', max_length=512, null=True)),
|
51
|
+
('sns_topic_inbound_arn', models.CharField(blank=True, help_text='SNS topic ARN for SES inbound notifications', max_length=512, null=True)),
|
52
|
+
('metadata', models.JSONField(blank=True, default=dict)),
|
53
|
+
],
|
54
|
+
options={
|
55
|
+
'db_table': 'aws_email_domain',
|
56
|
+
'ordering': ['name'],
|
57
|
+
},
|
58
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
59
|
+
),
|
60
|
+
migrations.CreateModel(
|
61
|
+
name='Mailbox',
|
62
|
+
fields=[
|
63
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
64
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
65
|
+
('modified', models.DateTimeField(auto_now=True, db_index=True)),
|
66
|
+
('email', models.EmailField(db_index=True, help_text='Full email address for this mailbox (e.g., support@example.com)', max_length=254, unique=True)),
|
67
|
+
('allow_inbound', models.BooleanField(default=True, help_text='If true, inbound messages addressed to this mailbox will be processed')),
|
68
|
+
('allow_outbound', models.BooleanField(default=True, help_text='If true, outbound messages can be sent from this mailbox')),
|
69
|
+
('async_handler', models.CharField(blank=True, help_text="Dotted path to async handler: 'package.module:function'", max_length=255, null=True)),
|
70
|
+
('metadata', models.JSONField(blank=True, default=dict)),
|
71
|
+
('domain', models.ForeignKey(help_text='Owning email domain (SES identity)', on_delete=django.db.models.deletion.CASCADE, related_name='mailboxes', to='aws.emaildomain')),
|
72
|
+
],
|
73
|
+
options={
|
74
|
+
'db_table': 'aws_mailbox',
|
75
|
+
'ordering': ['email'],
|
76
|
+
},
|
77
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
78
|
+
),
|
79
|
+
migrations.CreateModel(
|
80
|
+
name='SentMessage',
|
81
|
+
fields=[
|
82
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
83
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
84
|
+
('modified', models.DateTimeField(auto_now=True, db_index=True)),
|
85
|
+
('ses_message_id', models.CharField(blank=True, db_index=True, help_text='AWS SES MessageId returned after a successful send', max_length=255, null=True)),
|
86
|
+
('to_addresses', models.JSONField(blank=True, default=list, help_text='List of recipient addresses (To)')),
|
87
|
+
('cc_addresses', models.JSONField(blank=True, default=list, help_text='List of recipient addresses (Cc)')),
|
88
|
+
('bcc_addresses', models.JSONField(blank=True, default=list, help_text='List of recipient addresses (Bcc)')),
|
89
|
+
('subject', models.CharField(blank=True, help_text='Email subject', max_length=512, null=True)),
|
90
|
+
('body_text', models.TextField(blank=True, help_text='Plain text body', null=True)),
|
91
|
+
('body_html', models.TextField(blank=True, help_text='HTML body', null=True)),
|
92
|
+
('template_name', models.CharField(blank=True, help_text='Optional EmailTemplate name used to render this message', max_length=255, null=True)),
|
93
|
+
('template_context', models.JSONField(blank=True, default=dict, help_text='Context used when rendering a template')),
|
94
|
+
('status', models.CharField(choices=[('queued', 'Queued'), ('sending', 'Sending'), ('delivered', 'Delivered'), ('bounced', 'Bounced'), ('complained', 'Complained'), ('failed', 'Failed'), ('unknown', 'Unknown')], db_index=True, default='queued', help_text='Current delivery status', max_length=32)),
|
95
|
+
('status_reason', models.TextField(blank=True, help_text='Details or raw payload for bounces/complaints/errors', null=True)),
|
96
|
+
('metadata', models.JSONField(blank=True, default=dict, help_text='Arbitrary metadata for downstream processing/auditing')),
|
97
|
+
('mailbox', models.ForeignKey(help_text='Mailbox used as the sender (envelope MAIL FROM = mailbox.email)', on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to='aws.mailbox')),
|
98
|
+
],
|
99
|
+
options={
|
100
|
+
'db_table': 'aws_sent_message',
|
101
|
+
'ordering': ['-created', 'id'],
|
102
|
+
},
|
103
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
104
|
+
),
|
105
|
+
migrations.CreateModel(
|
106
|
+
name='IncomingEmail',
|
107
|
+
fields=[
|
108
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
109
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
110
|
+
('modified', models.DateTimeField(auto_now=True, db_index=True)),
|
111
|
+
('s3_object_url', models.CharField(help_text='S3 URL for the raw MIME message (e.g., s3://bucket/key)', max_length=512)),
|
112
|
+
('message_id', models.CharField(blank=True, db_index=True, help_text='SMTP Message-ID header (if present)', max_length=255, null=True)),
|
113
|
+
('from_address', models.CharField(blank=True, help_text='Raw From header address (may include name)', max_length=512, null=True)),
|
114
|
+
('to_addresses', models.JSONField(blank=True, default=list, help_text='List of recipient addresses from To header')),
|
115
|
+
('cc_addresses', models.JSONField(blank=True, default=list, help_text='List of recipient addresses from Cc header')),
|
116
|
+
('subject', models.CharField(blank=True, help_text='Email subject', max_length=512, null=True)),
|
117
|
+
('date_header', models.DateTimeField(blank=True, help_text='Parsed Date header from the message', null=True)),
|
118
|
+
('headers', models.JSONField(blank=True, default=dict, help_text='All headers as a JSON object (flattened)')),
|
119
|
+
('text_body', models.TextField(blank=True, help_text='Extracted plain text body (if available)', null=True)),
|
120
|
+
('html_body', models.TextField(blank=True, help_text='Extracted HTML body (if available)', null=True)),
|
121
|
+
('size_bytes', models.IntegerField(default=0, help_text='Approximate size of the raw message in bytes')),
|
122
|
+
('received_at', models.DateTimeField(blank=True, db_index=True, help_text='Time message was received (from SNS/S3 event or set by parser)', null=True)),
|
123
|
+
('processed', models.BooleanField(default=False, help_text='True if post-receive processing completed')),
|
124
|
+
('process_status', models.CharField(db_index=True, default='pending', help_text='Processing status: pending | success | error', max_length=32)),
|
125
|
+
('process_error', models.TextField(blank=True, help_text='Error details if processing failed', null=True)),
|
126
|
+
('mailbox', models.ForeignKey(blank=True, help_text='Associated mailbox if any recipient matches', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='incoming_emails', to='aws.mailbox')),
|
127
|
+
],
|
128
|
+
options={
|
129
|
+
'db_table': 'aws_incoming_email',
|
130
|
+
'ordering': ['-received_at', '-created'],
|
131
|
+
},
|
132
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
133
|
+
),
|
134
|
+
migrations.CreateModel(
|
135
|
+
name='EmailTemplate',
|
136
|
+
fields=[
|
137
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
138
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
139
|
+
('modified', models.DateTimeField(auto_now=True, db_index=True)),
|
140
|
+
('name', models.CharField(db_index=True, help_text='Unique template name (used by callers to reference this template)', max_length=255, unique=True)),
|
141
|
+
('subject_template', models.TextField(blank=True, default='', help_text='Django template string for the email subject')),
|
142
|
+
('html_template', models.TextField(blank=True, default='', help_text='Django template string for the HTML body')),
|
143
|
+
('text_template', models.TextField(blank=True, default='', help_text='Django template string for the plain text body')),
|
144
|
+
('metadata', models.JSONField(blank=True, default=dict, help_text='Arbitrary metadata for this template (e.g., description, tags)')),
|
145
|
+
],
|
146
|
+
options={
|
147
|
+
'db_table': 'aws_email_template',
|
148
|
+
'ordering': ['name'],
|
149
|
+
'indexes': [models.Index(fields=['modified'], name='aws_email_t_modifie_cf76d6_idx'), models.Index(fields=['name'], name='aws_email_t_name_d1662a_idx')],
|
150
|
+
},
|
151
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
152
|
+
),
|
153
|
+
migrations.AddIndex(
|
154
|
+
model_name='emaildomain',
|
155
|
+
index=models.Index(fields=['status'], name='aws_email_d_status_398945_idx'),
|
156
|
+
),
|
157
|
+
migrations.AddIndex(
|
158
|
+
model_name='emaildomain',
|
159
|
+
index=models.Index(fields=['modified'], name='aws_email_d_modifie_66ca75_idx'),
|
160
|
+
),
|
161
|
+
migrations.AddField(
|
162
|
+
model_name='emailattachment',
|
163
|
+
name='incoming_email',
|
164
|
+
field=models.ForeignKey(help_text='The inbound email this attachment belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='aws.incomingemail'),
|
165
|
+
),
|
166
|
+
migrations.AddIndex(
|
167
|
+
model_name='sentmessage',
|
168
|
+
index=models.Index(fields=['modified'], name='aws_sent_me_modifie_a26352_idx'),
|
169
|
+
),
|
170
|
+
migrations.AddIndex(
|
171
|
+
model_name='sentmessage',
|
172
|
+
index=models.Index(fields=['status'], name='aws_sent_me_status_63e619_idx'),
|
173
|
+
),
|
174
|
+
migrations.AddIndex(
|
175
|
+
model_name='sentmessage',
|
176
|
+
index=models.Index(fields=['ses_message_id'], name='aws_sent_me_ses_mes_b72855_idx'),
|
177
|
+
),
|
178
|
+
migrations.AddIndex(
|
179
|
+
model_name='mailbox',
|
180
|
+
index=models.Index(fields=['modified'], name='aws_mailbox_modifie_597f5d_idx'),
|
181
|
+
),
|
182
|
+
migrations.AddIndex(
|
183
|
+
model_name='mailbox',
|
184
|
+
index=models.Index(fields=['email'], name='aws_mailbox_email_e2c6d1_idx'),
|
185
|
+
),
|
186
|
+
migrations.AddIndex(
|
187
|
+
model_name='incomingemail',
|
188
|
+
index=models.Index(fields=['modified'], name='aws_incomin_modifie_8e68a9_idx'),
|
189
|
+
),
|
190
|
+
migrations.AddIndex(
|
191
|
+
model_name='incomingemail',
|
192
|
+
index=models.Index(fields=['received_at'], name='aws_incomin_receive_7b6d93_idx'),
|
193
|
+
),
|
194
|
+
migrations.AddIndex(
|
195
|
+
model_name='incomingemail',
|
196
|
+
index=models.Index(fields=['message_id'], name='aws_incomin_message_d8ca7f_idx'),
|
197
|
+
),
|
198
|
+
migrations.AddIndex(
|
199
|
+
model_name='emailattachment',
|
200
|
+
index=models.Index(fields=['modified'], name='aws_email_a_modifie_b283df_idx'),
|
201
|
+
),
|
202
|
+
migrations.AddIndex(
|
203
|
+
model_name='emailattachment',
|
204
|
+
index=models.Index(fields=['filename'], name='aws_email_a_filenam_da3c89_idx'),
|
205
|
+
),
|
206
|
+
]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-08-27 21:38
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('aws', '0001_initial'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AddField(
|
14
|
+
model_name='emaildomain',
|
15
|
+
name='can_recv',
|
16
|
+
field=models.BooleanField(default=False, help_text='True if inbound receiving is ready per last audit'),
|
17
|
+
),
|
18
|
+
migrations.AddField(
|
19
|
+
model_name='emaildomain',
|
20
|
+
name='can_send',
|
21
|
+
field=models.BooleanField(default=False, help_text='True if outbound sending is ready per last audit'),
|
22
|
+
),
|
23
|
+
migrations.AlterField(
|
24
|
+
model_name='emaildomain',
|
25
|
+
name='status',
|
26
|
+
field=models.CharField(db_index=True, default='pending', help_text='Domain status: "pending" (created), "ready" (audit passed), or "missing" (audit failed)', max_length=32),
|
27
|
+
),
|
28
|
+
]
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-08-31 23:09
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('aws', '0002_emaildomain_can_recv_emaildomain_can_send_and_more'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AddField(
|
14
|
+
model_name='mailbox',
|
15
|
+
name='is_domain_default',
|
16
|
+
field=models.BooleanField(db_index=True, default=False, help_text='Default mailbox for this domain (one per domain)'),
|
17
|
+
),
|
18
|
+
migrations.AddField(
|
19
|
+
model_name='mailbox',
|
20
|
+
name='is_system_default',
|
21
|
+
field=models.BooleanField(db_index=True, default=False, help_text='System-wide default mailbox (only one allowed)'),
|
22
|
+
),
|
23
|
+
migrations.AddIndex(
|
24
|
+
model_name='mailbox',
|
25
|
+
index=models.Index(fields=['is_system_default'], name='aws_mailbox_is_syst_9911c7_idx'),
|
26
|
+
),
|
27
|
+
migrations.AddIndex(
|
28
|
+
model_name='mailbox',
|
29
|
+
index=models.Index(fields=['is_domain_default', 'domain'], name='aws_mailbox_is_doma_189efb_idx'),
|
30
|
+
),
|
31
|
+
]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Generated by Django 4.2.23 on 2025-09-06 00:00
|
2
|
+
|
3
|
+
from django.conf import settings
|
4
|
+
from django.db import migrations, models
|
5
|
+
import django.db.models.deletion
|
6
|
+
import mojo.models.rest
|
7
|
+
|
8
|
+
|
9
|
+
class Migration(migrations.Migration):
|
10
|
+
|
11
|
+
dependencies = [
|
12
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
13
|
+
('account', '0014_notificationdelivery_data_payload_and_more'),
|
14
|
+
('aws', '0003_mailbox_is_domain_default_mailbox_is_system_default_and_more'),
|
15
|
+
]
|
16
|
+
|
17
|
+
operations = [
|
18
|
+
migrations.CreateModel(
|
19
|
+
name='S3Bucket',
|
20
|
+
fields=[
|
21
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
22
|
+
('mojo_secrets', models.TextField(blank=True, default=None, null=True)),
|
23
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
24
|
+
('modified', models.DateTimeField(auto_now=True, db_index=True)),
|
25
|
+
('name', models.CharField(db_index=True, max_length=255, unique=True)),
|
26
|
+
('region', models.CharField(default='us-west-2', help_text='AWS region for SES operations', max_length=64)),
|
27
|
+
('metadata', models.JSONField(blank=True, default=dict)),
|
28
|
+
('is_active', models.BooleanField(db_index=True, default=False)),
|
29
|
+
('is_system_default', models.BooleanField(db_index=True, default=False)),
|
30
|
+
('is_group_default', models.BooleanField(db_index=True, default=False)),
|
31
|
+
('group', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='s3_buckets', to='account.group')),
|
32
|
+
('user', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='s3_buckets', to=settings.AUTH_USER_MODEL)),
|
33
|
+
],
|
34
|
+
options={
|
35
|
+
'abstract': False,
|
36
|
+
},
|
37
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
38
|
+
),
|
39
|
+
]
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Generated by Django 4.2.23 on 2025-09-06 00:10
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('aws', '0004_s3bucket'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterField(
|
14
|
+
model_name='emaildomain',
|
15
|
+
name='region',
|
16
|
+
field=models.CharField(default='us-east-1', help_text='AWS region for SES operations', max_length=64),
|
17
|
+
),
|
18
|
+
migrations.DeleteModel(
|
19
|
+
name='S3Bucket',
|
20
|
+
),
|
21
|
+
]
|
mojo/apps/aws/models/__init__.py
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
"""
|
2
|
+
AWS models package exports
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .email_domain import EmailDomain
|
6
|
+
from .mailbox import Mailbox
|
7
|
+
from .incoming_email import IncomingEmail
|
8
|
+
from .email_attachment import EmailAttachment
|
9
|
+
from .sent_message import SentMessage
|
10
|
+
from .email_template import EmailTemplate
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
"EmailDomain",
|
14
|
+
"Mailbox",
|
15
|
+
"IncomingEmail",
|
16
|
+
"EmailAttachment",
|
17
|
+
"SentMessage",
|
18
|
+
"EmailTemplate",
|
19
|
+
]
|
@@ -0,0 +1,99 @@
|
|
1
|
+
from django.db import models
|
2
|
+
from mojo.models import MojoModel
|
3
|
+
|
4
|
+
|
5
|
+
class EmailAttachment(models.Model, MojoModel):
|
6
|
+
"""
|
7
|
+
EmailAttachment
|
8
|
+
|
9
|
+
Represents a single attachment extracted from an incoming email. The binary
|
10
|
+
content is stored in the same inbound S3 bucket as the raw message, and
|
11
|
+
referenced by `stored_as` (e.g., s3://bucket/key).
|
12
|
+
|
13
|
+
Relationships:
|
14
|
+
- incoming_email: FK to IncomingEmail
|
15
|
+
"""
|
16
|
+
|
17
|
+
created = models.DateTimeField(auto_now_add=True, editable=False)
|
18
|
+
modified = models.DateTimeField(auto_now=True, db_index=True)
|
19
|
+
|
20
|
+
incoming_email = models.ForeignKey(
|
21
|
+
"aws.IncomingEmail",
|
22
|
+
related_name="attachments",
|
23
|
+
on_delete=models.CASCADE,
|
24
|
+
help_text="The inbound email this attachment belongs to"
|
25
|
+
)
|
26
|
+
|
27
|
+
filename = models.CharField(
|
28
|
+
max_length=512,
|
29
|
+
null=True,
|
30
|
+
blank=True,
|
31
|
+
help_text="Original filename (if provided by the sender)"
|
32
|
+
)
|
33
|
+
|
34
|
+
content_type = models.CharField(
|
35
|
+
max_length=255,
|
36
|
+
null=True,
|
37
|
+
blank=True,
|
38
|
+
help_text="MIME content type (e.g., application/pdf)"
|
39
|
+
)
|
40
|
+
|
41
|
+
size_bytes = models.IntegerField(
|
42
|
+
default=0,
|
43
|
+
help_text="Size of the stored attachment in bytes (approximate)"
|
44
|
+
)
|
45
|
+
|
46
|
+
stored_as = models.CharField(
|
47
|
+
max_length=512,
|
48
|
+
help_text="Storage reference (e.g., s3://bucket/key)"
|
49
|
+
)
|
50
|
+
|
51
|
+
metadata = models.JSONField(
|
52
|
+
default=dict,
|
53
|
+
blank=True,
|
54
|
+
help_text="Arbitrary metadata (e.g., content-id, part headers)"
|
55
|
+
)
|
56
|
+
|
57
|
+
class Meta:
|
58
|
+
db_table = "aws_email_attachment"
|
59
|
+
indexes = [
|
60
|
+
models.Index(fields=["modified"]),
|
61
|
+
models.Index(fields=["filename"]),
|
62
|
+
]
|
63
|
+
ordering = ["-created", "id"]
|
64
|
+
|
65
|
+
class RestMeta:
|
66
|
+
VIEW_PERMS = ["manage_aws"]
|
67
|
+
SAVE_PERMS = ["manage_aws"]
|
68
|
+
DELETE_PERMS = ["manage_aws"]
|
69
|
+
SEARCH_FIELDS = ["filename", "content_type", "stored_as"]
|
70
|
+
GRAPHS = {
|
71
|
+
"basic": {
|
72
|
+
"fields": [
|
73
|
+
"id",
|
74
|
+
"incoming_email",
|
75
|
+
"filename",
|
76
|
+
"content_type",
|
77
|
+
"size_bytes",
|
78
|
+
"created",
|
79
|
+
],
|
80
|
+
"graphs": {"incoming_email": "basic"},
|
81
|
+
},
|
82
|
+
"default": {
|
83
|
+
"fields": [
|
84
|
+
"id",
|
85
|
+
"incoming_email",
|
86
|
+
"filename",
|
87
|
+
"content_type",
|
88
|
+
"size_bytes",
|
89
|
+
"stored_as",
|
90
|
+
"metadata",
|
91
|
+
"created",
|
92
|
+
"modified",
|
93
|
+
],
|
94
|
+
"graphs": {"incoming_email": "basic"},
|
95
|
+
},
|
96
|
+
}
|
97
|
+
|
98
|
+
def __str__(self) -> str:
|
99
|
+
return self.filename or self.stored_as or f"Attachment {self.pk}"
|
@@ -0,0 +1,218 @@
|
|
1
|
+
from django.db import models
|
2
|
+
from mojo.models import MojoModel, MojoSecrets
|
3
|
+
from mojo.helpers.settings import settings
|
4
|
+
from mojo.helpers.aws.ses_domain import audit_domain_config, reconcile_domain_config
|
5
|
+
from mojo.helpers import aws
|
6
|
+
|
7
|
+
|
8
|
+
class EmailDomain(MojoSecrets, MojoModel):
|
9
|
+
"""
|
10
|
+
EmailDomain
|
11
|
+
|
12
|
+
Minimal model for managing an SES-backed email domain configuration in MOJO.
|
13
|
+
|
14
|
+
Notes:
|
15
|
+
- 'name' is the domain (e.g., example.com).
|
16
|
+
- 'region' defaults to project AWS region (settings.AWS_REGION) or 'us-east-1'.
|
17
|
+
- 'receiving_enabled' toggles domain-level catch-all receiving (SES receipt rules).
|
18
|
+
- 's3_inbound_bucket' and 's3_inbound_prefix' identify where inbound emails are stored.
|
19
|
+
- 'status' is a lightweight lifecycle indicator: pending | verified | error (free-form for now).
|
20
|
+
- 'metadata' allows flexible per-domain extension without schema churn.
|
21
|
+
"""
|
22
|
+
|
23
|
+
created = models.DateTimeField(auto_now_add=True, editable=False)
|
24
|
+
modified = models.DateTimeField(auto_now=True, db_index=True)
|
25
|
+
|
26
|
+
name = models.CharField(max_length=255, unique=True, db_index=True)
|
27
|
+
region = models.CharField(
|
28
|
+
max_length=64,
|
29
|
+
default=getattr(settings, 'AWS_REGION', 'us-east-1'),
|
30
|
+
help_text="AWS region for SES operations"
|
31
|
+
)
|
32
|
+
|
33
|
+
status = models.CharField(
|
34
|
+
max_length=32,
|
35
|
+
default='pending',
|
36
|
+
db_index=True,
|
37
|
+
help_text='Domain status: "pending" (created), "ready" (audit passed), or "missing" (audit failed)'
|
38
|
+
)
|
39
|
+
|
40
|
+
receiving_enabled = models.BooleanField(
|
41
|
+
default=False,
|
42
|
+
help_text="When true, domain-level catch-all receiving is enabled via SES receipt rules"
|
43
|
+
)
|
44
|
+
|
45
|
+
s3_inbound_bucket = models.CharField(
|
46
|
+
max_length=255,
|
47
|
+
null=True,
|
48
|
+
blank=True,
|
49
|
+
help_text="S3 bucket for inbound emails (required if receiving_enabled)"
|
50
|
+
)
|
51
|
+
s3_inbound_prefix = models.CharField(
|
52
|
+
max_length=255,
|
53
|
+
default='',
|
54
|
+
blank=True,
|
55
|
+
help_text="S3 prefix for inbound emails (e.g., inbound/example.com/)"
|
56
|
+
)
|
57
|
+
|
58
|
+
dns_mode = models.CharField(
|
59
|
+
max_length=32,
|
60
|
+
default='manual',
|
61
|
+
help_text="DNS automation mode: manual | route53 | godaddy"
|
62
|
+
)
|
63
|
+
|
64
|
+
sns_topic_bounce_arn = models.CharField(
|
65
|
+
max_length=512,
|
66
|
+
null=True,
|
67
|
+
blank=True,
|
68
|
+
help_text="SNS topic ARN for SES bounce notifications"
|
69
|
+
)
|
70
|
+
sns_topic_complaint_arn = models.CharField(
|
71
|
+
max_length=512,
|
72
|
+
null=True,
|
73
|
+
blank=True,
|
74
|
+
help_text="SNS topic ARN for SES complaint notifications"
|
75
|
+
)
|
76
|
+
sns_topic_delivery_arn = models.CharField(
|
77
|
+
max_length=512,
|
78
|
+
null=True,
|
79
|
+
blank=True,
|
80
|
+
help_text="SNS topic ARN for SES delivery notifications"
|
81
|
+
)
|
82
|
+
sns_topic_inbound_arn = models.CharField(
|
83
|
+
max_length=512,
|
84
|
+
null=True,
|
85
|
+
blank=True,
|
86
|
+
help_text="SNS topic ARN for SES inbound notifications"
|
87
|
+
)
|
88
|
+
|
89
|
+
metadata = models.JSONField(default=dict, blank=True)
|
90
|
+
|
91
|
+
# Computed readiness flags updated by audit runs
|
92
|
+
can_send = models.BooleanField(
|
93
|
+
default=False,
|
94
|
+
help_text="True if outbound sending is ready per last audit"
|
95
|
+
)
|
96
|
+
can_recv = models.BooleanField(
|
97
|
+
default=False,
|
98
|
+
help_text="True if inbound receiving is ready per last audit"
|
99
|
+
)
|
100
|
+
|
101
|
+
class Meta:
|
102
|
+
db_table = "aws_email_domain"
|
103
|
+
indexes = [
|
104
|
+
models.Index(fields=["status"]),
|
105
|
+
models.Index(fields=["modified"]),
|
106
|
+
]
|
107
|
+
ordering = ["name"]
|
108
|
+
|
109
|
+
class RestMeta:
|
110
|
+
VIEW_PERMS = ["manage_aws"]
|
111
|
+
SAVE_PERMS = ["manage_aws"]
|
112
|
+
DELETE_PERMS = ["manage_aws"]
|
113
|
+
SEARCH_FIELDS = ["name", "region", "status"]
|
114
|
+
GRAPHS = {
|
115
|
+
"basic": {
|
116
|
+
"fields": [
|
117
|
+
"id",
|
118
|
+
"name",
|
119
|
+
"region",
|
120
|
+
"status",
|
121
|
+
"receiving_enabled",
|
122
|
+
]
|
123
|
+
},
|
124
|
+
"default": {
|
125
|
+
"fields": [
|
126
|
+
"id",
|
127
|
+
"name",
|
128
|
+
"region",
|
129
|
+
"status",
|
130
|
+
"receiving_enabled",
|
131
|
+
"s3_inbound_bucket",
|
132
|
+
"s3_inbound_prefix",
|
133
|
+
"dns_mode",
|
134
|
+
"sns_topic_bounce_arn",
|
135
|
+
"sns_topic_complaint_arn",
|
136
|
+
"sns_topic_delivery_arn",
|
137
|
+
"sns_topic_inbound_arn",
|
138
|
+
"metadata",
|
139
|
+
"created",
|
140
|
+
"modified",
|
141
|
+
],
|
142
|
+
"extra": [
|
143
|
+
"aws_key",
|
144
|
+
"aws_secret_masked"
|
145
|
+
]
|
146
|
+
},
|
147
|
+
}
|
148
|
+
|
149
|
+
@property
|
150
|
+
def aws_key(self):
|
151
|
+
return self.get_secret('aws_key')
|
152
|
+
|
153
|
+
@property
|
154
|
+
def aws_secret(self):
|
155
|
+
return self.get_secret('aws_secret')
|
156
|
+
|
157
|
+
@property
|
158
|
+
def aws_secret_masked(self):
|
159
|
+
secret = self.get_secret('aws_secret', '')
|
160
|
+
if len(secret) > 4:
|
161
|
+
return '*' * (len(secret) - 4) + secret[-4:]
|
162
|
+
return secret
|
163
|
+
|
164
|
+
|
165
|
+
@property
|
166
|
+
def aws_region(self):
|
167
|
+
return self.region or getattr(settings, 'AWS_REGION', 'us-east-1')
|
168
|
+
|
169
|
+
@property
|
170
|
+
def is_verified(self):
|
171
|
+
return self.status in ["verified", "ready"]
|
172
|
+
|
173
|
+
def set_aws_key(self, key):
|
174
|
+
self.set_secret('aws_key', key)
|
175
|
+
|
176
|
+
def set_aws_secret(self, secret):
|
177
|
+
self.set_secret('aws_secret', secret)
|
178
|
+
|
179
|
+
def on_rest_created(self):
|
180
|
+
"""
|
181
|
+
Automatically audit and reconcile SES/SNS configuration after this domain is created.
|
182
|
+
This keeps AWS-side resources aligned without requiring a separate call.
|
183
|
+
"""
|
184
|
+
try:
|
185
|
+
region = self.aws_region
|
186
|
+
# Audit current state (best-effort; ignore failures)
|
187
|
+
try:
|
188
|
+
desired_receiving = None
|
189
|
+
if self.receiving_enabled and self.s3_inbound_bucket:
|
190
|
+
desired_receiving = {
|
191
|
+
'bucket': self.s3_inbound_bucket,
|
192
|
+
'prefix': self.s3_inbound_prefix or '',
|
193
|
+
'rule_set': 'mojo-default-receiving',
|
194
|
+
'rule_name': f'mojo-{self.name}-catchall',
|
195
|
+
}
|
196
|
+
audit_domain_config(
|
197
|
+
domain=self.name,
|
198
|
+
region=region,
|
199
|
+
desired_receiving=desired_receiving,
|
200
|
+
)
|
201
|
+
except Exception:
|
202
|
+
# Non-fatal: continue with reconcile
|
203
|
+
pass
|
204
|
+
|
205
|
+
# Reconcile (idempotent): ensure topics/mappings and catch-all receipt rule if enabled
|
206
|
+
reconcile_domain_config(
|
207
|
+
domain=self.name,
|
208
|
+
region=region,
|
209
|
+
receiving_enabled=self.receiving_enabled,
|
210
|
+
s3_bucket=self.s3_inbound_bucket,
|
211
|
+
s3_prefix=self.s3_inbound_prefix or '',
|
212
|
+
)
|
213
|
+
except Exception:
|
214
|
+
# Swallow exceptions to avoid failing the create call; details can be inspected via /audit
|
215
|
+
pass
|
216
|
+
|
217
|
+
def __str__(self) -> str:
|
218
|
+
return self.name
|