import os import sys import json import uuid import time import threading from flask import Flask, redirect, url_for, send_file, jsonify from flask_login import login_required, current_user from werkzeug.security import generate_password_hash import webview from flask_socketio import SocketIO from core.utils import get_bundled_path, get_persistent_path from core.db import init_db as init_db_core, get_db_connection from core.events import socketio from core.sync import SyncManager from blueprints.auth import auth_bp, init_login_manager from blueprints.finance import finance_bp from blueprints.inventory import inventory_bp from blueprints.pos import pos_bp from blueprints.sales import sales_bp from blueprints.sync_server import sync_bp # --- PYINSTALLER WINDOWED MODE FIX --- if getattr(sys, 'frozen', False) and sys.platform == "win32": log_file = os.path.join(os.path.dirname(sys.executable), 'seki_crash.log') sys.stdout = open(log_file, 'w', encoding='utf-8') sys.stderr = sys.stdout # --- FLASK INIT --- app = Flask( __name__, template_folder=get_bundled_path('templates'), static_folder=get_bundled_path('static') ) app.config['SECRET_KEY'] = 'seki_super_secret_key_99' # --- PORT --- PORT = int(os.environ.get('PORT', 5000)) # --- MODE --- SEKIPOS_MODE = os.environ.get('SEKIPOS_MODE', 'desktop') # --- SYNC SECRET --- SYNC_SECRET = os.environ.get('SEKIPOS_SYNC_SECRET', '').strip() if SEKIPOS_MODE == 'server' and not SYNC_SECRET: print("[WARN] SEKIpos_SYNC_SECRET not set — sync API is unprotected!") # --- DIRECTORY SETUP --- DB_DIR = os.environ.get('SEKIPOS_DB_DIR', get_persistent_path('db')) os.makedirs(DB_DIR, exist_ok=True) DB_FILE = os.path.join(DB_DIR, "pos_database.db") app.config['DB_FILE'] = DB_FILE CACHE_DIR = get_persistent_path(os.path.join('static', 'cache')) os.makedirs(CACHE_DIR, exist_ok=True) app.config['CACHE_DIR'] = CACHE_DIR # --- INSTANCE CONFIG --- INSTANCE_FILE = os.path.join(DB_DIR, 'instance.json') if not os.path.exists(INSTANCE_FILE): config = { "instance_id": str(uuid.uuid4()), "display_name": "Caja Principal", "mode": SEKIPOS_MODE, "server_url": "", "last_sync_at": None } with open(INSTANCE_FILE, 'w') as f: json.dump(config, f, indent=2) else: with open(INSTANCE_FILE) as f: config = json.load(f) config['mode'] = SEKIPOS_MODE with open(INSTANCE_FILE, 'w') as f: json.dump(config, f, indent=2) INSTANCE_ID = config['instance_id'] DISPLAY_NAME = config.get('display_name', 'Caja Principal') SERVER_URL = config.get('server_url', '') app.config['INSTANCE_ID'] = INSTANCE_ID app.config['DISPLAY_NAME'] = DISPLAY_NAME app.config['SERVER_URL'] = SERVER_URL app.config['MODE'] = SEKIPOS_MODE app.config['SYNC_SECRET'] = SYNC_SECRET # --- BLUEPRINT REGISTRATION --- app.register_blueprint(auth_bp) app.register_blueprint(finance_bp) app.register_blueprint(inventory_bp) app.register_blueprint(pos_bp) app.register_blueprint(sales_bp) if SEKIPOS_MODE == 'server': app.register_blueprint(sync_bp) init_login_manager(app) socketio.init_app(app, cors_allowed_origins="*", async_mode='threading') # --- DATABASE INITIALIZATION --- init_db_core(DB_FILE) # --- ROOT ROUTE --- @app.route('/') @login_required def index(): return redirect(url_for('inventory.inventory')) # --- SYNC CLIENT --- if SEKIPOS_MODE == 'desktop' and SERVER_URL: sync_secret = config.get('sync_secret', '') sync_mgr = SyncManager(DB_FILE, INSTANCE_ID, SERVER_URL, DISPLAY_NAME, sync_secret) sync_mgr.start() print(f"[Sync] Desktop mode — syncing to {SERVER_URL}") # --- RUN FUNCTION --- def start_server(): socketio.run(app, host='127.0.0.1', port=PORT, log_output=False, allow_unsafe_werkzeug=True) def run_standalone(): t = threading.Thread(target=start_server) t.daemon = True t.start() time.sleep(2) webview.create_window('SekiPOS', f'http://127.0.0.1:{PORT}', width=1366, height=768, resizable=True, fullscreen=False, min_size=(800, 600), maximized=True) webview.start(private_mode=False) if __name__ == '__main__': #run_standalone() # Uncomment for desktop app socketio.run(app, host='0.0.0.0', port=PORT, debug=True, allow_unsafe_werkzeug=True)