django-nativemojo 0.1.10__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.10.dist-info/LICENSE +19 -0
- django_nativemojo-0.1.10.dist-info/METADATA +96 -0
- django_nativemojo-0.1.10.dist-info/NOTICE +8 -0
- django_nativemojo-0.1.10.dist-info/RECORD +194 -0
- django_nativemojo-0.1.10.dist-info/WHEEL +4 -0
- mojo/__init__.py +3 -0
- mojo/apps/account/__init__.py +1 -0
- mojo/apps/account/admin.py +91 -0
- mojo/apps/account/apps.py +16 -0
- mojo/apps/account/migrations/0001_initial.py +77 -0
- mojo/apps/account/migrations/0002_user_is_email_verified_user_is_phone_verified.py +23 -0
- mojo/apps/account/migrations/0003_group_mojo_secrets_user_mojo_secrets.py +23 -0
- mojo/apps/account/migrations/__init__.py +0 -0
- mojo/apps/account/models/__init__.py +3 -0
- mojo/apps/account/models/group.py +98 -0
- mojo/apps/account/models/member.py +95 -0
- mojo/apps/account/models/pkey.py +18 -0
- mojo/apps/account/models/user.py +211 -0
- mojo/apps/account/rest/__init__.py +3 -0
- mojo/apps/account/rest/group.py +25 -0
- mojo/apps/account/rest/user.py +47 -0
- mojo/apps/account/utils/__init__.py +0 -0
- mojo/apps/account/utils/jwtoken.py +72 -0
- mojo/apps/account/utils/passkeys.py +54 -0
- mojo/apps/fileman/README.md +549 -0
- mojo/apps/fileman/__init__.py +0 -0
- mojo/apps/fileman/apps.py +15 -0
- mojo/apps/fileman/backends/__init__.py +117 -0
- mojo/apps/fileman/backends/base.py +319 -0
- mojo/apps/fileman/backends/filesystem.py +397 -0
- mojo/apps/fileman/backends/s3.py +398 -0
- mojo/apps/fileman/examples/configurations.py +378 -0
- mojo/apps/fileman/examples/usage_example.py +665 -0
- mojo/apps/fileman/management/__init__.py +1 -0
- mojo/apps/fileman/management/commands/__init__.py +1 -0
- mojo/apps/fileman/management/commands/cleanup_expired_uploads.py +222 -0
- mojo/apps/fileman/models/__init__.py +7 -0
- mojo/apps/fileman/models/file.py +292 -0
- mojo/apps/fileman/models/manager.py +227 -0
- mojo/apps/fileman/models/render.py +0 -0
- mojo/apps/fileman/rest/__init__ +0 -0
- mojo/apps/fileman/rest/__init__.py +23 -0
- mojo/apps/fileman/rest/fileman.py +13 -0
- mojo/apps/fileman/rest/upload.py +92 -0
- mojo/apps/fileman/utils/__init__.py +19 -0
- mojo/apps/fileman/utils/upload.py +616 -0
- mojo/apps/incident/__init__.py +1 -0
- mojo/apps/incident/handlers/__init__.py +3 -0
- mojo/apps/incident/handlers/event_handlers.py +142 -0
- mojo/apps/incident/migrations/0001_initial.py +83 -0
- mojo/apps/incident/migrations/0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more.py +44 -0
- mojo/apps/incident/migrations/0003_alter_event_model_id.py +18 -0
- mojo/apps/incident/migrations/0004_alter_incident_model_id.py +18 -0
- mojo/apps/incident/migrations/__init__.py +0 -0
- mojo/apps/incident/models/__init__.py +3 -0
- mojo/apps/incident/models/event.py +135 -0
- mojo/apps/incident/models/incident.py +33 -0
- mojo/apps/incident/models/rule.py +247 -0
- mojo/apps/incident/parsers/__init__.py +0 -0
- mojo/apps/incident/parsers/ossec/__init__.py +1 -0
- mojo/apps/incident/parsers/ossec/core.py +82 -0
- mojo/apps/incident/parsers/ossec/parsed.py +23 -0
- mojo/apps/incident/parsers/ossec/rules.py +124 -0
- mojo/apps/incident/parsers/ossec/utils.py +169 -0
- mojo/apps/incident/reporter.py +42 -0
- mojo/apps/incident/rest/__init__.py +2 -0
- mojo/apps/incident/rest/event.py +23 -0
- mojo/apps/incident/rest/ossec.py +22 -0
- mojo/apps/logit/__init__.py +0 -0
- mojo/apps/logit/admin.py +37 -0
- mojo/apps/logit/migrations/0001_initial.py +32 -0
- mojo/apps/logit/migrations/0002_log_duid_log_payload_log_username.py +28 -0
- mojo/apps/logit/migrations/0003_log_level.py +18 -0
- mojo/apps/logit/migrations/__init__.py +0 -0
- mojo/apps/logit/models/__init__.py +1 -0
- mojo/apps/logit/models/log.py +57 -0
- mojo/apps/logit/rest.py +9 -0
- mojo/apps/metrics/README.md +79 -0
- mojo/apps/metrics/__init__.py +12 -0
- mojo/apps/metrics/redis_metrics.py +331 -0
- mojo/apps/metrics/rest/__init__.py +1 -0
- mojo/apps/metrics/rest/base.py +152 -0
- mojo/apps/metrics/rest/db.py +0 -0
- mojo/apps/metrics/utils.py +227 -0
- mojo/apps/notify/README.md +91 -0
- mojo/apps/notify/README_NOTIFICATIONS.md +566 -0
- mojo/apps/notify/__init__.py +0 -0
- mojo/apps/notify/admin.py +52 -0
- mojo/apps/notify/handlers/__init__.py +0 -0
- mojo/apps/notify/handlers/example_handlers.py +516 -0
- mojo/apps/notify/handlers/ses/__init__.py +25 -0
- mojo/apps/notify/handlers/ses/bounce.py +0 -0
- mojo/apps/notify/handlers/ses/complaint.py +25 -0
- mojo/apps/notify/handlers/ses/message.py +86 -0
- mojo/apps/notify/management/__init__.py +0 -0
- mojo/apps/notify/management/commands/__init__.py +1 -0
- mojo/apps/notify/management/commands/process_notifications.py +370 -0
- mojo/apps/notify/mod +0 -0
- mojo/apps/notify/models/__init__.py +12 -0
- mojo/apps/notify/models/account.py +128 -0
- mojo/apps/notify/models/attachment.py +24 -0
- mojo/apps/notify/models/bounce.py +68 -0
- mojo/apps/notify/models/complaint.py +40 -0
- mojo/apps/notify/models/inbox.py +113 -0
- mojo/apps/notify/models/inbox_message.py +173 -0
- mojo/apps/notify/models/outbox.py +129 -0
- mojo/apps/notify/models/outbox_message.py +288 -0
- mojo/apps/notify/models/template.py +30 -0
- mojo/apps/notify/providers/__init__.py +0 -0
- mojo/apps/notify/providers/aws.py +73 -0
- mojo/apps/notify/rest/__init__.py +0 -0
- mojo/apps/notify/rest/ses.py +0 -0
- mojo/apps/notify/utils/__init__.py +2 -0
- mojo/apps/notify/utils/notifications.py +404 -0
- mojo/apps/notify/utils/parsing.py +202 -0
- mojo/apps/notify/utils/render.py +144 -0
- mojo/apps/tasks/README.md +118 -0
- mojo/apps/tasks/__init__.py +11 -0
- mojo/apps/tasks/manager.py +489 -0
- mojo/apps/tasks/rest/__init__.py +2 -0
- mojo/apps/tasks/rest/hooks.py +0 -0
- mojo/apps/tasks/rest/tasks.py +62 -0
- mojo/apps/tasks/runner.py +174 -0
- mojo/apps/tasks/tq_handlers.py +14 -0
- mojo/decorators/__init__.py +3 -0
- mojo/decorators/auth.py +25 -0
- mojo/decorators/cron.py +31 -0
- mojo/decorators/http.py +132 -0
- mojo/decorators/validate.py +14 -0
- mojo/errors.py +88 -0
- mojo/helpers/__init__.py +0 -0
- mojo/helpers/aws/__init__.py +0 -0
- mojo/helpers/aws/client.py +8 -0
- mojo/helpers/aws/s3.py +268 -0
- mojo/helpers/aws/setup_email.py +0 -0
- mojo/helpers/cron.py +79 -0
- mojo/helpers/crypto/__init__.py +4 -0
- mojo/helpers/crypto/aes.py +60 -0
- mojo/helpers/crypto/hash.py +59 -0
- mojo/helpers/crypto/privpub/__init__.py +1 -0
- mojo/helpers/crypto/privpub/hybrid.py +97 -0
- mojo/helpers/crypto/privpub/rsa.py +104 -0
- mojo/helpers/crypto/sign.py +36 -0
- mojo/helpers/crypto/too.l.py +25 -0
- mojo/helpers/crypto/utils.py +26 -0
- mojo/helpers/daemon.py +94 -0
- mojo/helpers/dates.py +69 -0
- mojo/helpers/dns/__init__.py +0 -0
- mojo/helpers/dns/godaddy.py +62 -0
- mojo/helpers/filetypes.py +128 -0
- mojo/helpers/logit.py +310 -0
- mojo/helpers/modules.py +95 -0
- mojo/helpers/paths.py +63 -0
- mojo/helpers/redis.py +10 -0
- mojo/helpers/request.py +89 -0
- mojo/helpers/request_parser.py +269 -0
- mojo/helpers/response.py +14 -0
- mojo/helpers/settings.py +146 -0
- mojo/helpers/sysinfo.py +140 -0
- mojo/helpers/ua.py +0 -0
- mojo/middleware/__init__.py +0 -0
- mojo/middleware/auth.py +26 -0
- mojo/middleware/logging.py +55 -0
- mojo/middleware/mojo.py +21 -0
- mojo/migrations/0001_initial.py +32 -0
- mojo/migrations/__init__.py +0 -0
- mojo/models/__init__.py +2 -0
- mojo/models/meta.py +262 -0
- mojo/models/rest.py +538 -0
- mojo/models/secrets.py +59 -0
- mojo/rest/__init__.py +1 -0
- mojo/rest/info.py +26 -0
- mojo/serializers/__init__.py +0 -0
- mojo/serializers/models.py +165 -0
- mojo/serializers/openapi.py +188 -0
- mojo/urls.py +38 -0
- mojo/ws4redis/README.md +174 -0
- mojo/ws4redis/__init__.py +2 -0
- mojo/ws4redis/client.py +283 -0
- mojo/ws4redis/connection.py +327 -0
- mojo/ws4redis/exceptions.py +32 -0
- mojo/ws4redis/redis.py +183 -0
- mojo/ws4redis/servers/__init__.py +0 -0
- mojo/ws4redis/servers/base.py +86 -0
- mojo/ws4redis/servers/django.py +171 -0
- mojo/ws4redis/servers/uwsgi.py +63 -0
- mojo/ws4redis/settings.py +45 -0
- mojo/ws4redis/utf8validator.py +128 -0
- mojo/ws4redis/websocket.py +403 -0
- testit/__init__.py +0 -0
- testit/client.py +147 -0
- testit/faker.py +20 -0
- testit/helpers.py +198 -0
- testit/runner.py +262 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
from django.db import models
|
2
|
+
from mojo.models import MojoModel, MojoSecrets
|
3
|
+
|
4
|
+
|
5
|
+
class FileManager(MojoSecrets, MojoModel):
|
6
|
+
"""
|
7
|
+
File manager configuration for different storage backends and upload strategies
|
8
|
+
"""
|
9
|
+
|
10
|
+
class RestMeta:
|
11
|
+
CAN_SAVE = CAN_CREATE = True
|
12
|
+
CAN_DELETE = True
|
13
|
+
DEFAULT_SORT = "-id"
|
14
|
+
VIEW_PERMS = ["view_fileman"]
|
15
|
+
SEARCH_FIELDS = ["name", "backend_type", "description"]
|
16
|
+
SEARCH_TERMS = [
|
17
|
+
"name", "backend_type", "description",
|
18
|
+
("group", "group__name")]
|
19
|
+
|
20
|
+
GRAPHS = {
|
21
|
+
"default": {
|
22
|
+
"graphs": {
|
23
|
+
"group": "basic"
|
24
|
+
}
|
25
|
+
},
|
26
|
+
"list": {
|
27
|
+
"graphs": {
|
28
|
+
"group": "basic"
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
# Storage backend types
|
34
|
+
FILE_SYSTEM = 'file'
|
35
|
+
AWS_S3 = 's3'
|
36
|
+
AZURE_BLOB = 'azure'
|
37
|
+
GOOGLE_CLOUD = 'gcs'
|
38
|
+
CUSTOM = 'custom'
|
39
|
+
|
40
|
+
BACKEND_CHOICES = [
|
41
|
+
(FILE_SYSTEM, 'File System'),
|
42
|
+
(AWS_S3, 'AWS S3'),
|
43
|
+
(AZURE_BLOB, 'Azure Blob Storage'),
|
44
|
+
(GOOGLE_CLOUD, 'Google Cloud Storage'),
|
45
|
+
(CUSTOM, 'Custom Backend'),
|
46
|
+
]
|
47
|
+
|
48
|
+
created = models.DateTimeField(auto_now_add=True, editable=False, db_index=True)
|
49
|
+
modified = models.DateTimeField(auto_now=True)
|
50
|
+
|
51
|
+
group = models.ForeignKey(
|
52
|
+
"account.Group",
|
53
|
+
related_name="file_managers",
|
54
|
+
null=True,
|
55
|
+
blank=True,
|
56
|
+
default=None,
|
57
|
+
on_delete=models.CASCADE,
|
58
|
+
help_text="Group that owns this file manager configuration"
|
59
|
+
)
|
60
|
+
|
61
|
+
name = models.CharField(
|
62
|
+
max_length=255,
|
63
|
+
db_index=True,
|
64
|
+
help_text="Descriptive name for this file manager configuration"
|
65
|
+
)
|
66
|
+
|
67
|
+
description = models.TextField(
|
68
|
+
blank=True,
|
69
|
+
default="",
|
70
|
+
help_text="Optional description of this file manager's purpose"
|
71
|
+
)
|
72
|
+
|
73
|
+
backend_type = models.CharField(
|
74
|
+
max_length=32,
|
75
|
+
choices=BACKEND_CHOICES,
|
76
|
+
db_index=True,
|
77
|
+
help_text="Type of storage backend (file, s3, azure, gcs, custom)"
|
78
|
+
)
|
79
|
+
|
80
|
+
backend_url = models.CharField(
|
81
|
+
max_length=500,
|
82
|
+
help_text="Base URL or connection string for the storage backend"
|
83
|
+
)
|
84
|
+
|
85
|
+
supports_direct_upload = models.BooleanField(
|
86
|
+
default=False,
|
87
|
+
help_text="Whether this backend supports direct upload (pre-signed URLs)"
|
88
|
+
)
|
89
|
+
|
90
|
+
max_file_size = models.BigIntegerField(
|
91
|
+
default=100 * 1024 * 1024, # 100MB default
|
92
|
+
help_text="Maximum file size in bytes (0 for unlimited)"
|
93
|
+
)
|
94
|
+
|
95
|
+
allowed_extensions = models.JSONField(
|
96
|
+
default=list,
|
97
|
+
blank=True,
|
98
|
+
help_text="List of allowed file extensions (empty for all)"
|
99
|
+
)
|
100
|
+
|
101
|
+
allowed_mime_types = models.JSONField(
|
102
|
+
default=list,
|
103
|
+
blank=True,
|
104
|
+
help_text="List of allowed MIME types (empty for all)"
|
105
|
+
)
|
106
|
+
|
107
|
+
is_active = models.BooleanField(
|
108
|
+
default=True,
|
109
|
+
help_text="Whether this file manager is active and can be used"
|
110
|
+
)
|
111
|
+
|
112
|
+
is_default = models.BooleanField(
|
113
|
+
default=False,
|
114
|
+
help_text="Whether this is the default file manager for the group or user"
|
115
|
+
)
|
116
|
+
|
117
|
+
class Meta:
|
118
|
+
unique_together = [
|
119
|
+
['group', 'name'],
|
120
|
+
]
|
121
|
+
indexes = [
|
122
|
+
models.Index(fields=['backend_type', 'is_active']),
|
123
|
+
models.Index(fields=['group', 'is_default']),
|
124
|
+
models.Index(fields=['user', 'is_default']),
|
125
|
+
models.Index(fields=['group', 'backend_type']),
|
126
|
+
]
|
127
|
+
|
128
|
+
def __str__(self):
|
129
|
+
group_name = self.group.name if self.group else "Global"
|
130
|
+
return f"{self.name} ({self.get_backend_type_display()}) - {group_name}"
|
131
|
+
|
132
|
+
def get_setting(self, key, default=None):
|
133
|
+
"""Get a specific setting value"""
|
134
|
+
return self.get_secret(key, default)
|
135
|
+
|
136
|
+
def set_setting(self, key, value):
|
137
|
+
"""Set a specific setting value"""
|
138
|
+
self.set_secret(key, value)
|
139
|
+
|
140
|
+
@property
|
141
|
+
def settings(self):
|
142
|
+
return self.secrets
|
143
|
+
|
144
|
+
@property
|
145
|
+
def is_file_system(self):
|
146
|
+
return self.backend_type == self.FILE_SYSTEM
|
147
|
+
|
148
|
+
@property
|
149
|
+
def is_s3(self):
|
150
|
+
return self.backend_type == self.AWS_S3
|
151
|
+
|
152
|
+
@property
|
153
|
+
def is_azure(self):
|
154
|
+
return self.backend_type == self.AZURE_BLOB
|
155
|
+
|
156
|
+
@property
|
157
|
+
def is_gcs(self):
|
158
|
+
return self.backend_type == self.GOOGLE_CLOUD
|
159
|
+
|
160
|
+
@property
|
161
|
+
def is_custom(self):
|
162
|
+
return self.backend_type == self.CUSTOM
|
163
|
+
|
164
|
+
def can_upload_file(self, filename, file_size=None):
|
165
|
+
"""Check if a file can be uploaded based on restrictions"""
|
166
|
+
if not self.is_active:
|
167
|
+
return False, "File manager is not active"
|
168
|
+
|
169
|
+
# Check file size
|
170
|
+
if file_size and self.max_file_size > 0 and file_size > self.max_file_size:
|
171
|
+
return False, f"File size exceeds maximum of {self.max_file_size} bytes"
|
172
|
+
|
173
|
+
# Check file extension
|
174
|
+
if self.allowed_extensions:
|
175
|
+
import os
|
176
|
+
_, ext = os.path.splitext(filename.lower())
|
177
|
+
if ext and ext[1:] not in [e.lower() for e in self.allowed_extensions]:
|
178
|
+
return False, f"File extension {ext} is not allowed"
|
179
|
+
|
180
|
+
return True, "File can be uploaded"
|
181
|
+
|
182
|
+
def can_upload_mime_type(self, mime_type):
|
183
|
+
"""Check if a MIME type is allowed"""
|
184
|
+
if not self.allowed_mime_types:
|
185
|
+
return True
|
186
|
+
return mime_type.lower() in [mt.lower() for mt in self.allowed_mime_types]
|
187
|
+
|
188
|
+
def save(self, *args, **kwargs):
|
189
|
+
"""Custom save to enforce only one default per group"""
|
190
|
+
if self.is_default:
|
191
|
+
# Set all other file managers in the same group to not default
|
192
|
+
FileManager.objects.filter(
|
193
|
+
group=self.group,
|
194
|
+
is_default=True
|
195
|
+
).exclude(pk=self.pk).update(is_default=False)
|
196
|
+
|
197
|
+
super().save(*args, **kwargs)
|
198
|
+
|
199
|
+
@classmethod
|
200
|
+
def get_from_request(cls, request):
|
201
|
+
"""Get the file manager from the request"""
|
202
|
+
if request.DATA.fileman:
|
203
|
+
return cls.objects.get(pk=request.DATA.fileman)
|
204
|
+
if request.DATA.use_groups_fileman:
|
205
|
+
return cls.get_for_user_group(group=request.group)
|
206
|
+
return cls.get_for_user_group(user=request.user, group=request.group)
|
207
|
+
|
208
|
+
@classmethod
|
209
|
+
def get_for_user_group(cls, user=None, group=None):
|
210
|
+
"""Get the file manager from the user and/or group"""
|
211
|
+
file_manager = None
|
212
|
+
if not file_manager and user:
|
213
|
+
file_manager = cls.objects.filter(
|
214
|
+
user=user, group=group, is_default=True
|
215
|
+
).first()
|
216
|
+
|
217
|
+
if not file_manager and group:
|
218
|
+
file_manager = cls.objects.filter(
|
219
|
+
group=group, is_default=True
|
220
|
+
).first()
|
221
|
+
|
222
|
+
if not file_manager:
|
223
|
+
file_manager = cls.objects.filter(
|
224
|
+
group=None, user=None, is_default=True
|
225
|
+
).first()
|
226
|
+
|
227
|
+
return file_manager
|
File without changes
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""
|
2
|
+
File Manager REST API endpoints
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .fileman import on_filemanager, on_file
|
6
|
+
from .upload import (
|
7
|
+
on_upload_initiate,
|
8
|
+
on_upload_finalize,
|
9
|
+
on_direct_upload,
|
10
|
+
on_download
|
11
|
+
)
|
12
|
+
|
13
|
+
__all__ = [
|
14
|
+
# File manager model endpoints
|
15
|
+
'on_filemanager',
|
16
|
+
'on_file',
|
17
|
+
|
18
|
+
# File upload/download endpoints
|
19
|
+
'on_upload_initiate',
|
20
|
+
'on_upload_finalize',
|
21
|
+
'on_direct_upload',
|
22
|
+
'on_download'
|
23
|
+
]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from mojo import decorators as md
|
2
|
+
from mojo.apps.fileman.models import File, FileManager
|
3
|
+
|
4
|
+
|
5
|
+
@md.URL('manager')
|
6
|
+
@md.URL('manager/<int:pk>')
|
7
|
+
def on_filemanager(request, pk=None):
|
8
|
+
return FileManager.on_rest_request(request, pk)
|
9
|
+
|
10
|
+
@md.URL('file')
|
11
|
+
@md.URL('file/<int:pk>')
|
12
|
+
def on_file(request, pk=None):
|
13
|
+
return File.on_rest_request(request, pk)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from mojo import decorators as md
|
2
|
+
from mojo import JsonResponse
|
3
|
+
from mojo.apps.fileman.models import File, FileManager
|
4
|
+
from mojo.apps.fileman.utils.upload import (
|
5
|
+
initiate_upload,
|
6
|
+
finalize_upload,
|
7
|
+
direct_upload,
|
8
|
+
get_download_url
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
@md.POST('upload/initiate')
|
13
|
+
def on_upload_initiate(request):
|
14
|
+
"""
|
15
|
+
Initiate a file upload and get upload URLs
|
16
|
+
|
17
|
+
Request body format:
|
18
|
+
{
|
19
|
+
"files": [
|
20
|
+
{
|
21
|
+
"filename": "document.pdf",
|
22
|
+
"content_type": "application/pdf",
|
23
|
+
"size": 1024000
|
24
|
+
}
|
25
|
+
],
|
26
|
+
"file_manager_id": 123, // optional
|
27
|
+
"group_id": 456, // optional
|
28
|
+
"metadata": { // optional global metadata
|
29
|
+
"source": "web_upload",
|
30
|
+
"category": "documents"
|
31
|
+
}
|
32
|
+
}
|
33
|
+
"""
|
34
|
+
response_data = initiate_upload(request, request.DATA)
|
35
|
+
status_code = response_data.pop('status_code', 200)
|
36
|
+
return JsonResponse(response_data, status=status_code)
|
37
|
+
|
38
|
+
|
39
|
+
@md.POST('upload/finalize')
|
40
|
+
def on_upload_finalize(request):
|
41
|
+
"""
|
42
|
+
Finalize a file upload
|
43
|
+
|
44
|
+
Request body format:
|
45
|
+
{
|
46
|
+
"upload_token": "abc123...",
|
47
|
+
"file_size": 1024000, // optional
|
48
|
+
"checksum": "md5:abcdef...", // optional
|
49
|
+
"metadata": { // optional additional metadata
|
50
|
+
"processing_complete": true
|
51
|
+
}
|
52
|
+
}
|
53
|
+
"""
|
54
|
+
response_data = finalize_upload(request, request.DATA)
|
55
|
+
status_code = response_data.pop('status_code', 200)
|
56
|
+
return JsonResponse(response_data, status=status_code)
|
57
|
+
|
58
|
+
|
59
|
+
@md.POST('upload/<str:upload_token>')
|
60
|
+
def on_direct_upload(request, upload_token):
|
61
|
+
"""
|
62
|
+
Handle direct file upload for backends that don't support pre-signed URLs
|
63
|
+
"""
|
64
|
+
if not request.FILES or 'file' not in request.FILES:
|
65
|
+
return JsonResponse({
|
66
|
+
'success': False,
|
67
|
+
'error': 'No file provided'
|
68
|
+
}, status=400)
|
69
|
+
|
70
|
+
file_data = request.FILES['file']
|
71
|
+
response_data = direct_upload(request, upload_token, file_data)
|
72
|
+
status_code = response_data.pop('status_code', 200)
|
73
|
+
return JsonResponse(response_data, status=status_code)
|
74
|
+
|
75
|
+
|
76
|
+
@md.GET('download/<str:download_token>')
|
77
|
+
def on_download(request, download_token):
|
78
|
+
"""
|
79
|
+
Get a download URL for a file
|
80
|
+
"""
|
81
|
+
response_data = get_download_url(request, download_token)
|
82
|
+
|
83
|
+
# If direct URL is available, redirect to it
|
84
|
+
if response_data.get('success') and 'download_url' in response_data:
|
85
|
+
return JsonResponse({
|
86
|
+
'success': True,
|
87
|
+
'download_url': response_data['download_url'],
|
88
|
+
'file': response_data.get('file', {})
|
89
|
+
})
|
90
|
+
|
91
|
+
status_code = response_data.pop('status_code', 200)
|
92
|
+
return JsonResponse(response_data, status=status_code)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# File upload utility functions
|
2
|
+
|
3
|
+
from .upload import (
|
4
|
+
get_file_manager,
|
5
|
+
validate_file_request,
|
6
|
+
initiate_upload,
|
7
|
+
finalize_upload,
|
8
|
+
direct_upload,
|
9
|
+
get_download_url
|
10
|
+
)
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
'get_file_manager',
|
14
|
+
'validate_file_request',
|
15
|
+
'initiate_upload',
|
16
|
+
'finalize_upload',
|
17
|
+
'direct_upload',
|
18
|
+
'get_download_url'
|
19
|
+
]
|