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.
Files changed (194) hide show
  1. django_nativemojo-0.1.10.dist-info/LICENSE +19 -0
  2. django_nativemojo-0.1.10.dist-info/METADATA +96 -0
  3. django_nativemojo-0.1.10.dist-info/NOTICE +8 -0
  4. django_nativemojo-0.1.10.dist-info/RECORD +194 -0
  5. django_nativemojo-0.1.10.dist-info/WHEEL +4 -0
  6. mojo/__init__.py +3 -0
  7. mojo/apps/account/__init__.py +1 -0
  8. mojo/apps/account/admin.py +91 -0
  9. mojo/apps/account/apps.py +16 -0
  10. mojo/apps/account/migrations/0001_initial.py +77 -0
  11. mojo/apps/account/migrations/0002_user_is_email_verified_user_is_phone_verified.py +23 -0
  12. mojo/apps/account/migrations/0003_group_mojo_secrets_user_mojo_secrets.py +23 -0
  13. mojo/apps/account/migrations/__init__.py +0 -0
  14. mojo/apps/account/models/__init__.py +3 -0
  15. mojo/apps/account/models/group.py +98 -0
  16. mojo/apps/account/models/member.py +95 -0
  17. mojo/apps/account/models/pkey.py +18 -0
  18. mojo/apps/account/models/user.py +211 -0
  19. mojo/apps/account/rest/__init__.py +3 -0
  20. mojo/apps/account/rest/group.py +25 -0
  21. mojo/apps/account/rest/user.py +47 -0
  22. mojo/apps/account/utils/__init__.py +0 -0
  23. mojo/apps/account/utils/jwtoken.py +72 -0
  24. mojo/apps/account/utils/passkeys.py +54 -0
  25. mojo/apps/fileman/README.md +549 -0
  26. mojo/apps/fileman/__init__.py +0 -0
  27. mojo/apps/fileman/apps.py +15 -0
  28. mojo/apps/fileman/backends/__init__.py +117 -0
  29. mojo/apps/fileman/backends/base.py +319 -0
  30. mojo/apps/fileman/backends/filesystem.py +397 -0
  31. mojo/apps/fileman/backends/s3.py +398 -0
  32. mojo/apps/fileman/examples/configurations.py +378 -0
  33. mojo/apps/fileman/examples/usage_example.py +665 -0
  34. mojo/apps/fileman/management/__init__.py +1 -0
  35. mojo/apps/fileman/management/commands/__init__.py +1 -0
  36. mojo/apps/fileman/management/commands/cleanup_expired_uploads.py +222 -0
  37. mojo/apps/fileman/models/__init__.py +7 -0
  38. mojo/apps/fileman/models/file.py +292 -0
  39. mojo/apps/fileman/models/manager.py +227 -0
  40. mojo/apps/fileman/models/render.py +0 -0
  41. mojo/apps/fileman/rest/__init__ +0 -0
  42. mojo/apps/fileman/rest/__init__.py +23 -0
  43. mojo/apps/fileman/rest/fileman.py +13 -0
  44. mojo/apps/fileman/rest/upload.py +92 -0
  45. mojo/apps/fileman/utils/__init__.py +19 -0
  46. mojo/apps/fileman/utils/upload.py +616 -0
  47. mojo/apps/incident/__init__.py +1 -0
  48. mojo/apps/incident/handlers/__init__.py +3 -0
  49. mojo/apps/incident/handlers/event_handlers.py +142 -0
  50. mojo/apps/incident/migrations/0001_initial.py +83 -0
  51. mojo/apps/incident/migrations/0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more.py +44 -0
  52. mojo/apps/incident/migrations/0003_alter_event_model_id.py +18 -0
  53. mojo/apps/incident/migrations/0004_alter_incident_model_id.py +18 -0
  54. mojo/apps/incident/migrations/__init__.py +0 -0
  55. mojo/apps/incident/models/__init__.py +3 -0
  56. mojo/apps/incident/models/event.py +135 -0
  57. mojo/apps/incident/models/incident.py +33 -0
  58. mojo/apps/incident/models/rule.py +247 -0
  59. mojo/apps/incident/parsers/__init__.py +0 -0
  60. mojo/apps/incident/parsers/ossec/__init__.py +1 -0
  61. mojo/apps/incident/parsers/ossec/core.py +82 -0
  62. mojo/apps/incident/parsers/ossec/parsed.py +23 -0
  63. mojo/apps/incident/parsers/ossec/rules.py +124 -0
  64. mojo/apps/incident/parsers/ossec/utils.py +169 -0
  65. mojo/apps/incident/reporter.py +42 -0
  66. mojo/apps/incident/rest/__init__.py +2 -0
  67. mojo/apps/incident/rest/event.py +23 -0
  68. mojo/apps/incident/rest/ossec.py +22 -0
  69. mojo/apps/logit/__init__.py +0 -0
  70. mojo/apps/logit/admin.py +37 -0
  71. mojo/apps/logit/migrations/0001_initial.py +32 -0
  72. mojo/apps/logit/migrations/0002_log_duid_log_payload_log_username.py +28 -0
  73. mojo/apps/logit/migrations/0003_log_level.py +18 -0
  74. mojo/apps/logit/migrations/__init__.py +0 -0
  75. mojo/apps/logit/models/__init__.py +1 -0
  76. mojo/apps/logit/models/log.py +57 -0
  77. mojo/apps/logit/rest.py +9 -0
  78. mojo/apps/metrics/README.md +79 -0
  79. mojo/apps/metrics/__init__.py +12 -0
  80. mojo/apps/metrics/redis_metrics.py +331 -0
  81. mojo/apps/metrics/rest/__init__.py +1 -0
  82. mojo/apps/metrics/rest/base.py +152 -0
  83. mojo/apps/metrics/rest/db.py +0 -0
  84. mojo/apps/metrics/utils.py +227 -0
  85. mojo/apps/notify/README.md +91 -0
  86. mojo/apps/notify/README_NOTIFICATIONS.md +566 -0
  87. mojo/apps/notify/__init__.py +0 -0
  88. mojo/apps/notify/admin.py +52 -0
  89. mojo/apps/notify/handlers/__init__.py +0 -0
  90. mojo/apps/notify/handlers/example_handlers.py +516 -0
  91. mojo/apps/notify/handlers/ses/__init__.py +25 -0
  92. mojo/apps/notify/handlers/ses/bounce.py +0 -0
  93. mojo/apps/notify/handlers/ses/complaint.py +25 -0
  94. mojo/apps/notify/handlers/ses/message.py +86 -0
  95. mojo/apps/notify/management/__init__.py +0 -0
  96. mojo/apps/notify/management/commands/__init__.py +1 -0
  97. mojo/apps/notify/management/commands/process_notifications.py +370 -0
  98. mojo/apps/notify/mod +0 -0
  99. mojo/apps/notify/models/__init__.py +12 -0
  100. mojo/apps/notify/models/account.py +128 -0
  101. mojo/apps/notify/models/attachment.py +24 -0
  102. mojo/apps/notify/models/bounce.py +68 -0
  103. mojo/apps/notify/models/complaint.py +40 -0
  104. mojo/apps/notify/models/inbox.py +113 -0
  105. mojo/apps/notify/models/inbox_message.py +173 -0
  106. mojo/apps/notify/models/outbox.py +129 -0
  107. mojo/apps/notify/models/outbox_message.py +288 -0
  108. mojo/apps/notify/models/template.py +30 -0
  109. mojo/apps/notify/providers/__init__.py +0 -0
  110. mojo/apps/notify/providers/aws.py +73 -0
  111. mojo/apps/notify/rest/__init__.py +0 -0
  112. mojo/apps/notify/rest/ses.py +0 -0
  113. mojo/apps/notify/utils/__init__.py +2 -0
  114. mojo/apps/notify/utils/notifications.py +404 -0
  115. mojo/apps/notify/utils/parsing.py +202 -0
  116. mojo/apps/notify/utils/render.py +144 -0
  117. mojo/apps/tasks/README.md +118 -0
  118. mojo/apps/tasks/__init__.py +11 -0
  119. mojo/apps/tasks/manager.py +489 -0
  120. mojo/apps/tasks/rest/__init__.py +2 -0
  121. mojo/apps/tasks/rest/hooks.py +0 -0
  122. mojo/apps/tasks/rest/tasks.py +62 -0
  123. mojo/apps/tasks/runner.py +174 -0
  124. mojo/apps/tasks/tq_handlers.py +14 -0
  125. mojo/decorators/__init__.py +3 -0
  126. mojo/decorators/auth.py +25 -0
  127. mojo/decorators/cron.py +31 -0
  128. mojo/decorators/http.py +132 -0
  129. mojo/decorators/validate.py +14 -0
  130. mojo/errors.py +88 -0
  131. mojo/helpers/__init__.py +0 -0
  132. mojo/helpers/aws/__init__.py +0 -0
  133. mojo/helpers/aws/client.py +8 -0
  134. mojo/helpers/aws/s3.py +268 -0
  135. mojo/helpers/aws/setup_email.py +0 -0
  136. mojo/helpers/cron.py +79 -0
  137. mojo/helpers/crypto/__init__.py +4 -0
  138. mojo/helpers/crypto/aes.py +60 -0
  139. mojo/helpers/crypto/hash.py +59 -0
  140. mojo/helpers/crypto/privpub/__init__.py +1 -0
  141. mojo/helpers/crypto/privpub/hybrid.py +97 -0
  142. mojo/helpers/crypto/privpub/rsa.py +104 -0
  143. mojo/helpers/crypto/sign.py +36 -0
  144. mojo/helpers/crypto/too.l.py +25 -0
  145. mojo/helpers/crypto/utils.py +26 -0
  146. mojo/helpers/daemon.py +94 -0
  147. mojo/helpers/dates.py +69 -0
  148. mojo/helpers/dns/__init__.py +0 -0
  149. mojo/helpers/dns/godaddy.py +62 -0
  150. mojo/helpers/filetypes.py +128 -0
  151. mojo/helpers/logit.py +310 -0
  152. mojo/helpers/modules.py +95 -0
  153. mojo/helpers/paths.py +63 -0
  154. mojo/helpers/redis.py +10 -0
  155. mojo/helpers/request.py +89 -0
  156. mojo/helpers/request_parser.py +269 -0
  157. mojo/helpers/response.py +14 -0
  158. mojo/helpers/settings.py +146 -0
  159. mojo/helpers/sysinfo.py +140 -0
  160. mojo/helpers/ua.py +0 -0
  161. mojo/middleware/__init__.py +0 -0
  162. mojo/middleware/auth.py +26 -0
  163. mojo/middleware/logging.py +55 -0
  164. mojo/middleware/mojo.py +21 -0
  165. mojo/migrations/0001_initial.py +32 -0
  166. mojo/migrations/__init__.py +0 -0
  167. mojo/models/__init__.py +2 -0
  168. mojo/models/meta.py +262 -0
  169. mojo/models/rest.py +538 -0
  170. mojo/models/secrets.py +59 -0
  171. mojo/rest/__init__.py +1 -0
  172. mojo/rest/info.py +26 -0
  173. mojo/serializers/__init__.py +0 -0
  174. mojo/serializers/models.py +165 -0
  175. mojo/serializers/openapi.py +188 -0
  176. mojo/urls.py +38 -0
  177. mojo/ws4redis/README.md +174 -0
  178. mojo/ws4redis/__init__.py +2 -0
  179. mojo/ws4redis/client.py +283 -0
  180. mojo/ws4redis/connection.py +327 -0
  181. mojo/ws4redis/exceptions.py +32 -0
  182. mojo/ws4redis/redis.py +183 -0
  183. mojo/ws4redis/servers/__init__.py +0 -0
  184. mojo/ws4redis/servers/base.py +86 -0
  185. mojo/ws4redis/servers/django.py +171 -0
  186. mojo/ws4redis/servers/uwsgi.py +63 -0
  187. mojo/ws4redis/settings.py +45 -0
  188. mojo/ws4redis/utf8validator.py +128 -0
  189. mojo/ws4redis/websocket.py +403 -0
  190. testit/__init__.py +0 -0
  191. testit/client.py +147 -0
  192. testit/faker.py +20 -0
  193. testit/helpers.py +198 -0
  194. 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