172 lines
7.2 KiB
Python
172 lines
7.2 KiB
Python
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)
|