feat: initialize full-stack architecture with backend routes, database schema, and glassmorphic frontend UI
This commit is contained in:
76
backend/routes/content.py
Normal file
76
backend/routes/content.py
Normal file
@@ -0,0 +1,76 @@
|
||||
import os
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from werkzeug.utils import secure_filename
|
||||
from ..db import get_db_connection, release_db_connection
|
||||
from ..middleware import token_required
|
||||
from ..utils.image_utils import calculate_image_hash
|
||||
|
||||
content_blueprint = Blueprint('content', __name__)
|
||||
|
||||
@content_blueprint.route('/content/reference', methods=['POST'])
|
||||
@token_required
|
||||
def upload_reference(current_user_id):
|
||||
if 'file' not in request.files:
|
||||
return jsonify({"error": "No file"}), 400
|
||||
|
||||
file = request.files['file']
|
||||
article = request.form.get('article', 'unknown')
|
||||
|
||||
filename = secure_filename(file.filename)
|
||||
save_path = os.path.join(current_app.config['UPLOAD_FOLDER'], f"ref_{current_user_id}_{filename}")
|
||||
file.save(save_path)
|
||||
|
||||
# Calculate Hash
|
||||
img_hash = calculate_image_hash(save_path)
|
||||
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"INSERT INTO reference_images (user_id, file_path, image_hash, article) VALUES (%s, %s, %s, %s) RETURNING id",
|
||||
(current_user_id, save_path, img_hash, article)
|
||||
)
|
||||
ref_id = cur.fetchone()[0]
|
||||
conn.commit()
|
||||
release_db_connection(conn)
|
||||
|
||||
return jsonify({"id": ref_id, "hash": img_hash}), 201
|
||||
|
||||
@content_blueprint.route('/content/reference', methods=['GET'])
|
||||
@token_required
|
||||
def list_references(current_user_id):
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT id, file_path, image_hash, article, created_at FROM reference_images WHERE user_id = %s", (current_user_id,))
|
||||
rows = cur.fetchall()
|
||||
release_db_connection(conn)
|
||||
|
||||
return jsonify([{
|
||||
"id": r[0],
|
||||
"filename": os.path.basename(r[1]),
|
||||
"hash": r[2],
|
||||
"article": r[3],
|
||||
"date": r[4].isoformat()
|
||||
} for r in rows])
|
||||
|
||||
@content_blueprint.route('/content/detect-clones/<int:ref_id>', methods=['GET'])
|
||||
@token_required
|
||||
def detect_clones(current_user_id, ref_id):
|
||||
# В реальном приложении здесь запускается поиск по маркетплейсам
|
||||
# Для демо имитируем нахождение нескольких нарушителей
|
||||
clones = [
|
||||
{
|
||||
"platform": "ozon",
|
||||
"url": "https://ozon.ru/product/123456",
|
||||
"article": "OZ-9988",
|
||||
"similarity": "98%",
|
||||
"image_url": "https://ir.ozone.ru/s3/multimedia-1/654321.jpg"
|
||||
},
|
||||
{
|
||||
"platform": "wildberries",
|
||||
"url": "https://wildberries.ru/catalog/778899/detail.aspx",
|
||||
"article": "WB-CLONE-1",
|
||||
"similarity": "92%",
|
||||
"image_url": "https://basket-01.wb.ru/vol77/part778/778899/images/big/1.jpg"
|
||||
}
|
||||
]
|
||||
return jsonify(clones)
|
||||
Reference in New Issue
Block a user