ARC_Starter/backend/main.py

212 lines
7.3 KiB
Python

import subprocess
import threading
import time
import requests
import os
import pymysql
import logging
import bcrypt
import configparser
from functools import partial
CONFIG_PATH = "/app/config.ini"
#CONFIG_PATH = "../config.ini"
config = configparser.ConfigParser()
config.read(CONFIG_PATH)
loglevel_str = config["log"]["level"].upper()
loglevel = getattr(logging, loglevel_str, logging.INFO)
logging.basicConfig(level=loglevel)
DB_CONFIG = {
'host': config["database"]["host"],
'user': config["database"]["user"],
'password': config["database"]["password"],
'database': config["database"]["database"]
}
LICENSE_SERVER = "https://sikkerdata.com/validate_license.php"
LICENSE_KEY = config["license"]["license_key"]
with open("/etc/machine-id", "r") as f:
get_HWID = f.read().strip()
def validate_license():
try:
hwid = get_HWID
response = requests.post(LICENSE_SERVER, json={
"license_key": LICENSE_KEY,
"hwid": hwid
}, timeout=5)
logging.info(f"[MAIN] License server response: {response.text}")
if response.status_code == 200:
result = response.json()
if result.get("status") == "valid":
logging.info("[MAIN] License validated")
return True
else:
logging.info(f"[MAIN] License check failed: {result.get('message')}")
return False
else:
logging.info(f"[MAIN] License server error: {response.status_code}")
return False
except Exception as e:
logging.info(f"[MAIN] License check exception: {e}")
return False
def setup_database(db_config):
conn = pymysql.connect(**db_config)
cursor = conn.cursor()
def table_exists(name):
cursor.execute("SHOW TABLES LIKE %s", (name,))
return cursor.fetchone() is not None
# Create `users` table if missing
if not table_exists('users'):
print("[DB] Creating 'users' table...")
cursor.execute("""
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
full_name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
phone VARCHAR(20),
password_hash VARCHAR(255) NOT NULL,
totp_secret VARCHAR(64),
user_role ENUM('Admin','Operator','Installer','Client') DEFAULT 'Client',
access_group VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""")
# Create `signals` table if missing
if not table_exists('signals'):
print("[DB] Creating 'signals' table...")
cursor.execute("""
CREATE TABLE signals (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
protocol VARCHAR(10) NOT NULL,
raw_message TEXT NOT NULL,
account VARCHAR(20),
sequence VARCHAR(10),
line_number VARCHAR(10),
event_code VARCHAR(10),
partition VARCHAR(10),
zone VARCHAR(20),
signal_time DATETIME,
source_ip VARCHAR(45),
signal_text VARCHAR(255),
v TEXT,
x TEXT,
y TEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""")
if not table_exists('receivers'):
print("[DB] Creating 'receivers' table...")
cursor.execute("""
CREATE TABLE receivers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
type ENUM('SIA-DC09') NOT NULL DEFAULT 'SIA-DC09',
tcpport INT NOT NULL,
enabled BOOLEAN DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""")
cursor.execute("""INSERT INTO receivers (name, type, tcpport, enabled) VALUES ('Main Receiver', 'SIA-DC09', 9000, 1);""")
# If no users exist, create default admin
cursor.execute("SELECT COUNT(*) FROM users")
(user_count,) = cursor.fetchone()
if user_count == 0:
print("[DB] Inserting default admin user...")
default_password = bcrypt.hashpw("admin123".encode(), bcrypt.gensalt()).decode('utf-8')
cursor.execute("""
INSERT INTO users (username, full_name, email, password_hash, user_role)
VALUES (%s, %s, %s, %s, %s)
""", ("admin", "Admin", "admin@example.com", default_password, "Admin"))
print("[DB] Admin created with username 'admin' and password 'admin123'")
conn.commit()
cursor.close()
conn.close()
running_services = {}
def watchdog():
bin_path = "/app/watchdog.bin"
def run_watchdog():
while True:
cmd = [bin_path]
logging.debug(f"[MAIN] Starting Watchdog...")
process = subprocess.Popen(cmd)
running_services["Watchdog"] = process
process.wait()
logging.debug(f"[MAIN] Watchdog finished. Restarting in 5s...")
time.sleep(5)
thread = threading.Thread(target=run_watchdog, name="Watchdog", daemon=True)
thread.start()
def is_watchdog_running():
return any(thread.name == "Watchdog" and thread.is_alive() for thread in threading.enumerate())
def main():
logging.info("[MAIN] Starting main function")
try:
if not validate_license():
logging.info("[MAIN] Shutting down due to invalid license.")
return
setup_thread = threading.Thread(target=partial(setup_database, DB_CONFIG), daemon=True)
setup_thread.start()
setup_thread.join() # Wait for DB setup before continuing
while True:
try:
logging.debug(f"[MAIN] Main is running")
if is_watchdog_running():
logging.debug("[MAIN] Watchdog thread is already running")
else:
watchdog()
time.sleep(10)
except Exception as e:
logging.exception(f"[MAIN] Exception while logging thread status: {e}")
except KeyboardInterrupt:
logging.info("Graceful shutdown requested")
def wait_for_mariadb(host, port, user, password, database, timeout=5):
logging.info(f"[WAIT] Waiting for MariaDB at {host}:{port} to become ready...")
while True:
try:
conn = pymysql.connect(
host=host,
port=port,
user=user,
password=password,
database=database,
connect_timeout=timeout
)
with conn.cursor() as cursor:
cursor.execute("SELECT 1;")
result = cursor.fetchone()
if result:
logging.info("[WAIT] MariaDB is ready.")
conn.close()
break
except pymysql.MySQLError as e:
logging.warning(f"[WAIT] MariaDB not ready yet: {e}")
time.sleep(timeout)
if __name__ == "__main__":
wait_for_mariadb(
host=config["database"]["host"],
port=3306,
user=config["database"]["user"],
password=config["database"]["password"],
database=config["database"]["database"]
)
main()