import os from flask import Flask from flask_cors import CORS from dotenv import load_dotenv from flasgger import Swagger from flask_socketio import SocketIO, emit # Load environment variables from .env load_dotenv() app = Flask(__name__) CORS(app) socketio = SocketIO(app, cors_allowed_origins="*") # Swagger setup # ... (keeping existing swagger config) app.config['SWAGGER'] = { 'title': 'AutoMarket API', 'uiversion': 3, 'specs_route': '/api/docs/', 'securityDefinitions': { 'APIKeyHeader': { 'type': 'apiKey', 'name': 'Authorization', 'in': 'header', 'description': 'Введите токен в формате: Bearer <ваш_токен>' } } } swagger = Swagger(app) # Ensure upload folder exists # ... UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'uploads') os.makedirs(UPLOAD_FOLDER, exist_ok=True) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # Автоматическая инициализация таблиц при старте def init_db(): try: from .db import get_db_connection, release_db_connection conn = get_db_connection() cur = conn.cursor() cur.execute(""" CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, password_hash TEXT NOT NULL, role VARCHAR(20) DEFAULT 'admin', parent_id INT REFERENCES users(id) ON DELETE CASCADE, plan VARCHAR(20) DEFAULT 'free', created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS credentials ( user_id INT REFERENCES users(id) ON DELETE CASCADE, platform VARCHAR(50), api_key TEXT, client_id TEXT, client_secret TEXT, other_data JSONB, updated_at TIMESTAMP DEFAULT NOW(), PRIMARY KEY (user_id, platform) ); CREATE TABLE IF NOT EXISTS cases ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id) ON DELETE CASCADE, platform VARCHAR(50) NOT NULL DEFAULT 'ozon', violator_url TEXT NOT NULL, violator_article VARCHAR(50) NOT NULL, my_article VARCHAR(50), comment TEXT, status VARCHAR(20) DEFAULT 'new', created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS files ( id SERIAL PRIMARY KEY, case_id INT REFERENCES cases(id) ON DELETE CASCADE, file_path TEXT NOT NULL, uploaded_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS audit_log ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id) ON DELETE CASCADE, action VARCHAR(100) NOT NULL, details JSONB, created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS seo_keywords ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id) ON DELETE CASCADE, keyword VARCHAR(255) NOT NULL, article VARCHAR(100) NOT NULL, platform VARCHAR(50) NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS seo_history ( id SERIAL PRIMARY KEY, keyword_id INT REFERENCES seo_keywords(id) ON DELETE CASCADE, position INT, checked_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS reference_images ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id) ON DELETE CASCADE, file_path TEXT NOT NULL, image_hash VARCHAR(64), article VARCHAR(100), created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS repricer_rules ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id) ON DELETE CASCADE, product_name VARCHAR(255) NOT NULL, sku VARCHAR(100) NOT NULL, min_price DECIMAL(10,2) NOT NULL, max_price DECIMAL(10,2) NOT NULL, strategy VARCHAR(20) DEFAULT 'follow_competitor', is_active BOOLEAN DEFAULT TRUE, current_price DECIMAL(10,2), last_run TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); """) conn.commit() release_db_connection(conn) print("✅ База данных инициализирована успешно") except Exception as e: print(f"⚠️ Ошибка инициализации БД: {e}") init_db() # Register blueprints from .routes.cases import cases_blueprint from .routes.credentials import credentials_blueprint from .routes.auth import auth_blueprint from .routes.dashboard import dashboard_blueprint from .routes.reports import reports_blueprint from .routes.ai import ai_blueprint from .routes.seo import seo_blueprint from .routes.content import content_blueprint from .routes.finance import finance_blueprint from .routes.team import team_blueprint from .routes.repricer import repricer_blueprint app.register_blueprint(cases_blueprint, url_prefix='/api') app.register_blueprint(credentials_blueprint, url_prefix='/api') app.register_blueprint(auth_blueprint, url_prefix='/api') app.register_blueprint(dashboard_blueprint, url_prefix='/api') app.register_blueprint(reports_blueprint, url_prefix='/api') app.register_blueprint(ai_blueprint, url_prefix='/api') app.register_blueprint(seo_blueprint, url_prefix='/api') app.register_blueprint(content_blueprint, url_prefix='/api') app.register_blueprint(finance_blueprint, url_prefix='/api') app.register_blueprint(team_blueprint, url_prefix='/api') app.register_blueprint(repricer_blueprint, url_prefix='/api') # Health check endpoint - проверка состояния приложения и БД from flask import jsonify from .db import get_db_connection, release_db_connection @app.route('/api/health') def health_check(): db_status = "ok" db_error = None try: conn = get_db_connection() cur = conn.cursor() cur.execute("SELECT 1") release_db_connection(conn) except Exception as e: db_status = "error" db_error = str(e) status = "ok" if db_status == "ok" else "degraded" return jsonify({ "status": status, "backend": "ok", "database": db_status, "db_error": db_error }), 200 if status == "ok" else 503 import threading import time def background_scanner(): """Фоновая задача для имитации сканирования маркетплейсов.""" while True: # Здесь в будущем будет вызов api.ozon_api и т.д. print("--- [Scanner] Checking marketplaces for price dumping... ---") # Эмитируем событие для примера (что сканер что-то нашел) # socketio.emit('scanner_update', {'message': 'Сканер проверил 10 товаров...'}, namespace='/') time.sleep(60) # Start background thread scanner_thread = threading.Thread(target=background_scanner, daemon=True) scanner_thread.start() if __name__ == '__main__': # Use socketio.run instead of app.run socketio.run(app, host='0.0.0.0', port=5000, debug=True, allow_unsafe_werkzeug=True)