Your IP : 216.73.217.78


Current Path : /home/seto/888mirror.pm/includes/
Upload File :
Current File : /home/seto/888mirror.pm/includes/db.php

<?php
require_once __DIR__ . '/../config.php';

function db(): PDO {
    static $pdo = null;
    if ($pdo === null) {
        $pdo = new PDO('sqlite:' . DB_PATH);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        $pdo->exec('PRAGMA journal_mode=WAL');
        $pdo->exec('PRAGMA foreign_keys=ON');
        migrate($pdo);
    }
    return $pdo;
}

function migrate(PDO $pdo): void {
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS users (
            id            INTEGER PRIMARY KEY AUTOINCREMENT,
            login         TEXT NOT NULL UNIQUE,
            password_hash TEXT NOT NULL,
            role          TEXT NOT NULL DEFAULT 'admin'
        );

        CREATE TABLE IF NOT EXISTS languages (
            id        INTEGER PRIMARY KEY AUTOINCREMENT,
            code      TEXT NOT NULL UNIQUE,
            name      TEXT NOT NULL,
            is_active INTEGER DEFAULT 1
        );

        CREATE TABLE IF NOT EXISTS brands (
            id           INTEGER PRIMARY KEY AUTOINCREMENT,
            name         TEXT NOT NULL,
            slug         TEXT NOT NULL UNIQUE,
            logo         TEXT,
            tracking_url TEXT NOT NULL,
            weight       INTEGER DEFAULT 0,
            is_featured  INTEGER DEFAULT 0,
            badge        TEXT,
            year_founded INTEGER,
            status       TEXT DEFAULT 'active',
            created_at   TEXT DEFAULT (datetime('now'))
        );

        CREATE TABLE IF NOT EXISTS brand_countries (
            brand_id     INTEGER NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
            country_code TEXT NOT NULL,
            PRIMARY KEY (brand_id, country_code)
        );

        CREATE TABLE IF NOT EXISTS brand_translations (
            id                    INTEGER PRIMARY KEY AUTOINCREMENT,
            brand_id              INTEGER NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
            language_code         TEXT NOT NULL,
            bonus                 TEXT,
            free_spins            TEXT,
            games_count           TEXT,
            advantages            TEXT,
            license               TEXT,
            payment_methods       TEXT,
            payment_methods_count TEXT,
            min_withdraw          TEXT,
            max_withdraw          TEXT,
            currencies            TEXT,
            generated_at          TEXT,
            UNIQUE (brand_id, language_code)
        );

        CREATE TABLE IF NOT EXISTS stats (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            brand_id   INTEGER NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
            country    TEXT DEFAULT '-',
            language   TEXT DEFAULT '-',
            event_type TEXT NOT NULL,
            created_at TEXT DEFAULT (datetime('now'))
        );

        CREATE TABLE IF NOT EXISTS settings (
            key   TEXT PRIMARY KEY,
            value TEXT
        );

        CREATE TABLE IF NOT EXISTS login_attempts (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            ip         TEXT NOT NULL,
            created_at TEXT DEFAULT (datetime('now'))
        );

        CREATE INDEX IF NOT EXISTS idx_stats_brand   ON stats(brand_id);
        CREATE INDEX IF NOT EXISTS idx_stats_date    ON stats(created_at);
        CREATE INDEX IF NOT EXISTS idx_stats_country ON stats(country);
        CREATE INDEX IF NOT EXISTS idx_brand_status  ON brands(status);
        CREATE INDEX IF NOT EXISTS idx_brand_slug    ON brands(slug);
    ");

    // Таблица лендингов
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS landings (
            id               INTEGER PRIMARY KEY AUTOINCREMENT,
            slug             TEXT NOT NULL UNIQUE,
            name             TEXT NOT NULL,
            status           TEXT NOT NULL DEFAULT 'draft',
            template         TEXT NOT NULL DEFAULT 'dark-premium',
            geo              TEXT DEFAULT '',
            brand_limit      INTEGER DEFAULT 0,
            h1               TEXT DEFAULT '',
            subtitle         TEXT DEFAULT '',
            cta_text         TEXT DEFAULT 'Get Bonus →',
            body_text        TEXT DEFAULT '',
            footer_text      TEXT DEFAULT '',
            meta_title       TEXT DEFAULT '',
            meta_description TEXT DEFAULT '',
            created_at       TEXT DEFAULT (datetime('now')),
            updated_at       TEXT DEFAULT (datetime('now'))
        )
    ");

    // Таблица переводов лендингов
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS landing_translations (
            id               INTEGER PRIMARY KEY AUTOINCREMENT,
            landing_id       INTEGER NOT NULL REFERENCES landings(id) ON DELETE CASCADE,
            language_code    TEXT NOT NULL,
            h1               TEXT DEFAULT '',
            subtitle         TEXT DEFAULT '',
            cta_text         TEXT DEFAULT '',
            body_text        TEXT DEFAULT '',
            meta_title       TEXT DEFAULT '',
            meta_description TEXT DEFAULT '',
            footer_text      TEXT DEFAULT '',
            generated_at     TEXT,
            UNIQUE (landing_id, language_code)
        )
    ");

    // landing_id в stats
    try { $pdo->exec("ALTER TABLE stats ADD COLUMN landing_id INTEGER DEFAULT NULL"); } catch (Exception $e) {}
    // subid для трекинга постбеков
    try { $pdo->exec("ALTER TABLE stats ADD COLUMN subid TEXT DEFAULT NULL"); } catch (Exception $e) {}
    // visit_id — уникальный ID загрузки страницы (один на все бренды одного page load)
    try { $pdo->exec("ALTER TABLE stats ADD COLUMN visit_id TEXT DEFAULT NULL"); } catch (Exception $e) {}
    // referrer — домен источника перехода
    try { $pdo->exec("ALTER TABLE stats ADD COLUMN referrer TEXT DEFAULT NULL"); } catch (Exception $e) {}
    // ip — для дедупликации кликов и показов
    try { $pdo->exec("ALTER TABLE stats ADD COLUMN ip TEXT DEFAULT NULL"); } catch (Exception $e) {}
    // постбек включён для бренда
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN postback_enabled INTEGER DEFAULT 0"); } catch (Exception $e) {}

    // Таблица конверсий (постбеки)
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS conversions (
            id          INTEGER PRIMARY KEY AUTOINCREMENT,
            subid       TEXT NOT NULL DEFAULT '',
            brand_id    INTEGER,
            event_type  TEXT NOT NULL DEFAULT 'fd',
            payout      REAL DEFAULT 0,
            ms_id       TEXT DEFAULT '',
            customer_id TEXT DEFAULT '',
            dynamic     TEXT DEFAULT '',
            dynamic2    TEXT DEFAULT '',
            tid         TEXT DEFAULT '',
            country     TEXT DEFAULT '',
            dep_sum     TEXT DEFAULT '',
            raw_params  TEXT DEFAULT '',
            created_at  TEXT DEFAULT (datetime('now'))
        );
        CREATE INDEX IF NOT EXISTS idx_conv_subid  ON conversions(subid);
        CREATE INDEX IF NOT EXISTS idx_conv_brand  ON conversions(brand_id);
        CREATE INDEX IF NOT EXISTS idx_conv_date   ON conversions(created_at);
    ");

    // Таблица бот-запросов (устаревшая, оставлена для совместимости)
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS bot_impressions (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            ip         TEXT DEFAULT '',
            user_agent TEXT DEFAULT '',
            path       TEXT DEFAULT '',
            country    TEXT DEFAULT '-',
            created_at TEXT DEFAULT (datetime('now'))
        );
        CREATE INDEX IF NOT EXISTS idx_bot_date ON bot_impressions(created_at);
    ");

    // Единый лог доступов (реальные пользователи + боты)
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS access_log (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            type       TEXT NOT NULL DEFAULT 'real',
            ip         TEXT DEFAULT '',
            country    TEXT DEFAULT '-',
            language   TEXT DEFAULT '-',
            user_agent TEXT DEFAULT '',
            path       TEXT DEFAULT '/',
            created_at TEXT DEFAULT (datetime('now'))
        );
        CREATE INDEX IF NOT EXISTS idx_alog_date    ON access_log(created_at);
        CREATE INDEX IF NOT EXISTS idx_alog_type    ON access_log(type);
        CREATE INDEX IF NOT EXISTS idx_alog_country ON access_log(country);
    ");

    // Таблица партнёрок (ПП)
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS partners (
            id               INTEGER PRIMARY KEY AUTOINCREMENT,
            name             TEXT NOT NULL,
            type             TEXT NOT NULL DEFAULT 'direct',
            contact_name     TEXT,
            contact_telegram TEXT,
            affiliate_url    TEXT,
            notes            TEXT,
            created_at       TEXT DEFAULT (datetime('now'))
        )
    ");

    // Связь бренда с ПП
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN partner_id INTEGER DEFAULT NULL"); } catch (Exception $e) {}

    // Сид: создать ПП из брендов с контактами (если ПП ещё нет)
    $pp_count = (int)$pdo->query("SELECT COUNT(*) FROM partners")->fetchColumn();
    if ($pp_count === 0) {
        $seed_brands = $pdo->query("
            SELECT id, contact_name, contact_telegram, affiliate_url
            FROM brands
            WHERE contact_name IS NOT NULL AND contact_name != ''
               OR contact_telegram IS NOT NULL AND contact_telegram != ''
               OR affiliate_url IS NOT NULL AND affiliate_url != ''
        ")->fetchAll(PDO::FETCH_ASSOC);
        $n = 1;
        foreach ($seed_brands as $sb) {
            $pdo->prepare("INSERT INTO partners (name, type, contact_name, contact_telegram, affiliate_url) VALUES (?,?,?,?,?)")
                ->execute(['ПП ' . $n, 'direct', $sb['contact_name'], $sb['contact_telegram'], $sb['affiliate_url']]);
            $pp_id = $pdo->lastInsertId();
            $pdo->prepare("UPDATE brands SET partner_id = ? WHERE id = ?")->execute([$pp_id, $sb['id']]);
            $n++;
        }
    }

    // Партнёрские поля бренда
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN contact_name TEXT DEFAULT NULL"); } catch (Exception $e) {}
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN contact_telegram TEXT DEFAULT NULL"); } catch (Exception $e) {}
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN affiliate_url TEXT DEFAULT NULL"); } catch (Exception $e) {}
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN payment_models TEXT DEFAULT NULL"); } catch (Exception $e) {}
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN rate_cpa REAL DEFAULT NULL"); } catch (Exception $e) {}
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN rate_revshare REAL DEFAULT NULL"); } catch (Exception $e) {}
    try { $pdo->exec("ALTER TABLE brands ADD COLUMN logo_bg TEXT DEFAULT NULL"); } catch (Exception $e) {}

    // Добавить колонку group_name если ещё нет
    try { $pdo->exec("ALTER TABLE languages ADD COLUMN group_name TEXT DEFAULT 'Core Markets'"); } catch (Exception $e) {}

    // Сид: дефолтные языки
    $count = $pdo->query("SELECT COUNT(*) FROM languages")->fetchColumn();
    if ($count == 0) {
        $langs = [
            ['en', 'English'], ['ru', 'Русский'], ['fr', 'Français'],
            ['de', 'Deutsch'], ['es', 'Español'], ['it', 'Italiano'],
            ['pt', 'Português'], ['pl', 'Polski'], ['tr', 'Türkçe'],
            ['uk', 'Українська'], ['kk', 'Қазақша'], ['ro', 'Română'],
            ['hu', 'Magyar'], ['nl', 'Nederlands'], ['cs', 'Čeština'],
        ];
        $stmt = $pdo->prepare("INSERT INTO languages (code, name) VALUES (?, ?)");
        foreach ($langs as [$code, $name]) {
            $stmt->execute([$code, $name]);
        }
    }

    // Установить group_name для существующих языков (идемпотентно)
    $pdo->exec("UPDATE languages SET group_name='Core Markets'  WHERE code IN ('en','es','fr','pt','ru','de','tr','it','pl','uk','nl','ro','hu','cs')");
    $pdo->exec("UPDATE languages SET group_name='Caucasus & CIS' WHERE code='kk'");

    // Добавить новые языки (INSERT OR IGNORE = идемпотентно)
    $new_langs = [
        // Nordics (активны)
        ['sv','Svenska',          'Nordics',        1],
        ['no','Norsk',            'Nordics',        1],
        ['da','Dansk',            'Nordics',        1],
        ['fi','Suomi',            'Nordics',        1],
        ['is','Íslenska',         'Nordics',        1],
        // Balkans & SEE (неактивны)
        ['el','Ελληνικά',         'Balkans & SEE',  0],
        ['bg','Български',        'Balkans & SEE',  0],
        ['sr','Srpski',           'Balkans & SEE',  0],
        ['hr','Hrvatski',         'Balkans & SEE',  0],
        ['sq','Shqip',            'Balkans & SEE',  0],
        ['sl','Slovenščina',      'Balkans & SEE',  0],
        ['mk','Македонски',       'Balkans & SEE',  0],
        ['bs','Bosanski',         'Balkans & SEE',  0],
        ['me','Crnogorski',       'Balkans & SEE',  0],
        // Baltics (активны)
        ['lt','Lietuvių',         'Baltics',        1],
        ['lv','Latviešu',         'Baltics',        1],
        ['et','Eesti',            'Baltics',        1],
        // Caucasus & CIS (неактивны)
        ['az','Azərbaycan dili',  'Caucasus & CIS', 0],
        ['hy','Հայերեն',          'Caucasus & CIS', 0],
        ['ka','ქართული',          'Caucasus & CIS', 0],
        // Asia Growth (неактивны)
        ['zh','中文',              'Asia Growth',    0],
        ['id','Bahasa Indonesia',  'Asia Growth',    0],
    ];
    $stmt_nl = $pdo->prepare("INSERT OR IGNORE INTO languages (code, name, group_name, is_active) VALUES (?,?,?,?)");
    foreach ($new_langs as $l) {
        $stmt_nl->execute($l);
    }

    // Сид: дефолтные настройки
    $pdo->exec("
        INSERT OR IGNORE INTO settings (key, value) VALUES ('openai_key', '');
        INSERT OR IGNORE INTO settings (key, value) VALUES ('openai_model', 'gpt-4o');
        INSERT OR IGNORE INTO settings (key, value) VALUES ('site_name', '888mirror.pm');
    ");

    // Миграция: добавить role если отсутствует
    try { $pdo->exec("ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'admin'"); } catch (\Exception $e) {}

    // Сид: дефолтный admin (логин: admin, пароль: admin123 — сменить сразу!)
    $exists = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
    if ($exists == 0) {
        $hash = password_hash('admin123', PASSWORD_DEFAULT);
        $pdo->prepare("INSERT INTO users (login, password_hash, role) VALUES ('admin', ?, 'admin')")->execute([$hash]);
    }

    // Сид: пользователь yauheni (роль: limited, пароль: changeme — сменить!)
    $yExists = $pdo->prepare("SELECT COUNT(*) FROM users WHERE login = 'yauheni'");
    $yExists->execute();
    if ((int)$yExists->fetchColumn() === 0) {
        $hash = password_hash('changeme', PASSWORD_DEFAULT);
        $pdo->prepare("INSERT INTO users (login, password_hash, role) VALUES ('yauheni', ?, 'limited')")->execute([$hash]);
    }

    // Widget stats (отдельная таблица для аналитики виджета)
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS widget_stats (
            id            INTEGER PRIMARY KEY AUTOINCREMENT,
            brand_id      INTEGER NOT NULL,
            country       TEXT NOT NULL DEFAULT '-',
            language      TEXT NOT NULL DEFAULT '-',
            event_type    TEXT NOT NULL,
            visit_id      TEXT,
            ref_host      TEXT DEFAULT '',
            widget_layout TEXT DEFAULT '',
            created_at    TEXT DEFAULT (datetime('now'))
        )
    ");

    // Widget settings
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS widget_settings (
            key   TEXT PRIMARY KEY,
            value TEXT NOT NULL DEFAULT ''
        )
    ");
    $pdo->exec("INSERT OR IGNORE INTO widget_settings (key, value) VALUES ('default_layout', 'table')");
    $pdo->exec("INSERT OR IGNORE INTO widget_settings (key, value) VALUES ('brand_limit', '0')");

    // Redirect system
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS redirect_clicks (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            brand_id   INTEGER NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
            country    TEXT NOT NULL DEFAULT '-',
            source     TEXT NOT NULL DEFAULT '',
            subid      TEXT NOT NULL DEFAULT '',
            ip         TEXT NOT NULL DEFAULT '',
            created_at TEXT DEFAULT (datetime('now'))
        );
        CREATE TABLE IF NOT EXISTS redirect_settings (
            country_code TEXT PRIMARY KEY,
            mode         TEXT NOT NULL DEFAULT 'random'
        );
    ");
    $pdo->exec("INSERT OR IGNORE INTO settings (key, value) VALUES ('redirect_mode_default', 'random')");
}

function setting(string $key): string {
    $row = db()->prepare("SELECT value FROM settings WHERE key = ?");
    $row->execute([$key]);
    return (string)($row->fetchColumn() ?? '');
}

function setting_set(string $key, string $value): void {
    db()->prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)")->execute([$key, $value]);
}