# backend/routes/cases.py import os from flask import Blueprint, request, jsonify, current_app from werkzeug.utils import secure_filename from datetime import datetime from ..db import get_db_connection, release_db_connection from ..middleware import token_required cases_blueprint = Blueprint('cases', __name__) # Helper to serialize case rows def serialize_case(row): return { "id": row[0], "platform": row[1], "violator_url": row[2], "violator_article": row[3], "my_article": row[4], "comment": row[5], "status": row[6], "created_at": row[7].isoformat() if row[7] else None, "updated_at": row[8].isoformat() if row[8] else None, } # ---------- CASE CRUD ---------- @cases_blueprint.route('/cases', methods=['GET']) @token_required def list_cases(current_user_id): # optional filters: ?article=...&status=...&platform=... article = request.args.get('article') status = request.args.get('status') platform = request.args.get('platform') conn = get_db_connection() cur = conn.cursor() query = "SELECT id, platform, violator_url, violator_article, my_article, comment, status, created_at, updated_at FROM cases" params = [current_user_id] conditions = ["user_id = %s"] if article: conditions.append("violator_article = %s") params.append(article) if status: conditions.append("status = %s") params.append(status) if platform: conditions.append("platform = %s") params.append(platform) if conditions: query += " WHERE " + " AND ".join(conditions) query += " ORDER BY id DESC" cur.execute(query, params) rows = cur.fetchall() release_db_connection(conn) return jsonify([serialize_case(r) for r in rows]) @cases_blueprint.route('/cases', methods=['POST']) @token_required def create_case(current_user_id): data = request.json required = ["platform", "violator_url", "violator_article"] for f in required: if f not in data: return jsonify({"error": f"Missing field {f}"}), 400 conn = get_db_connection() cur = conn.cursor() cur.execute( """INSERT INTO cases (user_id, platform, violator_url, violator_article, my_article, comment, status) VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING id""", ( current_user_id, data.get('platform'), data.get('violator_url'), data.get('violator_article'), data.get('my_article'), data.get('comment'), data.get('status', 'new') ) ) case_id = cur.fetchone()[0] conn.commit() release_db_connection(conn) return jsonify({"id": case_id}), 201 @cases_blueprint.route('/cases/', methods=['GET']) @token_required def get_case(current_user_id, case_id): conn = get_db_connection() cur = conn.cursor() cur.execute("SELECT id, platform, violator_url, violator_article, my_article, comment, status, created_at, updated_at FROM cases WHERE id = %s AND user_id = %s", (case_id, current_user_id)) row = cur.fetchone() release_db_connection(conn) if not row: return jsonify({"error": "Case not found"}), 404 return jsonify(serialize_case(row)) @cases_blueprint.route('/cases/', methods=['PUT']) @token_required def update_case(current_user_id, case_id): data = request.json fields = [] params = [] allowed = ["platform", "violator_url", "violator_article", "my_article", "comment", "status"] for f in allowed: if f in data: fields.append(f"{f} = %s") params.append(data[f]) if not fields: return jsonify({"error": "No updatable fields provided"}), 400 params.extend([case_id, current_user_id]) conn = get_db_connection() cur = conn.cursor() cur.execute(f"UPDATE cases SET {', '.join(fields)}, updated_at = %s WHERE id = %s AND user_id = %s", (*params[:-2], datetime.utcnow(), case_id, current_user_id)) conn.commit() release_db_connection(conn) return jsonify({"message": "Case updated"}) @cases_blueprint.route('/cases/', methods=['DELETE']) @token_required def delete_case(current_user_id, case_id): conn = get_db_connection() cur = conn.cursor() cur.execute("DELETE FROM cases WHERE id = %s AND user_id = %s", (case_id, current_user_id)) conn.commit() release_db_connection(conn) return jsonify({"message": "Case deleted"}) # ---------- FILE UPLOAD ---------- @cases_blueprint.route('/cases//files', methods=['POST']) @token_required def upload_files(current_user_id, case_id): if 'files' not in request.files: return jsonify({"error": "No files part in request"}), 400 files = request.files.getlist('files') saved_paths = [] upload_folder = current_app.config['UPLOAD_FOLDER'] conn = get_db_connection() cur = conn.cursor() # Check ownership cur.execute("SELECT id FROM cases WHERE id = %s AND user_id = %s", (case_id, current_user_id)) if not cur.fetchone(): release_db_connection(conn) return jsonify({"error": "Case not found"}), 404 for f in files: if f.filename == '': continue filename = secure_filename(f.filename) timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f') unique_name = f"{timestamp}_{filename}" file_path = os.path.join(upload_folder, unique_name) f.save(file_path) # store relative path in DB rel_path = os.path.relpath(file_path, start=os.path.dirname(os.path.abspath(__file__))) cur.execute( "INSERT INTO files (case_id, file_path) VALUES (%s, %s) RETURNING id", (case_id, rel_path) ) saved_paths.append(rel_path) conn.commit() release_db_connection(conn) return jsonify({"files": saved_paths}), 201 # Get files for a case @cases_blueprint.route('/cases//files', methods=['GET']) @token_required def list_files(current_user_id, case_id): conn = get_db_connection() cur = conn.cursor() cur.execute("SELECT f.id, f.file_path, f.uploaded_at FROM files f JOIN cases c ON f.case_id = c.id WHERE f.case_id = %s AND c.user_id = %s", (case_id, current_user_id)) rows = cur.fetchall() release_db_connection(conn) files = [{"id": r[0], "path": r[1], "uploaded_at": r[2].isoformat()} for r in rows] return jsonify(files)