# Architecture ## Entry points | File | Purpose | |---|---| | [index.php](index.php) | Main router. Autoloader (lines 3-12), session init, `$route_aliases` table (lines 40-59), URL-segment fallback (lines 70-79), auth via session or persistent cookie (lines 88-102), public-path whitelist for `/api/*`, `/cron/*`, `/login` (lines 106-118). Default module: `campaigns/main_view`. | | [ajax.php](ajax.php) | AJAX handler. Same autoloader. Session regeneration on first request, IP binding check (lines 28-33). Responses: `echo json_encode(...); exit`. | | [api.php](api.php) | External API endpoints (~1350 lines). Uses RedBeanPHP. Helpers `api_json_response()` and `api_validate_api_key()`. | | [cron.php](cron.php) | Legacy cron. Calls `\Cron::tasks_emails()` and `\Cron::recursive_tasks()`. | Modern cron routes (dispatched through index.php → `\controls\Cron`): | Route | Purpose | |---|---| | `/cron/cron_universal` | Google Ads campaigns + products daily snapshot | | `/cron/cron_campaigns_product_alerts_merchant` | Product alerts from Merchant Center | | `/cron/cron_products_urls` | Bulk fetch product URLs | | `/cron/cron_facebook_ads` | Facebook Ads sync (30-day window) | | `/cron/cron_xml_feed_import` | Import supplemental/product XML feeds | ## Routing `index.php` builds a request URL into `$_GET['module']` and `$_GET['action']`: 1. Apply `$route_aliases` map (e.g. `/login` → `users/login_form`). 2. Fallback: `/$seg0/$seg1` → `module=$seg0`, `action=$seg1`. 3. Default: `campaigns/main_view`. 4. `\controls\Site::route()` instantiates `\controls\{Module}` and calls action method. ## Layers ### Controllers — `autoload/controls/` (namespace `\controls`) Static action methods. Pattern: ```php $id = (int) \S::get('client_id'); $rows = \factory\Campaigns::get_campaigns_list($id); echo json_encode($rows); exit; // AJAX return \Tpl::view('campaigns/main_view', ['clients' => $rows]); // page ``` Representative files: [autoload/controls/class.Campaigns.php](autoload/controls/class.Campaigns.php), [autoload/controls/class.Products.php](autoload/controls/class.Products.php), [autoload/controls/class.Cron.php](autoload/controls/class.Cron.php) (~5,200 lines — see [concerns.md](concerns.md)). ### Factories — `autoload/factory/` (namespace `\factory`) Static methods wrapping `$mdb` queries. Examples: - [autoload/factory/class.Campaigns.php](autoload/factory/class.Campaigns.php) (~400 lines) - [autoload/factory/class.Products.php](autoload/factory/class.Products.php) (~1,540 lines) - [autoload/factory/class.Logs.php](autoload/factory/class.Logs.php) ### Services — `autoload/services/` (namespace `\services`) External API integrations (see [integrations.md](integrations.md)). ### Views — `autoload/view/` (namespace `\view`) Thin orchestrators. [autoload/view/class.Site.php](autoload/view/class.Site.php) wraps controller output in `site/layout-logged.php`, injecting `campaign_alerts_count`, `user`, `current_module`, flash alerts. ### Templates — `templates/{module}/` Rendered via `\Tpl::view('module/file', $data)`. Lookup order: `templates_user/` (override) → `templates/`. Data accessed in templates as `$this->varName` (magic `__get`). Output captured with `ob_start/ob_get_clean`. ## Modules | Module | Controller | Purpose | |---|---|---| | campaigns | `\controls\Campaigns` | Google Ads campaign list, history, charts, alerts | | products | `\controls\Products` | Merchant Center product feed, AI title/desc suggestions | | clients | `\controls\Clients` | Merchant accounts (Google Ads ID, Merchant ID, settings) | | users | `\controls\Users` | Auth, settings, API key, cron status dashboard | | feeds | `\controls\Feeds` | Supplemental TSV feed generation | | logs | `\controls\Logs` | System event log viewer | | campaign_alerts | `\controls\CampaignAlerts` | AI-detected campaign issues | | campaign_terms | `\controls\CampaignTerms` | Search term aggregation, keyword suggestions | | facebook_ads | `\controls\FacebookAds` | Facebook Ads sync and tracking | | allegro | `\controls\Allegro` | Allegro.pl marketplace integration (legacy) | | cron | `\controls\Cron` | Cron execution dispatcher and status UI | | site | layout only | `layout-logged.php`, `layout-unlogged.php` | | html | components | Reusable form elements (input, textarea, select, etc.) | ## Database schema (selected) Migrations in [migrations/](migrations/) (30+ files, e.g. `001_google_ads_settings.sql`). Tracked in `schema_migrations`. Run via `php install.php` (`--force`, `--with_demo`). Key tables: - `settings` — global key-value config store - `clients` — merchant accounts (Google Ads Customer ID, Merchant ID) - `campaigns`, `campaigns_history` — campaign metadata + daily snapshots - `cron_sync_status` — pipeline phase tracking (pending → fetch → aggregate_30 → done) - `campaign_alerts`, `campaign_search_terms_history`, `campaign_ad_groups`, `campaign_keywords`, `campaign_negative_keywords` - `products`, `products_aggregate`, `products_keyword_planner_terms`, `products_merchant_sync_log` - `logs` — structured event log (level/source/message/context JSON/client_id) - `facebook_campaigns`, `facebook_campaigns_history`, `facebook_ad_sets`, `facebook_ads`, `facebook_ads_history` ## Request flow **Page (HTML):** request → `.htaccess` → `index.php` → routing → auth → `\view\Site::show()` → `\controls\Site::route()` → controller action → factory → `\Tpl::view(...)` → wrapped in `site/layout-logged.php` → response. **AJAX (JSON):** POST/GET → `ajax.php` → session+IP check → controller action → `echo json_encode(...); exit`. **Cron (JSON):** external GET → `index.php` (whitelisted) → `\controls\Cron::cron_universal()` → service API call → write to `*_history` + `cron_sync_status` → `self::output_cron_response([...])`. ## Auth Session-based, with persistent cookie. Cookie stores JSON `{email, hash}` (salted). On revisit, `index.php` lines 92-102 verify and rehydrate session. AJAX adds IP binding (`$_SESSION['ip']` vs `$_SERVER['REMOTE_ADDR']`); mismatch → `session_destroy()`.