06/05 Optimize app
This commit is contained in:
@@ -253,6 +253,18 @@ def reconcile_api():
|
||||
})
|
||||
|
||||
|
||||
@dashboard_bp.route('/api/health-score')
|
||||
@login_required
|
||||
def health_score_api():
|
||||
from app.services.health_score_service import compute_health_score
|
||||
try:
|
||||
data = compute_health_score()
|
||||
except Exception as e:
|
||||
log.warning('[dashboard] health_score_api failed: %s', e)
|
||||
return jsonify({'error': str(e)}), 500
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@dashboard_bp.route('/api/anomalies')
|
||||
@login_required
|
||||
def anomalies_api():
|
||||
|
||||
+16
-1
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import uuid
|
||||
from flask import (Blueprint, render_template, redirect, url_for, flash,
|
||||
request, current_app, send_from_directory)
|
||||
request, current_app, send_from_directory, jsonify)
|
||||
from flask_login import login_required, current_user
|
||||
from app.utils.audit import audit
|
||||
from flask_wtf import FlaskForm
|
||||
@@ -208,6 +208,21 @@ def recurring():
|
||||
return render_template('settings/recurring.html', rules=rules, upcoming=upcoming)
|
||||
|
||||
|
||||
@settings_bp.route('/recurring/projection')
|
||||
@login_required
|
||||
def recurring_projection():
|
||||
days = request.args.get('days', 30, type=int)
|
||||
if days not in (30, 60, 90):
|
||||
days = 30
|
||||
from app.services.recurring_service import projected_cash_flow
|
||||
data = projected_cash_flow(days)
|
||||
# Serialize events (date objects → string)
|
||||
data['events'] = [
|
||||
{**ev, 'date': ev['date'].strftime('%b %d')} for ev in data['events']
|
||||
]
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@settings_bp.route('/recurring/new', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def recurring_new():
|
||||
|
||||
@@ -443,12 +443,21 @@ def ocr_receipt_file():
|
||||
from app.models.category import Category
|
||||
from flask import current_app
|
||||
|
||||
from app.models.receipt import Receipt
|
||||
|
||||
data = request.get_json()
|
||||
if not data or not data.get('filename'):
|
||||
return jsonify({'error': 'No filename provided'}), 400
|
||||
|
||||
# Security: only allow basenames, no path traversal
|
||||
filename = os.path.basename(data['filename'])
|
||||
|
||||
# Ownership check: filename must exist in receipts table (single-user, but
|
||||
# prevents OCR extraction from arbitrary files on disk via a crafted request)
|
||||
receipt_record = Receipt.query.filter_by(filename=filename).first()
|
||||
if not receipt_record:
|
||||
return jsonify({'error': 'Receipt not found'}), 404
|
||||
|
||||
upload_dir = current_app.config.get('UPLOAD_FOLDER', '/home/pfm/app/uploads')
|
||||
file_path = os.path.join(upload_dir, filename)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user