update
This commit is contained in:
109
.paul/codebase/architecture.md
Normal file
109
.paul/codebase/architecture.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 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()`.
|
||||
146
.paul/codebase/concerns.md
Normal file
146
.paul/codebase/concerns.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Areas of Concern
|
||||
|
||||
Prioritized: **HIGH** = security/data risk, **MEDIUM** = significant tech debt, **LOW** = polish/quality.
|
||||
|
||||
---
|
||||
|
||||
## Security — HIGH
|
||||
|
||||
### Hardcoded credentials in [config.php](config.php)
|
||||
|
||||
DB password, email password, and remote DB host stored in plaintext. If the repo leaks (or is on a shared dev machine), full DB compromise is immediate.
|
||||
|
||||
**Fix:** move to `.env` + `getenv()`; add `config.php` to `.gitignore`; rotate the leaked secret.
|
||||
|
||||
### Unsafe `unserialize()`
|
||||
|
||||
PHP object-injection surface:
|
||||
|
||||
- [autoload/class.Cache.php](autoload/class.Cache.php) line 29 — `@unserialize($data)` (cache files in `temp/`)
|
||||
- [libraries/grid/grid.php](libraries/grid/grid.php) lines 95, 122
|
||||
- [libraries/medoo/medoo.php](libraries/medoo/medoo.php) line 1264
|
||||
|
||||
**Fix:** switch to `json_encode/decode`, or pass the `['allowed_classes' => false]` option.
|
||||
|
||||
### Insecure persistent-login cookie
|
||||
|
||||
[index.php](index.php) lines 92-102 sets a cookie containing JSON `{email, hash}` with no `HttpOnly`, no `Secure`, no `SameSite` (also see `setcookie()` calls in [autoload/controls/class.Users.php](autoload/controls/class.Users.php) lines 43, 561).
|
||||
|
||||
**Fix:** issue an opaque random token, store hash server-side, set `HttpOnly; Secure; SameSite=Strict`.
|
||||
|
||||
### `eval()` in vendored grid
|
||||
|
||||
[libraries/grid/templates/results.php](libraries/grid/templates/results.php) line 289 and [libraries/grid/templates/print.php](libraries/grid/templates/print.php) line 73 evaluate strings drawn from `$_SESSION`. Session takeover ⇒ RCE.
|
||||
|
||||
**Fix:** replace with a safe expression evaluator or simple template helpers.
|
||||
|
||||
### Debug scripts shipped with credentials
|
||||
|
||||
`tmp/debug_*.php` (7 files) embed live DB creds. If `tmp/` is web-accessible, they're a one-shot console.
|
||||
|
||||
**Fix:** delete from repo; add `tmp/` to `.gitignore`; verify `tmp/` is not served (check [.htaccess](.htaccess)).
|
||||
|
||||
---
|
||||
|
||||
## Security — MEDIUM
|
||||
|
||||
### No CSRF protection
|
||||
|
||||
POST endpoints (controllers, [ajax.php](ajax.php), [api.php](api.php)) accept requests with no token validation. **Fix:** generate per-session token, embed in forms, verify in mutating actions.
|
||||
|
||||
### Path traversal in `\Tpl::render()`
|
||||
|
||||
[autoload/class.Tpl.php](autoload/class.Tpl.php) lines 31-62 builds `include` paths from `$file` without whitelisting. If `$file` ever flows from request data, `../../config` is reachable. Currently template names are hardcoded in controllers, so risk is latent — keep it that way.
|
||||
|
||||
### Inconsistent XSS escaping in templates
|
||||
|
||||
[templates/products/main_view.php](templates/products/main_view.php) defines a local `escape_html()` but applies it inconsistently. Polish content with apostrophes / quoted JSON in `<script>` blocks needs `htmlspecialchars($v, ENT_QUOTES, 'UTF-8')` consistently.
|
||||
|
||||
### IP-bound sessions break legitimate users
|
||||
|
||||
[ajax.php](ajax.php) lines 28-33 destroys the session on any `REMOTE_ADDR` change. NAT, mobile networks, and VPN switches will log users out frequently. Consider fingerprinting (UA + IP `/24`) instead of strict IP equality.
|
||||
|
||||
### File upload in Allegro controller
|
||||
|
||||
[autoload/controls/class.Allegro.php](autoload/controls/class.Allegro.php) lines 47-62 validates extension via `pathinfo`, not MIME. **Fix:** `finfo_file()`; store outside webroot.
|
||||
|
||||
### `exec()` in vendored upload handler
|
||||
|
||||
[libraries/filemanager-9.14.1/UploadHandler.php](libraries/filemanager-9.14.1/UploadHandler.php) lines 1006, 1032. **Fix:** prefer the `imagick`/`gd` extension; if `exec` stays, validate every argument with `escapeshellarg`.
|
||||
|
||||
---
|
||||
|
||||
## Tech debt — MEDIUM
|
||||
|
||||
### Dual ORM (Medoo + RedBeanPHP)
|
||||
|
||||
Medoo (`$mdb`) is the documented standard, but [api.php](api.php) (~1350 lines) and [cron.php](cron.php) use RedBeanPHP (`\R::`). Two query styles, two error paths, two sets of edge cases. Estimated 2-3 weeks to consolidate to Medoo.
|
||||
|
||||
### Monolithic controllers / factories
|
||||
|
||||
| File | Lines |
|
||||
|---|---|
|
||||
| [autoload/controls/class.Cron.php](autoload/controls/class.Cron.php) | ~5,200 |
|
||||
| [autoload/factory/class.Products.php](autoload/factory/class.Products.php) | ~1,540 |
|
||||
| [api.php](api.php) | ~1,350 |
|
||||
| [autoload/services/class.GoogleAdsApi.php](autoload/services/class.GoogleAdsApi.php) | ~3,200 |
|
||||
|
||||
`Cron` mixes campaign sync, product sync, search-term aggregation, and Facebook ingest. Extract per-pipeline service classes.
|
||||
|
||||
### N+1 queries in cron paths
|
||||
|
||||
[autoload/controls/class.Cron.php](autoload/controls/class.Cron.php) lines 96 (`task_user`), 155 (`users.email` inside nested loop). For 10 tasks × multiple users per task → ~100 round-trips. **Fix:** pre-fetch with `WHERE IN (...)`.
|
||||
|
||||
### Migration safety
|
||||
|
||||
[migrations/](migrations/) files use `IF NOT EXISTS` (good) and `schema_migrations` tracks application, but there's no rollback mechanism and no recovery path if a migration partially fails mid-run.
|
||||
|
||||
### Cache strategy gaps
|
||||
|
||||
[autoload/class.Cache.php](autoload/class.Cache.php) is file-based with MD5 keys, no invalidation hooks, no namespacing, no TTL audit. Suppression (`@unserialize`) hides corruption. Multi-server deployments would silently desync.
|
||||
|
||||
---
|
||||
|
||||
## Operational — MEDIUM
|
||||
|
||||
### No retry / backoff on external APIs
|
||||
|
||||
[autoload/services/class.GoogleAdsApi.php](autoload/services/class.GoogleAdsApi.php) issues 50+ direct `curl_exec` calls. No exponential backoff, no rate-limit detection. Same in [class.FacebookAdsApi.php](autoload/services/class.FacebookAdsApi.php). A throttle response just becomes a logged error.
|
||||
|
||||
### Global warning suppression
|
||||
|
||||
`error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING)` in [ajax.php](ajax.php) line 1 hides production bugs. Log warnings to a file, even if not displayed.
|
||||
|
||||
### No secrets rotation
|
||||
|
||||
API keys live in `settings` indefinitely; no expiry, no rotation reminder. Consider a 90-day reminder + log entry on access.
|
||||
|
||||
---
|
||||
|
||||
## Frontend — LOW
|
||||
|
||||
- jQuery 3.6 is fine; Bootstrap 5 (active) coexists with Bootstrap 4.1.3 (legacy) in [libraries/bootstrap-4.1.3/](libraries/bootstrap-4.1.3/) — verify nothing in production still loads it.
|
||||
- Two Font Awesome versions (4.7.0 + 6.5.1) — same audit needed.
|
||||
- SCSS is compiled manually. A 5-line `package.json` + `sass` watcher would prevent stale CSS commits.
|
||||
|
||||
---
|
||||
|
||||
## Compliance / privacy — LOW
|
||||
|
||||
- No documented GDPR data-export or deletion flow despite serving EU/PL users.
|
||||
- Persistent-cookie design embeds user identifier; not strictly PII-violating but worth re-architecting alongside the cookie-security fix.
|
||||
- No retention policy on `logs` or product/campaign history tables.
|
||||
|
||||
---
|
||||
|
||||
## Suggested remediation order
|
||||
|
||||
1. **Rotate and remove [config.php](config.php) credentials** — 1 day.
|
||||
2. **Cookie hardening + opaque token** — 2-3 days.
|
||||
3. **Replace `unserialize` with JSON in cache + grid** — 3-5 days.
|
||||
4. **CSRF tokens on mutating endpoints** — 1 week.
|
||||
5. **Consolidate ORMs (drop RedBeanPHP)** — 2-3 weeks.
|
||||
6. **Split `Cron` and `Products` factory** — 2 weeks.
|
||||
7. **Introduce PHPUnit + minimal CI** — 1 week.
|
||||
|
||||
Total realistic remediation: **~13 weeks** at one full-time engineer.
|
||||
104
.paul/codebase/conventions.md
Normal file
104
.paul/codebase/conventions.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Coding Conventions
|
||||
|
||||
CLAUDE.md documents the conventions; actual code matches ~95%. Specific patterns:
|
||||
|
||||
## Naming
|
||||
|
||||
- **Files:** `class.{ClassName}.php` (consistent across 40+ classes).
|
||||
- **Namespaces:** `\controls`, `\factory`, `\services`, `\view`. Root-namespace utilities live at `autoload/class.{Name}.php` (e.g. `\S`, `\Tpl`, `\DbModel`, `\Cache`, `\Chunk`, `\Cron`, `\Excel`, `\Html`).
|
||||
- Mapping enforced by `spl_autoload_register` in [index.php](index.php) lines 3-12.
|
||||
|
||||
## Methods
|
||||
|
||||
- Heavy reliance on **static methods** in controllers, factories, services. ~99% of public API.
|
||||
- Static modifier ordering inconsistent (`static public` vs `public static`).
|
||||
|
||||
## Database access
|
||||
|
||||
- Always via global `$mdb` (Medoo). Never `new` a connection.
|
||||
- Initialized once in [index.php](index.php) lines 24-31.
|
||||
- Raw SQL only for complex joins via `$mdb->query(':sql', [':param' => $value])->fetch()`.
|
||||
- **Exception:** [api.php](api.php) and [cron.php](cron.php) use RedBeanPHP (`\R::`). See [concerns.md](concerns.md) for the inconsistency.
|
||||
|
||||
## Reading params
|
||||
|
||||
```php
|
||||
$id = (int) \S::get('client_id');
|
||||
```
|
||||
|
||||
`\S::get($name)` reads POST first, then GET. Defined in [autoload/class.S.php](autoload/class.S.php) lines 198-217.
|
||||
|
||||
**Violations found** (use `$_POST` / `$_GET` directly):
|
||||
- [autoload/controls/class.Api.php](autoload/controls/class.Api.php) line 47 — API token fallback
|
||||
- [autoload/controls/class.Products.php](autoload/controls/class.Products.php) — DataTables `$_POST['order'][0]['dir']`/`['name']`
|
||||
|
||||
## Response patterns
|
||||
|
||||
```php
|
||||
// AJAX
|
||||
echo json_encode([...]);
|
||||
exit;
|
||||
|
||||
// Page
|
||||
return \Tpl::view('module/template', ['var' => $value]);
|
||||
```
|
||||
|
||||
100% consistent across controllers.
|
||||
|
||||
## Flash messages
|
||||
|
||||
```php
|
||||
\S::alert('Nazwa klienta jest wymagana.');
|
||||
header('Location: /clients');
|
||||
exit;
|
||||
```
|
||||
|
||||
`\S::alert()` writes to `$_SESSION['alert']`; layout reads it.
|
||||
|
||||
## Currency formatting
|
||||
|
||||
`\S::number_display($value)` → `"1 234,56 zł"` (Polish locale: space thousands separator, comma decimal). Defined at [autoload/class.S.php](autoload/class.S.php) lines 51-54.
|
||||
|
||||
## Localization
|
||||
|
||||
- All UI strings in **Polish**.
|
||||
- Timezone fixed to `Europe/Warsaw` (`date_default_timezone_set` in [index.php](index.php) line 14, [ajax.php](ajax.php) line 13).
|
||||
- Polish slug helper: `\S::seo($val)`. ASCII transliteration: `\S::noPL($val)`.
|
||||
|
||||
## Code style
|
||||
|
||||
- 2-space indent, no tabs.
|
||||
- K&R braces (open on same line).
|
||||
- No `declare(strict_types=1);` anywhere.
|
||||
- No type hints on signatures.
|
||||
- Explicit casting (`(int)`, `(string)`, `(float)`) used liberally for normalization.
|
||||
|
||||
## Comments
|
||||
|
||||
- Density low (~5%). No `@param`/`@return` docblocks in production code.
|
||||
- Only [autoload/class.Chunk.php](autoload/class.Chunk.php) (vendored) has docblocks.
|
||||
- Section headers occasionally in Polish (e.g. `// --- Autoryzacja ---`).
|
||||
|
||||
## Error handling
|
||||
|
||||
- Try/catch present in newer modules ([class.Clients.php](autoload/controls/class.Clients.php) lines 58-75, [class.Cron.php](autoload/controls/class.Cron.php)).
|
||||
- Catches generic `\Throwable`; no custom exception classes.
|
||||
- Errors persisted as DB settings (e.g. `google_ads_last_error`).
|
||||
- No central logger; ad-hoc `error_log` and DB `logs` table.
|
||||
- Global `error_reporting` suppresses `E_NOTICE | E_STRICT | E_WARNING` (set in [ajax.php](ajax.php) line 1).
|
||||
|
||||
## Templates
|
||||
|
||||
- Data injected via `\Tpl::view('module/file', ['k' => $v])`.
|
||||
- Inside templates: `<?php echo $this->k ?>` (magic `__get`).
|
||||
- Output buffering wraps each `include`.
|
||||
- User overrides: `templates_user/` is searched before `templates/`.
|
||||
|
||||
## Adding a new module
|
||||
|
||||
1. Create `autoload/controls/class.{Module}.php` (namespace `\controls`).
|
||||
2. Create `autoload/factory/class.{Module}.php` (namespace `\factory`).
|
||||
3. Optionally create `autoload/view/class.{Module}.php` (namespace `\view`).
|
||||
4. Create `templates/{module}/main_view.php` and other actions.
|
||||
5. Add to `$route_aliases` in [index.php](index.php) if clean URL needed.
|
||||
6. Add sidebar link in [templates/site/layout-logged.php](templates/site/layout-logged.php).
|
||||
80
.paul/codebase/integrations.md
Normal file
80
.paul/codebase/integrations.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# External Integrations
|
||||
|
||||
All API credentials are stored in the `settings` DB table and read via `\services\GoogleAdsApi::get_setting($key)` (used globally despite the name).
|
||||
|
||||
## Google Ads REST API
|
||||
|
||||
- **File:** [autoload/services/class.GoogleAdsApi.php](autoload/services/class.GoogleAdsApi.php) (~3,200 lines)
|
||||
- **API version:** `v23` (constant `$API_VERSION = 'v23'`)
|
||||
- **Base URLs:**
|
||||
- Ads: `https://googleads.googleapis.com`
|
||||
- Merchant Center: `https://shoppingcontent.googleapis.com/content/v2.1`
|
||||
- OAuth: `https://oauth2.googleapis.com/token`
|
||||
- **Auth:** OAuth 2.0 refresh-token flow. Settings keys: `google_ads_developer_token`, `google_ads_client_id`, `google_ads_client_secret`, `google_ads_refresh_token`, `google_ads_manager_account_id`. Merchant variant: `google_merchant_refresh_token` (falls back to ads token).
|
||||
- **Endpoints used:**
|
||||
- `POST /v23/customers/{customerId}/googleAds:search` — campaigns, search terms, ad groups
|
||||
- `GET /content/v2.1/{merchantAccountId}/products` — product listing/details
|
||||
- Product ID format: `online:{lang}:{feedLabel}:{offerId}`
|
||||
- **Errors:** stored in settings keys `google_ads_last_error` / `google_ads_last_error_at`.
|
||||
- **Cron:** `/cron/cron_universal`, `/cron/cron_products_urls`, `/cron/cron_campaigns_product_alerts_merchant`.
|
||||
|
||||
## Facebook Ads Graph API
|
||||
|
||||
- **File:** [autoload/services/class.FacebookAdsApi.php](autoload/services/class.FacebookAdsApi.php) (~300 lines)
|
||||
- **API version:** `v25.0` (default, configurable per client)
|
||||
- **Base URL:** `https://graph.facebook.com/{version}/{accountId}/insights`
|
||||
- **Auth:** access token in `facebook_ads_access_token` setting.
|
||||
- **Methods:** `get_insights()`, `get_campaigns()`, `parse_date_range()`. 30-day rolling window.
|
||||
- **Errors:** `facebook_ads_last_error` / `_at`.
|
||||
- **Cron:** `/cron/cron_facebook_ads`.
|
||||
|
||||
## Google Merchant Center
|
||||
|
||||
Integrated through `GoogleAdsApi`. Supplemental TSV feeds generated via [autoload/services/class.SupplementalFeed.php](autoload/services/class.SupplementalFeed.php) into `feeds/supplemental_{client_id}.tsv`. XML feed parsing via [autoload/services/class.XmlFeedImporter.php](autoload/services/class.XmlFeedImporter.php) using `\Chunk` streaming reader.
|
||||
|
||||
## OpenAI
|
||||
|
||||
- **File:** [autoload/services/class.OpenAiApi.php](autoload/services/class.OpenAiApi.php) (~400 lines)
|
||||
- **Endpoint:** `https://api.openai.com/v1/chat/completions`
|
||||
- **Auth:** Bearer token in `openai_api_key` setting; model in `openai_model`.
|
||||
- **Use cases:** product title (≤150 chars) and description (≤5000 chars) generation, page-content fetch + HTML strip + LLM rewrite. System prompt: Polish-language Merchant Center best practices.
|
||||
|
||||
## Claude (Anthropic)
|
||||
|
||||
- **File:** [autoload/services/class.ClaudeApi.php](autoload/services/class.ClaudeApi.php) (~300 lines)
|
||||
- **Endpoint:** `https://api.anthropic.com/v1/messages`
|
||||
- **Default model:** `claude-sonnet-4-5-20250929` (override via `claude_model`)
|
||||
- **Auth:** `x-api-key` header from `claude_api_key`. `anthropic-version: 2023-06-01`.
|
||||
- **Use cases:** same as OpenAI (product text optimization).
|
||||
|
||||
## Google Gemini
|
||||
|
||||
- **File:** [autoload/services/class.GeminiApi.php](autoload/services/class.GeminiApi.php) (~400 lines)
|
||||
- **Base:** `https://generativelanguage.googleapis.com/v1beta/models/`
|
||||
- **Default model:** `gemini-2.5-flash` (override via `gemini_model`)
|
||||
- **Auth:** API key in `?key=` query param (`gemini_api_key`).
|
||||
- **Special handling:** detects `gemini-2.5*` as thinking models — multiplies max_tokens by 6 for internal reasoning. Uses `systemInstruction` field.
|
||||
|
||||
## SMTP (PHPMailer)
|
||||
|
||||
- **Library:** [libraries/phpmailer/](libraries/phpmailer/) (3 files)
|
||||
- **Wrapper:** `\S::send_email($email, $subject, $text, $file)` in [autoload/class.S.php](autoload/class.S.php)
|
||||
- **Config (from [config.php](config.php)):** host `mail.project-pro.pl`, port 25, login `www@project-pro.pl`, password in plaintext (see [concerns.md](concerns.md)). SSL/TLS verification disabled (self-signed cert support). UTF-8, HTML body. Default From `www@projectpro.pl`, Reply-To `biuro@project-pro.pl`.
|
||||
|
||||
## Other / minor
|
||||
|
||||
- **Allegro.pl** — [autoload/controls/class.Allegro.php](autoload/controls/class.Allegro.php) (legacy marketplace integration)
|
||||
- **Open Page Rank API** — referenced in [api.php](api.php) for domain authority lookups
|
||||
- **Domain tester** — third-party domain validation in [api.php](api.php)
|
||||
|
||||
## Summary
|
||||
|
||||
| Integration | API ver | Auth | Cron |
|
||||
|---|---|---|---|
|
||||
| Google Ads | v23 | OAuth 2.0 refresh | `/cron/cron_universal` |
|
||||
| Google Merchant | v2.1 | OAuth 2.0 | `/cron/cron_campaigns_product_alerts_merchant` |
|
||||
| Facebook Ads | v25.0 | Bearer token | `/cron/cron_facebook_ads` |
|
||||
| OpenAI | latest | API key (header) | on-demand |
|
||||
| Claude | v1 / 2023-06-01 | `x-api-key` | on-demand |
|
||||
| Gemini | v1beta | API key (query) | on-demand |
|
||||
| SMTP | — | Basic auth | reminders via `cron.php` |
|
||||
53
.paul/codebase/overview.md
Normal file
53
.paul/codebase/overview.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# adsPRO — Codebase Overview
|
||||
|
||||
**Generated:** 2026-05-06
|
||||
|
||||
## What it is
|
||||
|
||||
adsPRO is a PHP web application for managing **Google Ads**, **Facebook Ads**, and **Google Merchant Center** campaigns. It tracks performance (ROAS, budgets, conversions), manages product feeds, and provides AI-powered suggestions (OpenAI / Claude / Gemini) for product titles and descriptions. UI in Polish, timezone `Europe/Warsaw`.
|
||||
|
||||
## At a glance
|
||||
|
||||
| Aspect | Value |
|
||||
|---|---|
|
||||
| Language | PHP (no framework, custom MVC-like) |
|
||||
| Database | MySQL via Medoo ORM (`$mdb`) + RedBeanPHP (`\R::`) in legacy paths |
|
||||
| Frontend | jQuery 3.6, Bootstrap 5, DataTables 2.1.7, Highcharts, Select2, FontAwesome 6.5.1 |
|
||||
| Build | SCSS → CSS (manual); no Composer, no npm |
|
||||
| Tests | None (manual `tmp/debug_*.php` scripts) |
|
||||
| CI/CD | None |
|
||||
|
||||
## Entry points
|
||||
|
||||
- [index.php](index.php) — main router, autoloader, auth, request dispatch
|
||||
- [ajax.php](ajax.php) — AJAX endpoints, session+IP binding
|
||||
- [api.php](api.php) — external API endpoints (~1350 lines, RedBeanPHP)
|
||||
- [cron.php](cron.php) — legacy cron (task reminders, recurring tasks)
|
||||
- `/cron/*` routes (via index.php) — modern cron pipelines
|
||||
|
||||
## Layered structure
|
||||
|
||||
- **`autoload/controls/`** — Controllers (static methods, read params via `\S::get()`)
|
||||
- **`autoload/factory/`** — Data access (static, uses global `$mdb`)
|
||||
- **`autoload/services/`** — External API integrations
|
||||
- **`autoload/view/`** — Thin view helpers wrapping `\Tpl::view()`
|
||||
- **`templates/{module}/`** — PHP templates accessed via `$this->varName`
|
||||
- **`migrations/`** — Numbered SQL files, tracked in `schema_migrations` table
|
||||
|
||||
## Key base classes
|
||||
|
||||
| Class | File | Purpose |
|
||||
|---|---|---|
|
||||
| `\S` | [autoload/class.S.php](autoload/class.S.php) | Params (`get`), session, alerts, email, currency formatting |
|
||||
| `\Tpl` | [autoload/class.Tpl.php](autoload/class.Tpl.php) | Template engine (looks in `templates_user/` then `templates/`) |
|
||||
| `\DbModel` | [autoload/class.DbModel.php](autoload/class.DbModel.php) | Active Record base |
|
||||
| `\Cache` | [autoload/class.Cache.php](autoload/class.Cache.php) | File-based cache in `temp/` |
|
||||
|
||||
## Documents in this map
|
||||
|
||||
- [tech-stack.md](tech-stack.md) — language, libraries, versions
|
||||
- [architecture.md](architecture.md) — entry points, layers, data flow, modules
|
||||
- [integrations.md](integrations.md) — external APIs (Google Ads, Facebook, AI providers, SMTP)
|
||||
- [conventions.md](conventions.md) — coding style and patterns
|
||||
- [testing.md](testing.md) — testing posture (limited)
|
||||
- [concerns.md](concerns.md) — risks, security, tech debt
|
||||
47
.paul/codebase/tech-stack.md
Normal file
47
.paul/codebase/tech-stack.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Tech Stack
|
||||
|
||||
## Backend
|
||||
|
||||
| Component | Version | Notes |
|
||||
|---|---|---|
|
||||
| PHP | 5.6+ assumed (no explicit constraint) | No `composer.json`, no `declare(strict_types)` |
|
||||
| MySQL | unspecified | Schema in [migrations/](migrations/) |
|
||||
| Medoo ORM | 1.7.10 | [libraries/medoo/medoo.php](libraries/medoo/medoo.php), used via global `$mdb` |
|
||||
| RedBeanPHP | bundled | [libraries/rb.php](libraries/rb.php), used in [api.php](api.php) and [cron.php](cron.php) |
|
||||
| PHPMailer | bundled | [libraries/phpmailer/](libraries/phpmailer/) |
|
||||
|
||||
**Autoloader:** custom `spl_autoload_register` in [index.php](index.php) maps namespaces to `autoload/{layer}/class.{Name}.php`. No Composer.
|
||||
|
||||
## Frontend
|
||||
|
||||
| Library | Version | Source |
|
||||
|---|---|---|
|
||||
| jQuery | 3.6.0 | CDN |
|
||||
| jQuery UI | bundled | [libraries/framework/vendor/jquery/jquery_ui/](libraries/framework/vendor/jquery/jquery_ui/) |
|
||||
| Bootstrap | 5 (active) + 4.1.3 (legacy) | CDN + [libraries/bootstrap-4.1.3/](libraries/bootstrap-4.1.3/) |
|
||||
| DataTables | 2.1.7 | CDN |
|
||||
| Highcharts | latest | CDN |
|
||||
| Select2 | 4.1.0-rc.0 | CDN + [libraries/select2/](libraries/select2/) |
|
||||
| Font Awesome | 6.5.1 (active) + 4.7.0 (legacy) | CDN + [libraries/font-awesome-4.7.0/](libraries/font-awesome-4.7.0/) |
|
||||
| Moment.js | 2.18.1 | CDN + [libraries/moment/](libraries/moment/) |
|
||||
| Date Range Picker | bundled | [libraries/daterange/](libraries/daterange/) |
|
||||
| CKEditor | bundled | [libraries/ckeditor/](libraries/ckeditor/) |
|
||||
|
||||
Custom assets: [libraries/adspro-dialog.js](libraries/adspro-dialog.js), [libraries/functions.js](libraries/functions.js), [libraries/xlsxwriter.class.php](libraries/xlsxwriter.class.php).
|
||||
|
||||
## Styling
|
||||
|
||||
- SCSS: [layout/style.scss](layout/style.scss) → [layout/style.css](layout/style.css) (manual compilation; no automated build pipeline)
|
||||
- Source map present: [layout/style.css.map](layout/style.css.map)
|
||||
|
||||
## Configuration
|
||||
|
||||
- [config.php](config.php) — DB credentials, SMTP settings (plaintext; see [concerns.md](concerns.md))
|
||||
- `settings` DB table — runtime config, API keys, cron state, feature flags. Accessed via `\services\GoogleAdsApi::get_setting($key)` / `set_setting($k, $v)` (used globally despite the class name).
|
||||
|
||||
## Tooling absent
|
||||
|
||||
- No Composer / package.json / lockfiles
|
||||
- No PHPUnit / phpcs / phpstan
|
||||
- No `.editorconfig`, no `.github/workflows`, no CI
|
||||
- No linting / formatting config
|
||||
35
.paul/codebase/testing.md
Normal file
35
.paul/codebase/testing.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Testing
|
||||
|
||||
## Posture
|
||||
|
||||
**No formal test infrastructure.** No PHPUnit, no Pest, no `tests/` or `spec/` directories, no `phpunit.xml`, no `composer.json`. No CI pipeline (no `.github/workflows`).
|
||||
|
||||
## What exists
|
||||
|
||||
Manual debug scripts under [tmp/](tmp/) — developer-run, not automated:
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `tmp/debug_clients.php` | Client sync verification |
|
||||
| `tmp/debug_clients_remote.php`, `debug_clients_remote2.php` | Remote API checks |
|
||||
| `tmp/debug_dup_products.php` | Duplicate product detection (currently uncommitted) |
|
||||
| `tmp/debug_eligible_remote.php` | Eligibility checks |
|
||||
| `tmp/debug_products_urls.php` | URL fetch verification |
|
||||
| `tmp/campaign_alerts_debug.log` | Output log from alerts run |
|
||||
| `tmp/meta_active_last30d.json`, `tmp/products_data_save.txt` | Test data snapshots |
|
||||
|
||||
These scripts contain **live database credentials** — see [concerns.md](concerns.md) for the security implication.
|
||||
|
||||
## Implicit "tests"
|
||||
|
||||
- `cron_sync_status` table acts as production-time integration check (phase progression).
|
||||
- `logs` table provides audit trail for after-the-fact debugging.
|
||||
- Settings keys `*_last_error` / `*_last_error_at` capture last failure per integration.
|
||||
|
||||
## Recommendations (if testing is introduced)
|
||||
|
||||
1. Adopt PHPUnit via Composer (also fixes the lack of dependency management).
|
||||
2. Start with **factory** layer — it's the most testable (pure DB queries given a fixture DB).
|
||||
3. Service layer needs HTTP client abstraction before the cURL calls can be tested without hitting live APIs.
|
||||
4. Migrations are already partly idempotent — a CI job that runs `php install.php` against a scratch DB would be cheap insurance.
|
||||
5. Move `tmp/debug_*.php` content into proper integration tests; delete the scripts.
|
||||
42
.paul/governance/governance_2026-05-06.jsonl
Normal file
42
.paul/governance/governance_2026-05-06.jsonl
Normal file
@@ -0,0 +1,42 @@
|
||||
{"ts":"2026-05-06T20:10:08Z","tool":"Bash","cmd":"mysql --version 2>&1 | head -3\",\"description\":\"Check mysql client availability\"},\"tool_response\":{\"stdout\":\"/usr/bin/bash: line 1: mysql: command not found\",\"stderr\":\"\",\"interrupted\":","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:10:14Z","tool":"Bash","cmd":"where.exe mysql 2>&1; where.exe php 2>&1\",\"description\":\"Check mysql/php on Windows\"},\"tool_response\":{\"stdout\":\"INFO: Could not find files for the given pattern(s).\\r\\nC:\\\\xampp\\\\php\","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:10:38Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\debug_dup_products.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:10:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\debug_dup_products.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:10:57Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/debug_dup_products.php\" 2>&1\",\"timeout\":60000,\"description\":\"Run diagnostic against remote DB\"},\"tool_response\":{\"stdout","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:11:58Z","tool":"Bash","cmd":"mkdir -p .paul/codebase && ls .paul/codebase/\",\"description\":\"Create codebase directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputExpe","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:12:53Z","tool":"Bash","cmd":"Get-ChildItem -Path \"C:\\\\visual studio code\\\\projekty\\\\adsPRO\" -Recurse -Force | Select-Object FullName | wc -l\"},\"tool_response\":{\"stdout\":\"/usr/bin/bash: line 1: Get-ChildItem: command","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:13:39Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\debug_dup_audit.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:13:45Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/debug_dup_audit.php\" 2>&1\",\"timeout\":120000,\"description\":\"Audit duplicates and FK landscape\"},\"tool_response\":{\"stdout\","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:13:52Z","tool":"Bash","cmd":"wc -l \"C:/visual studio code/projekty/adsPRO/cron.php\" \"C:/visual studio code/projekty/adsPRO/api.php\"\"},\"tool_response\":{\"stdout\":\" 77 C:/visual studio code/projekty/adsPRO/cron.php\\n 13","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:14:31Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\debug_dup_indexes.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:14:56Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/debug_dup_indexes.php\" 2>&1\",\"timeout\":90000,\"description\":\"Audit indexes on tables with product_id\"},\"tool_response\":{\"","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:15:17Z","tool":"Write","file":"c:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\codebase\\\\overview.md","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:15:35Z","tool":"Write","file":"c:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\codebase\\\\tech-stack.md","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:16:15Z","tool":"Write","file":"c:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\codebase\\\\architecture.md","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:16:46Z","tool":"Write","file":"c:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\codebase\\\\integrations.md","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:17:18Z","tool":"Write","file":"c:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\codebase\\\\conventions.md","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:17:36Z","tool":"Write","file":"c:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\codebase\\\\testing.md","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:18:26Z","tool":"Write","file":"c:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\codebase\\\\concerns.md","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:18:42Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\merge_duplicate_products.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:26:43Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/merge_duplicate_products.php\" --dry-run 2>&1\",\"timeout\":600000,\"description\":\"Full dry-run\"},\"tool_response\":{\"stdout\":","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:28:44Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/adsPRO\" && BACKUP=\"backup_dup_$(date +%Y%m%d_%H%M).sql\" && \"C:/xampp/mysql/bin/mysqldump.exe\" -h host700513.hostido.net.pl -u host700513_adspro -p\"2Ug7DvBy5MC","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:28:55Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/merge_duplicate_products.php\" --execute --client=2 --limit=5 2>&1\",\"timeout\":120000,\"description\":\"Test execute on 5 groups\","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:29:11Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\debug_verify_test.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:29:17Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/debug_verify_test.php\" 2>&1\",\"timeout\":60000,\"description\":\"Verify test execute\"},\"tool_response\":{\"stdout\":\"=== Czy l","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:39:25Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/merge_duplicate_products.php\" --execute 2>&1\",\"timeout\":600000,\"description\":\"Full migration\"},\"tool_response\":{\"stdout\","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:40:09Z","tool":"Bash","cmd":"until grep -q \"STATYSTYKI (WYKONANO)\" \"C:/Users/jacek/AppData/Local/Temp/claude/C--visual-studio-code-projekty-adsPRO/002c2267-55dd-47de-b760-4f48b17e7e6c/tasks/bcrvifsv6.output\" 2>/dev/null; do s","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:45:18Z","tool":"Bash","cmd":"until grep -q \"STATYSTYKI (WYKONANO)\" \"C:/Users/jacek/AppData/Local/Temp/claude/C--visual-studio-code-projekty-adsPRO/002c2267-55dd-47de-b760-4f48b17e7e6c/tasks/bcrvifsv6.output\" 2>/dev/null; do s","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:45:39Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\debug_verify_final.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:45:46Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/debug_verify_final.php\" 2>&1\",\"timeout\":90000,\"description\":\"Final verification\"},\"tool_response\":{\"stdout\":\"=== Czy z","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:46:23Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\migrations\\\\031_products_unique_client_offer.sql","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:46:44Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\apply_migration_031.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:47:17Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\tmp\\\\apply_migration_031.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:47:23Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/adsPRO/tmp/apply_migration_031.php\" 2>&1\",\"timeout\":120000,\"description\":\"Apply migration 031\"},\"tool_response\":{\"stdout\":\"Apliku","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:49:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\autoload\\\\factory\\\\class.Products.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:49:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\autoload\\\\controls\\\\class.Cron.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:50:04Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\autoload\\\\controls\\\\class.Api.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:50:10Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\autoload\\\\controls\\\\class.Api.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:50:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\autoload\\\\controls\\\\class.Allegro.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:50:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\autoload\\\\services\\\\class.XmlFeedImporter.php","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:50:54Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" -l \"C:/visual studio code/projekty/adsPRO/autoload/factory/class.Products.php\" 2>&1; \"C:/xampp/php/php.exe\" -l \"C:/visual studio code/projekty/adsPRO/autoload/controls/cl","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
{"ts":"2026-05-06T20:51:06Z","tool":"Bash","cmd":"rm \"C:/visual studio code/projekty/adsPRO/tmp/debug_dup_products.php\" \"C:/visual studio code/projekty/adsPRO/tmp/debug_dup_audit.php\" \"C:/visual studio code/projekty/adsPRO/tmp/debug_dup_indexes.","cwd":"/c/visual studio code/projekty/adsPRO"}
|
||||
Reference in New Issue
Block a user