06/01 Optimize codes

This commit is contained in:
2026-06-01 17:07:00 -04:00
parent 25b15d8432
commit 4406d9734c
7 changed files with 231 additions and 10 deletions
+34 -2
View File
@@ -322,12 +322,36 @@ def import_confirm():
ALLOWED_RECEIPT_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'pdf'}
# Magic bytes → (canonical_extension, mime_type)
_MAGIC = [
(b'\xff\xd8\xff', 'jpg', 'image/jpeg'),
(b'\x89PNG\r\n\x1a\n', 'png', 'image/png'),
(b'GIF87a', 'gif', 'image/gif'),
(b'GIF89a', 'gif', 'image/gif'),
(b'%PDF-', 'pdf', 'application/pdf'),
# WEBP: RIFF????WEBP (bytes 0-3 = RIFF, bytes 8-11 = WEBP)
]
def _allowed_receipt(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_RECEIPT_EXTENSIONS
def _check_magic(header_bytes):
"""
Inspect the first 12 bytes of a file and return (ext, mime) if recognised,
or (None, None) if the bytes don't match any allowed type.
"""
for magic, ext, mime in _MAGIC:
if header_bytes[:len(magic)] == magic:
return ext, mime
# WEBP special case: RIFF at 0, WEBP at 8
if header_bytes[:4] == b'RIFF' and header_bytes[8:12] == b'WEBP':
return 'webp', 'image/webp'
return None, None
@settings_bp.route('/receipt/upload/<int:txn_id>', methods=['POST'])
@login_required
def upload_receipt(txn_id):
@@ -350,7 +374,15 @@ def upload_receipt(txn_id):
flash('File too large. Maximum 10MB.', 'warning')
return redirect(request.referrer or url_for('transactions.index'))
ext = f.filename.rsplit('.', 1)[1].lower()
# Validate actual file content via magic bytes (prevents renamed-file attacks)
header = f.read(12)
f.seek(0)
magic_ext, magic_mime = _check_magic(header)
if magic_ext is None:
flash('File content does not match an allowed type (PNG, JPG, GIF, PDF, WEBP).', 'warning')
return redirect(request.referrer or url_for('transactions.index'))
ext = magic_ext # use the extension we detected, not what the user claimed
unique_name = f'{uuid.uuid4().hex}.{ext}'
upload_dir = current_app.config.get('UPLOAD_FOLDER', '/home/pfm/app/uploads')
os.makedirs(upload_dir, exist_ok=True)
@@ -367,7 +399,7 @@ def upload_receipt(txn_id):
filename=unique_name,
original_filename=secure_filename(f.filename),
file_size=file_size,
mime_type=f.content_type,
mime_type=magic_mime, # use validated type, not the browser-supplied header
)
db.session.add(receipt)
db.session.flush()
+20 -3
View File
@@ -226,14 +226,30 @@ def sync_confirm():
parsed.append(r)
imported, skipped = import_transactions(parsed, ta)
flash(f'Imported {imported} transaction(s). Skipped {skipped} duplicate(s).', 'success')
flash(f'Imported {imported} transaction(s) from {ta.account_name}. '
f'Skipped {skipped} duplicate(s).', 'success')
# Advance the sync queue if this came from a "Sync All" run
from flask import session as flask_session
queue = flask_session.pop('teller_sync_queue', [])
if queue:
next_id = queue[0]
flask_session['teller_sync_queue'] = queue[1:]
flash(f'{len(queue)} account(s) remaining in sync queue.', 'info')
return redirect(url_for('teller.sync_preview_view', teller_account_id=next_id))
return redirect(url_for('transactions.index'))
@teller_bp.route('/sync/all', methods=['POST'])
@login_required
def sync_all():
"""Sync all mapped accounts — redirects to first account's preview."""
"""
Queue all mapped accounts for sequential sync.
Stores the full list in session, then redirects to the first account's preview.
After each confirm the queue advances automatically.
"""
from flask import session as flask_session
enrollments = TellerEnrollment.query.filter_by(is_active=True).all()
mapped = []
for e in enrollments:
@@ -245,7 +261,8 @@ def sync_all():
flash('No accounts mapped for sync.', 'warning')
return redirect(url_for('teller.index'))
# For simplicity, sync first account; user can chain through others
# Store the full queue; first item will be previewed immediately
flask_session['teller_sync_queue'] = [ta.id for ta in mapped[1:]]
return redirect(url_for('teller.sync_preview_view', teller_account_id=mapped[0].id))
+6
View File
@@ -140,6 +140,9 @@ def new():
db.session.commit()
calc_balance(txn.account_id)
flash(f'{"Income" if txn_type == "income" else "Expense"} added.', 'success')
if txn_type == 'expense':
from app.services.alert_service import check_and_flash_budget_alerts
check_and_flash_budget_alerts(flash)
return redirect(url_for('transactions.index', tab=txn_type))
return render_template('transactions/form.html',
@@ -174,6 +177,9 @@ def edit(id):
calc_balance(old_account_id)
calc_balance(txn.account_id)
flash('Transaction updated.', 'success')
if txn.transaction_type == 'expense':
from app.services.alert_service import check_and_flash_budget_alerts
check_and_flash_budget_alerts(flash)
return redirect(url_for('transactions.index', tab=txn.transaction_type))
return render_template('transactions/form.html',