diff --git a/files/migrations/0001_initial.py b/files/migrations/0001_initial.py new file mode 100644 index 0000000..f20cb9a --- /dev/null +++ b/files/migrations/0001_initial.py @@ -0,0 +1,169 @@ +# Generated by Django 5.2.8 on 2026-02-10 16:00 + +import django.db.models.deletion +import files.models +import simple_history.models +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='MediaFile', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('file', models.FileField(upload_to=files.models.media_upload_path)), + ('original_filename', models.CharField(max_length=255)), + ('mime_type', models.CharField(blank=True, default='', max_length=127)), + ('file_size', models.BigIntegerField(default=0, help_text='File size in bytes')), + ('file_hash', models.CharField(blank=True, db_index=True, default='', help_text='SHA256 hash for deduplication', max_length=64)), + ('title', models.CharField(blank=True, default='', max_length=255)), + ('description', models.TextField(blank=True, default='')), + ('tags', models.JSONField(blank=True, default=list)), + ('extracted_text', models.TextField(blank=True, default='')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('is_processed', models.BooleanField(default=False, help_text='Whether OCR/transcription has been run')), + ('processing_error', models.TextField(blank=True, default='')), + ('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploaded_files', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='PublicShare', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('token', models.CharField(db_index=True, default=files.models.generate_share_token, max_length=64, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('expires_at', models.DateTimeField(blank=True, help_text='Leave empty for permanent link', null=True)), + ('max_downloads', models.PositiveIntegerField(blank=True, help_text='Leave empty for unlimited downloads', null=True)), + ('download_count', models.PositiveIntegerField(default=0)), + ('is_password_protected', models.BooleanField(default=False)), + ('password_hash', models.CharField(blank=True, default='', max_length=128)), + ('note', models.CharField(blank=True, default='', help_text='Optional note about this share', max_length=255)), + ('is_active', models.BooleanField(default=True)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_shares', to=settings.AUTH_USER_MODEL)), + ('media_file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='public_shares', to='files.mediafile')), + ], + options={ + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='FileAccessLog', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('accessed_at', models.DateTimeField(auto_now_add=True)), + ('ip_address', models.GenericIPAddressField(blank=True, null=True)), + ('user_agent', models.CharField(blank=True, default='', max_length=500)), + ('access_type', models.CharField(choices=[('download', 'Download'), ('view', 'View'), ('share_created', 'Share Created'), ('share_revoked', 'Share Revoked')], max_length=20)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ('media_file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='access_logs', to='files.mediafile')), + ('share', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='access_logs', to='files.publicshare')), + ], + options={ + 'ordering': ['-accessed_at'], + }, + ), + migrations.CreateModel( + name='SharedSpace', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('slug', models.SlugField(help_text="Subdomain identifier (e.g., 'cofi' for cofi.rfiles.online)", max_length=63, unique=True)), + ('name', models.CharField(help_text='Display name for the space', max_length=255)), + ('description', models.TextField(blank=True, default='')), + ('password_hash', models.CharField(max_length=128)), + ('is_active', models.BooleanField(default=True)), + ('max_file_size_mb', models.PositiveIntegerField(default=100, help_text='Maximum file size in MB for uploads')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_spaces', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Shared Space', + 'verbose_name_plural': 'Shared Spaces', + 'ordering': ['name'], + }, + ), + migrations.AddField( + model_name='mediafile', + name='shared_space', + field=models.ForeignKey(blank=True, help_text='Shared space this file belongs to', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='files', to='files.sharedspace'), + ), + migrations.CreateModel( + name='HistoricalMediaFile', + fields=[ + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ('file', models.TextField(max_length=100)), + ('original_filename', models.CharField(max_length=255)), + ('mime_type', models.CharField(blank=True, default='', max_length=127)), + ('file_size', models.BigIntegerField(default=0, help_text='File size in bytes')), + ('file_hash', models.CharField(blank=True, db_index=True, default='', help_text='SHA256 hash for deduplication', max_length=64)), + ('title', models.CharField(blank=True, default='', max_length=255)), + ('description', models.TextField(blank=True, default='')), + ('tags', models.JSONField(blank=True, default=list)), + ('extracted_text', models.TextField(blank=True, default='')), + ('created_at', models.DateTimeField(blank=True, editable=False)), + ('updated_at', models.DateTimeField(blank=True, editable=False)), + ('is_processed', models.BooleanField(default=False, help_text='Whether OCR/transcription has been run')), + ('processing_error', models.TextField(blank=True, default='')), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('uploaded_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ('shared_space', models.ForeignKey(blank=True, db_constraint=False, help_text='Shared space this file belongs to', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='files.sharedspace')), + ], + options={ + 'verbose_name': 'historical media file', + 'verbose_name_plural': 'historical media files', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.AddIndex( + model_name='publicshare', + index=models.Index(fields=['token'], name='files_publi_token_dd8469_idx'), + ), + migrations.AddIndex( + model_name='publicshare', + index=models.Index(fields=['expires_at'], name='files_publi_expires_35e2c5_idx'), + ), + migrations.AddIndex( + model_name='publicshare', + index=models.Index(fields=['is_active'], name='files_publi_is_acti_997938_idx'), + ), + migrations.AddIndex( + model_name='fileaccesslog', + index=models.Index(fields=['accessed_at'], name='files_filea_accesse_4e276b_idx'), + ), + migrations.AddIndex( + model_name='fileaccesslog', + index=models.Index(fields=['access_type'], name='files_filea_access__ac88fd_idx'), + ), + migrations.AddIndex( + model_name='mediafile', + index=models.Index(fields=['file_hash'], name='files_media_file_ha_328e55_idx'), + ), + migrations.AddIndex( + model_name='mediafile', + index=models.Index(fields=['mime_type'], name='files_media_mime_ty_eb2ddc_idx'), + ), + migrations.AddIndex( + model_name='mediafile', + index=models.Index(fields=['created_at'], name='files_media_created_be0465_idx'), + ), + ] diff --git a/files/migrations/__init__.py b/files/migrations/__init__.py new file mode 100644 index 0000000..e69de29