Allow uploading of files and folders.

This commit is contained in:
2025-07-28 11:07:40 +02:00
committed by Jens Luedicke
parent 87471e033e
commit f98e8f3e71
13 changed files with 2805 additions and 8 deletions

92
models/note_attachment.py Normal file
View File

@@ -0,0 +1,92 @@
"""
Note attachment model for storing uploaded files associated with notes.
"""
from datetime import datetime
from . import db
class NoteAttachment(db.Model):
"""Model for files attached to notes (images, documents, etc.)"""
__tablename__ = 'note_attachment'
id = db.Column(db.Integer, primary_key=True)
note_id = db.Column(db.Integer, db.ForeignKey('note.id'), nullable=False)
# File information
filename = db.Column(db.String(255), nullable=False) # Stored filename
original_filename = db.Column(db.String(255), nullable=False) # Original upload name
file_path = db.Column(db.String(500), nullable=False) # Relative path from uploads dir
file_size = db.Column(db.Integer) # Size in bytes
mime_type = db.Column(db.String(100))
# File type for easier filtering
file_type = db.Column(db.String(20)) # 'image', 'document', 'archive', etc.
# Metadata
uploaded_at = db.Column(db.DateTime, default=datetime.now)
uploaded_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
# For images: dimensions
image_width = db.Column(db.Integer)
image_height = db.Column(db.Integer)
# Soft delete
is_deleted = db.Column(db.Boolean, default=False)
deleted_at = db.Column(db.DateTime)
# Relationships
note = db.relationship('Note', backref='attachments')
uploaded_by = db.relationship('User', backref='uploaded_attachments')
def __repr__(self):
return f'<NoteAttachment {self.original_filename} for Note {self.note_id}>'
@property
def is_image(self):
"""Check if attachment is an image"""
return self.file_type == 'image'
@property
def url(self):
"""Get the URL to access this attachment"""
return f'/uploads/notes/{self.file_path}'
def get_file_extension(self):
"""Get file extension"""
return self.original_filename.rsplit('.', 1)[1].lower() if '.' in self.original_filename else ''
@staticmethod
def allowed_file(filename, file_type='any'):
"""Check if file extension is allowed"""
ALLOWED_EXTENSIONS = {
'image': {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'},
'document': {'pdf', 'doc', 'docx', 'txt', 'md', 'csv', 'xls', 'xlsx'},
'archive': {'zip', 'tar', 'gz', '7z'},
'any': {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'pdf', 'doc',
'docx', 'txt', 'md', 'csv', 'xls', 'xlsx', 'zip', 'tar', 'gz', '7z'}
}
if '.' not in filename:
return False
ext = filename.rsplit('.', 1)[1].lower()
allowed = ALLOWED_EXTENSIONS.get(file_type, ALLOWED_EXTENSIONS['any'])
return ext in allowed
@staticmethod
def get_file_type(filename):
"""Determine file type from extension"""
if '.' not in filename:
return 'unknown'
ext = filename.rsplit('.', 1)[1].lower()
if ext in {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'}:
return 'image'
elif ext in {'pdf', 'doc', 'docx', 'txt', 'md', 'csv', 'xls', 'xlsx'}:
return 'document'
elif ext in {'zip', 'tar', 'gz', '7z'}:
return 'archive'
else:
return 'other'