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,665 @@
|
|
1
|
+
"""
|
2
|
+
Complete usage example for Django File Manager (fileman)
|
3
|
+
|
4
|
+
This example demonstrates how to use the fileman system for file uploads
|
5
|
+
with both S3 and local file system backends.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import json
|
10
|
+
import hashlib
|
11
|
+
from datetime import datetime, timedelta
|
12
|
+
from django.contrib.auth import get_user_model
|
13
|
+
from django.test import TestCase, Client
|
14
|
+
from django.urls import reverse
|
15
|
+
from django.core.files.uploadedfile import SimpleUploadedFile
|
16
|
+
|
17
|
+
from mojo.apps.fileman.models import FileManager, File
|
18
|
+
from mojo.apps.fileman.backends import get_backend
|
19
|
+
from mojo.apps.account.models import Group
|
20
|
+
|
21
|
+
User = get_user_model()
|
22
|
+
|
23
|
+
|
24
|
+
class FilemanUsageExample:
|
25
|
+
"""
|
26
|
+
Complete example of using the fileman system
|
27
|
+
"""
|
28
|
+
|
29
|
+
def __init__(self):
|
30
|
+
self.client = Client()
|
31
|
+
self.user = None
|
32
|
+
self.group = None
|
33
|
+
self.s3_file_manager = None
|
34
|
+
self.local_file_manager = None
|
35
|
+
|
36
|
+
def setup_test_data(self):
|
37
|
+
"""Set up test users, groups, and file managers"""
|
38
|
+
|
39
|
+
# Create test user and group
|
40
|
+
self.user = User.objects.create_user(
|
41
|
+
username='testuser',
|
42
|
+
email='test@example.com',
|
43
|
+
password='testpass123'
|
44
|
+
)
|
45
|
+
|
46
|
+
self.group = Group.objects.create(
|
47
|
+
name='Test Group',
|
48
|
+
description='Group for testing file uploads'
|
49
|
+
)
|
50
|
+
|
51
|
+
# Add user to group (assuming GroupMember model exists)
|
52
|
+
# GroupMember.objects.create(user=self.user, group=self.group)
|
53
|
+
|
54
|
+
# Create S3 file manager
|
55
|
+
self.s3_file_manager = FileManager.objects.create(
|
56
|
+
name="AWS S3 Test Storage",
|
57
|
+
description="S3 storage for testing",
|
58
|
+
backend_type="s3",
|
59
|
+
backend_url="s3://test-bucket/",
|
60
|
+
supports_direct_upload=True,
|
61
|
+
max_file_size=100 * 1024 * 1024, # 100MB
|
62
|
+
allowed_extensions=["pdf", "jpg", "png", "txt"],
|
63
|
+
allowed_mime_types=[
|
64
|
+
"application/pdf",
|
65
|
+
"image/jpeg",
|
66
|
+
"image/png",
|
67
|
+
"text/plain"
|
68
|
+
],
|
69
|
+
settings={
|
70
|
+
"bucket_name": "test-bucket",
|
71
|
+
"region_name": "us-east-1",
|
72
|
+
"access_key_id": "test_key_id",
|
73
|
+
"secret_access_key": "test_secret_key",
|
74
|
+
"upload_expires_in": 3600,
|
75
|
+
"download_expires_in": 3600,
|
76
|
+
"server_side_encryption": "AES256"
|
77
|
+
},
|
78
|
+
group=self.group,
|
79
|
+
is_default=True,
|
80
|
+
is_active=True
|
81
|
+
)
|
82
|
+
|
83
|
+
# Create local file system manager
|
84
|
+
self.local_file_manager = FileManager.objects.create(
|
85
|
+
name="Local File Storage",
|
86
|
+
description="Local file system for testing",
|
87
|
+
backend_type="file",
|
88
|
+
backend_url="file:///tmp/test_uploads/",
|
89
|
+
supports_direct_upload=False,
|
90
|
+
max_file_size=50 * 1024 * 1024, # 50MB
|
91
|
+
settings={
|
92
|
+
"base_path": "/tmp/test_uploads",
|
93
|
+
"base_url": "/media/test/",
|
94
|
+
"create_directories": True,
|
95
|
+
"permissions": 0o644,
|
96
|
+
"directory_permissions": 0o755,
|
97
|
+
"temp_upload_path": "/tmp/test_uploads/temp"
|
98
|
+
},
|
99
|
+
group=self.group,
|
100
|
+
is_active=True
|
101
|
+
)
|
102
|
+
|
103
|
+
def example_1_initiate_single_upload(self):
|
104
|
+
"""Example 1: Initiate upload for a single file"""
|
105
|
+
|
106
|
+
print("=== Example 1: Initiate Single File Upload ===")
|
107
|
+
|
108
|
+
# Login user
|
109
|
+
self.client.login(username='testuser', password='testpass123')
|
110
|
+
|
111
|
+
# Prepare upload request
|
112
|
+
upload_data = {
|
113
|
+
"files": [
|
114
|
+
{
|
115
|
+
"filename": "test_document.pdf",
|
116
|
+
"content_type": "application/pdf",
|
117
|
+
"size": 1024000 # 1MB
|
118
|
+
}
|
119
|
+
],
|
120
|
+
"file_manager_id": self.s3_file_manager.id,
|
121
|
+
"metadata": {
|
122
|
+
"source": "example_upload",
|
123
|
+
"category": "documents",
|
124
|
+
"description": "Test document upload"
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
# Initiate upload
|
129
|
+
response = self.client.post(
|
130
|
+
'/fileman/initiate-upload/',
|
131
|
+
data=json.dumps(upload_data),
|
132
|
+
content_type='application/json'
|
133
|
+
)
|
134
|
+
|
135
|
+
print(f"Response status: {response.status_code}")
|
136
|
+
result = response.json()
|
137
|
+
print(f"Success: {result.get('success')}")
|
138
|
+
|
139
|
+
if result.get('success'):
|
140
|
+
file_data = result['files'][0]
|
141
|
+
print(f"Upload token: {file_data['upload_token']}")
|
142
|
+
print(f"Upload URL: {file_data['upload_url']}")
|
143
|
+
print(f"Method: {file_data['method']}")
|
144
|
+
print(f"Fields: {file_data.get('fields', {})}")
|
145
|
+
|
146
|
+
return file_data
|
147
|
+
else:
|
148
|
+
print(f"Error: {result.get('error')}")
|
149
|
+
return None
|
150
|
+
|
151
|
+
def example_2_initiate_multiple_uploads(self):
|
152
|
+
"""Example 2: Initiate upload for multiple files"""
|
153
|
+
|
154
|
+
print("\n=== Example 2: Initiate Multiple File Upload ===")
|
155
|
+
|
156
|
+
# Prepare multiple files
|
157
|
+
upload_data = {
|
158
|
+
"files": [
|
159
|
+
{
|
160
|
+
"filename": "image1.jpg",
|
161
|
+
"content_type": "image/jpeg",
|
162
|
+
"size": 512000
|
163
|
+
},
|
164
|
+
{
|
165
|
+
"filename": "image2.png",
|
166
|
+
"content_type": "image/png",
|
167
|
+
"size": 768000
|
168
|
+
},
|
169
|
+
{
|
170
|
+
"filename": "readme.txt",
|
171
|
+
"content_type": "text/plain",
|
172
|
+
"size": 4096
|
173
|
+
}
|
174
|
+
],
|
175
|
+
"metadata": {
|
176
|
+
"batch_id": "batch_001",
|
177
|
+
"uploaded_via": "web_interface"
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
response = self.client.post(
|
182
|
+
'/fileman/initiate-upload/',
|
183
|
+
data=json.dumps(upload_data),
|
184
|
+
content_type='application/json'
|
185
|
+
)
|
186
|
+
|
187
|
+
result = response.json()
|
188
|
+
print(f"Multiple upload success: {result.get('success')}")
|
189
|
+
print(f"Number of files initiated: {len(result.get('files', []))}")
|
190
|
+
|
191
|
+
if result.get('errors'):
|
192
|
+
print(f"Errors: {result['errors']}")
|
193
|
+
|
194
|
+
return result.get('files', [])
|
195
|
+
|
196
|
+
def example_3_direct_upload_simulation(self, file_data):
|
197
|
+
"""Example 3: Simulate direct upload to S3"""
|
198
|
+
|
199
|
+
print("\n=== Example 3: Simulate Direct Upload ===")
|
200
|
+
|
201
|
+
if not file_data:
|
202
|
+
print("No file data available for upload")
|
203
|
+
return False
|
204
|
+
|
205
|
+
print(f"Simulating upload to: {file_data['upload_url']}")
|
206
|
+
print(f"Using method: {file_data['method']}")
|
207
|
+
|
208
|
+
# In a real scenario, this would be done by the client (browser)
|
209
|
+
# uploading directly to S3 using the pre-signed URL
|
210
|
+
|
211
|
+
# For demonstration, we'll just print what would happen
|
212
|
+
print("Client would now:")
|
213
|
+
print("1. Create FormData with the provided fields")
|
214
|
+
print("2. Add the actual file to the FormData")
|
215
|
+
print("3. POST to the upload_url")
|
216
|
+
print("4. Handle the response from S3")
|
217
|
+
|
218
|
+
# Simulate successful upload
|
219
|
+
print("✓ Simulated successful upload to S3")
|
220
|
+
return True
|
221
|
+
|
222
|
+
def example_4_finalize_upload(self, file_data):
|
223
|
+
"""Example 4: Finalize the upload"""
|
224
|
+
|
225
|
+
print("\n=== Example 4: Finalize Upload ===")
|
226
|
+
|
227
|
+
if not file_data:
|
228
|
+
print("No file data available for finalization")
|
229
|
+
return None
|
230
|
+
|
231
|
+
# Create test file content for checksum
|
232
|
+
test_content = b"This is test PDF content"
|
233
|
+
md5_hash = hashlib.md5(test_content).hexdigest()
|
234
|
+
|
235
|
+
finalize_data = {
|
236
|
+
"upload_token": file_data['upload_token'],
|
237
|
+
"file_size": len(test_content),
|
238
|
+
"checksum": f"md5:{md5_hash}",
|
239
|
+
"metadata": {
|
240
|
+
"finalized_at": datetime.now().isoformat(),
|
241
|
+
"processing_complete": True
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
response = self.client.post(
|
246
|
+
'/fileman/finalize-upload/',
|
247
|
+
data=json.dumps(finalize_data),
|
248
|
+
content_type='application/json'
|
249
|
+
)
|
250
|
+
|
251
|
+
result = response.json()
|
252
|
+
print(f"Finalization success: {result.get('success')}")
|
253
|
+
|
254
|
+
if result.get('success'):
|
255
|
+
file_info = result['file']
|
256
|
+
print(f"File ID: {file_info['id']}")
|
257
|
+
print(f"Final filename: {file_info['filename']}")
|
258
|
+
print(f"File path: {file_info['file_path']}")
|
259
|
+
print(f"Status: {file_info['upload_status']}")
|
260
|
+
print(f"Download URL: {file_info['download_url']}")
|
261
|
+
|
262
|
+
return file_info
|
263
|
+
else:
|
264
|
+
print(f"Finalization error: {result.get('error')}")
|
265
|
+
return None
|
266
|
+
|
267
|
+
def example_5_local_file_upload(self):
|
268
|
+
"""Example 5: Upload to local file system backend"""
|
269
|
+
|
270
|
+
print("\n=== Example 5: Local File System Upload ===")
|
271
|
+
|
272
|
+
# Create a test file
|
273
|
+
test_content = b"This is a test file for local upload"
|
274
|
+
test_file = SimpleUploadedFile(
|
275
|
+
"test_local.txt",
|
276
|
+
test_content,
|
277
|
+
content_type="text/plain"
|
278
|
+
)
|
279
|
+
|
280
|
+
# First initiate upload for local backend
|
281
|
+
upload_data = {
|
282
|
+
"files": [
|
283
|
+
{
|
284
|
+
"filename": "test_local.txt",
|
285
|
+
"content_type": "text/plain",
|
286
|
+
"size": len(test_content)
|
287
|
+
}
|
288
|
+
],
|
289
|
+
"file_manager_id": self.local_file_manager.id
|
290
|
+
}
|
291
|
+
|
292
|
+
response = self.client.post(
|
293
|
+
'/fileman/initiate-upload/',
|
294
|
+
data=json.dumps(upload_data),
|
295
|
+
content_type='application/json'
|
296
|
+
)
|
297
|
+
|
298
|
+
result = response.json()
|
299
|
+
if not result.get('success'):
|
300
|
+
print(f"Failed to initiate local upload: {result.get('error')}")
|
301
|
+
return None
|
302
|
+
|
303
|
+
file_data = result['files'][0]
|
304
|
+
upload_token = file_data['upload_token']
|
305
|
+
|
306
|
+
print(f"Local upload initiated with token: {upload_token}")
|
307
|
+
|
308
|
+
# Upload file using the direct upload endpoint
|
309
|
+
response = self.client.post(
|
310
|
+
f'/fileman/upload/{upload_token}/',
|
311
|
+
data={'file': test_file},
|
312
|
+
format='multipart'
|
313
|
+
)
|
314
|
+
|
315
|
+
result = response.json()
|
316
|
+
print(f"Direct upload success: {result.get('success')}")
|
317
|
+
|
318
|
+
if result.get('success'):
|
319
|
+
# Finalize the upload
|
320
|
+
finalize_data = {
|
321
|
+
"upload_token": upload_token,
|
322
|
+
"file_size": len(test_content)
|
323
|
+
}
|
324
|
+
|
325
|
+
response = self.client.post(
|
326
|
+
'/fileman/finalize-upload/',
|
327
|
+
data=json.dumps(finalize_data),
|
328
|
+
content_type='application/json'
|
329
|
+
)
|
330
|
+
|
331
|
+
result = response.json()
|
332
|
+
print(f"Local upload finalized: {result.get('success')}")
|
333
|
+
return result.get('file')
|
334
|
+
|
335
|
+
return None
|
336
|
+
|
337
|
+
def example_6_file_management(self):
|
338
|
+
"""Example 6: File management operations"""
|
339
|
+
|
340
|
+
print("\n=== Example 6: File Management ===")
|
341
|
+
|
342
|
+
# Get all files uploaded by user
|
343
|
+
user_files = File.objects.filter(
|
344
|
+
uploaded_by=self.user,
|
345
|
+
upload_status=File.COMPLETED
|
346
|
+
)
|
347
|
+
|
348
|
+
print(f"User has {user_files.count()} completed uploads")
|
349
|
+
|
350
|
+
for file_obj in user_files:
|
351
|
+
print(f"- {file_obj.original_filename} ({file_obj.get_human_readable_size()})")
|
352
|
+
print(f" Status: {file_obj.get_upload_status_display()}")
|
353
|
+
print(f" Path: {file_obj.file_path}")
|
354
|
+
print(f" Created: {file_obj.created}")
|
355
|
+
|
356
|
+
# Check if file exists in backend
|
357
|
+
try:
|
358
|
+
backend = get_backend(file_obj.file_manager)
|
359
|
+
exists = backend.exists(file_obj.file_path)
|
360
|
+
print(f" Exists in storage: {exists}")
|
361
|
+
|
362
|
+
if exists:
|
363
|
+
file_size = backend.get_file_size(file_obj.file_path)
|
364
|
+
print(f" Backend file size: {file_size}")
|
365
|
+
|
366
|
+
# Generate download URL
|
367
|
+
download_url = backend.get_url(file_obj.file_path, expires_in=300)
|
368
|
+
print(f" Download URL: {download_url}")
|
369
|
+
|
370
|
+
except Exception as e:
|
371
|
+
print(f" Backend error: {e}")
|
372
|
+
|
373
|
+
print()
|
374
|
+
|
375
|
+
def example_7_download_file(self, upload_token):
|
376
|
+
"""Example 7: Download a file"""
|
377
|
+
|
378
|
+
print("\n=== Example 7: Download File ===")
|
379
|
+
|
380
|
+
if not upload_token:
|
381
|
+
print("No upload token provided")
|
382
|
+
return
|
383
|
+
|
384
|
+
# Use the download endpoint
|
385
|
+
response = self.client.get(f'/fileman/download/{upload_token}/')
|
386
|
+
|
387
|
+
print(f"Download response status: {response.status_code}")
|
388
|
+
|
389
|
+
if response.status_code == 302: # Redirect to actual file
|
390
|
+
print(f"Redirected to: {response['Location']}")
|
391
|
+
elif response.status_code == 200:
|
392
|
+
print("File downloaded successfully")
|
393
|
+
print(f"Content type: {response.get('Content-Type')}")
|
394
|
+
print(f"Content length: {response.get('Content-Length')}")
|
395
|
+
else:
|
396
|
+
try:
|
397
|
+
result = response.json()
|
398
|
+
print(f"Download error: {result.get('error')}")
|
399
|
+
except:
|
400
|
+
print(f"Download failed with status {response.status_code}")
|
401
|
+
|
402
|
+
def example_8_backend_operations(self):
|
403
|
+
"""Example 8: Direct backend operations"""
|
404
|
+
|
405
|
+
print("\n=== Example 8: Backend Operations ===")
|
406
|
+
|
407
|
+
# Test S3 backend
|
408
|
+
try:
|
409
|
+
s3_backend = get_backend(self.s3_file_manager)
|
410
|
+
print(f"S3 Backend: {s3_backend}")
|
411
|
+
|
412
|
+
# Test configuration validation
|
413
|
+
is_valid, errors = s3_backend.validate_configuration()
|
414
|
+
print(f"S3 config valid: {is_valid}")
|
415
|
+
if errors:
|
416
|
+
print(f"S3 errors: {errors}")
|
417
|
+
|
418
|
+
# Test file operations (if backend is properly configured)
|
419
|
+
test_path = "test/example.txt"
|
420
|
+
test_content = b"Hello from S3 backend test"
|
421
|
+
|
422
|
+
print(f"Testing file operations with path: {test_path}")
|
423
|
+
|
424
|
+
except Exception as e:
|
425
|
+
print(f"S3 backend error: {e}")
|
426
|
+
|
427
|
+
# Test local backend
|
428
|
+
try:
|
429
|
+
local_backend = get_backend(self.local_file_manager)
|
430
|
+
print(f"Local Backend: {local_backend}")
|
431
|
+
|
432
|
+
# Test configuration validation
|
433
|
+
is_valid, errors = local_backend.validate_configuration()
|
434
|
+
print(f"Local config valid: {is_valid}")
|
435
|
+
if errors:
|
436
|
+
print(f"Local errors: {errors}")
|
437
|
+
|
438
|
+
# Test file operations
|
439
|
+
import io
|
440
|
+
test_content = b"Hello from local backend test"
|
441
|
+
test_file = io.BytesIO(test_content)
|
442
|
+
|
443
|
+
saved_path = local_backend.save(test_file, "backend_test.txt")
|
444
|
+
print(f"Saved test file to: {saved_path}")
|
445
|
+
|
446
|
+
# Check if file exists
|
447
|
+
exists = local_backend.exists(saved_path)
|
448
|
+
print(f"File exists: {exists}")
|
449
|
+
|
450
|
+
if exists:
|
451
|
+
file_size = local_backend.get_file_size(saved_path)
|
452
|
+
print(f"File size: {file_size}")
|
453
|
+
|
454
|
+
# Generate URL
|
455
|
+
file_url = local_backend.get_url(saved_path)
|
456
|
+
print(f"File URL: {file_url}")
|
457
|
+
|
458
|
+
# Clean up
|
459
|
+
deleted = local_backend.delete(saved_path)
|
460
|
+
print(f"File deleted: {deleted}")
|
461
|
+
|
462
|
+
except Exception as e:
|
463
|
+
print(f"Local backend error: {e}")
|
464
|
+
|
465
|
+
def example_9_cleanup_operations(self):
|
466
|
+
"""Example 9: Cleanup operations"""
|
467
|
+
|
468
|
+
print("\n=== Example 9: Cleanup Operations ===")
|
469
|
+
|
470
|
+
# Find expired uploads
|
471
|
+
from django.utils import timezone
|
472
|
+
cutoff_date = timezone.now() - timedelta(hours=1)
|
473
|
+
|
474
|
+
expired_files = File.objects.filter(
|
475
|
+
upload_expires_at__lt=timezone.now(),
|
476
|
+
upload_status__in=[File.PENDING, File.UPLOADING]
|
477
|
+
)
|
478
|
+
|
479
|
+
print(f"Found {expired_files.count()} expired uploads")
|
480
|
+
|
481
|
+
for file_obj in expired_files:
|
482
|
+
print(f"- {file_obj.original_filename} (expired: {file_obj.upload_expires_at})")
|
483
|
+
file_obj.mark_as_expired()
|
484
|
+
|
485
|
+
# Find failed uploads older than cutoff
|
486
|
+
old_failed = File.objects.filter(
|
487
|
+
created__lt=cutoff_date,
|
488
|
+
upload_status=File.FAILED
|
489
|
+
)
|
490
|
+
|
491
|
+
print(f"Found {old_failed.count()} old failed uploads")
|
492
|
+
|
493
|
+
# Backend cleanup
|
494
|
+
for file_manager in [self.s3_file_manager, self.local_file_manager]:
|
495
|
+
try:
|
496
|
+
backend = get_backend(file_manager)
|
497
|
+
print(f"Running cleanup for {file_manager.name}")
|
498
|
+
backend.cleanup_expired_uploads(cutoff_date)
|
499
|
+
except Exception as e:
|
500
|
+
print(f"Cleanup error for {file_manager.name}: {e}")
|
501
|
+
|
502
|
+
def run_all_examples(self):
|
503
|
+
"""Run all examples in sequence"""
|
504
|
+
|
505
|
+
print("Django File Manager Usage Examples")
|
506
|
+
print("==================================")
|
507
|
+
|
508
|
+
# Setup
|
509
|
+
self.setup_test_data()
|
510
|
+
print("✓ Test data setup complete")
|
511
|
+
|
512
|
+
# Run examples
|
513
|
+
file_data = self.example_1_initiate_single_upload()
|
514
|
+
self.example_2_initiate_multiple_uploads()
|
515
|
+
|
516
|
+
if file_data:
|
517
|
+
upload_success = self.example_3_direct_upload_simulation(file_data)
|
518
|
+
if upload_success:
|
519
|
+
finalized_file = self.example_4_finalize_upload(file_data)
|
520
|
+
if finalized_file:
|
521
|
+
self.example_7_download_file(file_data['upload_token'])
|
522
|
+
|
523
|
+
local_file = self.example_5_local_file_upload()
|
524
|
+
self.example_6_file_management()
|
525
|
+
self.example_8_backend_operations()
|
526
|
+
self.example_9_cleanup_operations()
|
527
|
+
|
528
|
+
print("\n=== All Examples Complete ===")
|
529
|
+
|
530
|
+
|
531
|
+
# JavaScript client example for browser usage
|
532
|
+
JAVASCRIPT_EXAMPLE = """
|
533
|
+
// JavaScript example for browser-based file uploads
|
534
|
+
|
535
|
+
class FilemanClient {
|
536
|
+
constructor(csrfToken) {
|
537
|
+
this.csrfToken = csrfToken;
|
538
|
+
this.baseUrl = '/fileman';
|
539
|
+
}
|
540
|
+
|
541
|
+
async uploadFiles(files, options = {}) {
|
542
|
+
try {
|
543
|
+
// 1. Initiate upload
|
544
|
+
const initResponse = await this.initiateUpload(files, options);
|
545
|
+
if (!initResponse.success) {
|
546
|
+
throw new Error(`Failed to initiate upload: ${initResponse.error}`);
|
547
|
+
}
|
548
|
+
|
549
|
+
// 2. Upload each file
|
550
|
+
const uploadPromises = initResponse.files.map(async (fileData, index) => {
|
551
|
+
const file = files[index];
|
552
|
+
|
553
|
+
// Upload to pre-signed URL or direct endpoint
|
554
|
+
await this.uploadFile(file, fileData);
|
555
|
+
|
556
|
+
// Finalize upload
|
557
|
+
return this.finalizeUpload(fileData.upload_token, file);
|
558
|
+
});
|
559
|
+
|
560
|
+
// 3. Wait for all uploads
|
561
|
+
const results = await Promise.all(uploadPromises);
|
562
|
+
return results;
|
563
|
+
|
564
|
+
} catch (error) {
|
565
|
+
console.error('Upload failed:', error);
|
566
|
+
throw error;
|
567
|
+
}
|
568
|
+
}
|
569
|
+
|
570
|
+
async initiateUpload(files, options) {
|
571
|
+
const response = await fetch(`${this.baseUrl}/initiate-upload/`, {
|
572
|
+
method: 'POST',
|
573
|
+
headers: {
|
574
|
+
'Content-Type': 'application/json',
|
575
|
+
'X-CSRFToken': this.csrfToken,
|
576
|
+
},
|
577
|
+
body: JSON.stringify({
|
578
|
+
files: files.map(file => ({
|
579
|
+
filename: file.name,
|
580
|
+
content_type: file.type,
|
581
|
+
size: file.size
|
582
|
+
})),
|
583
|
+
...options
|
584
|
+
})
|
585
|
+
});
|
586
|
+
|
587
|
+
return response.json();
|
588
|
+
}
|
589
|
+
|
590
|
+
async uploadFile(file, uploadData) {
|
591
|
+
const formData = new FormData();
|
592
|
+
|
593
|
+
// Add fields for S3 or other backends
|
594
|
+
Object.entries(uploadData.fields || {}).forEach(([key, value]) => {
|
595
|
+
formData.append(key, value);
|
596
|
+
});
|
597
|
+
|
598
|
+
// Add file last
|
599
|
+
formData.append('file', file);
|
600
|
+
|
601
|
+
const response = await fetch(uploadData.upload_url, {
|
602
|
+
method: uploadData.method,
|
603
|
+
body: formData
|
604
|
+
});
|
605
|
+
|
606
|
+
if (!response.ok) {
|
607
|
+
throw new Error(`Upload failed: ${response.statusText}`);
|
608
|
+
}
|
609
|
+
}
|
610
|
+
|
611
|
+
async finalizeUpload(uploadToken, file) {
|
612
|
+
const response = await fetch(`${this.baseUrl}/finalize-upload/`, {
|
613
|
+
method: 'POST',
|
614
|
+
headers: {
|
615
|
+
'Content-Type': 'application/json',
|
616
|
+
'X-CSRFToken': this.csrfToken,
|
617
|
+
},
|
618
|
+
body: JSON.stringify({
|
619
|
+
upload_token: uploadToken,
|
620
|
+
file_size: file.size
|
621
|
+
})
|
622
|
+
});
|
623
|
+
|
624
|
+
return response.json();
|
625
|
+
}
|
626
|
+
}
|
627
|
+
|
628
|
+
// Usage example:
|
629
|
+
const fileInput = document.getElementById('file-input');
|
630
|
+
const uploader = new FilemanClient(getCsrfToken());
|
631
|
+
|
632
|
+
fileInput.addEventListener('change', async (e) => {
|
633
|
+
const files = Array.from(e.target.files);
|
634
|
+
|
635
|
+
try {
|
636
|
+
const results = await uploader.uploadFiles(files, {
|
637
|
+
metadata: { source: 'web_upload' }
|
638
|
+
});
|
639
|
+
|
640
|
+
console.log('Upload completed:', results);
|
641
|
+
|
642
|
+
// Handle successful uploads
|
643
|
+
results.forEach(result => {
|
644
|
+
if (result.success) {
|
645
|
+
console.log(`Uploaded: ${result.file.original_filename}`);
|
646
|
+
// Update UI, show download links, etc.
|
647
|
+
}
|
648
|
+
});
|
649
|
+
|
650
|
+
} catch (error) {
|
651
|
+
console.error('Upload error:', error);
|
652
|
+
// Handle error - show message to user
|
653
|
+
}
|
654
|
+
});
|
655
|
+
"""
|
656
|
+
|
657
|
+
|
658
|
+
if __name__ == "__main__":
|
659
|
+
# This would typically be run in a Django environment
|
660
|
+
print("This example should be run within a Django environment")
|
661
|
+
print("You can copy the FilemanUsageExample class and adapt it for your needs")
|
662
|
+
|
663
|
+
# Uncomment to run examples (requires Django setup):
|
664
|
+
# example = FilemanUsageExample()
|
665
|
+
# example.run_all_examples()
|
@@ -0,0 +1 @@
|
|
1
|
+
# Management package for fileman app
|
@@ -0,0 +1 @@
|
|
1
|
+
# Management commands package for fileman app
|