'mysql', 'database_name' => $database['name'], 'server' => $database['host'], 'username' => $database['user'], 'password' => $database['password'], 'charset' => 'utf8' ] ); \R::setup( 'mysql:host=' . $database['host'] . ';dbname=' . $database['name'], $database['user'], $database['password'] ); \R::ext( 'xdispense', function( $type ) { return R::getRedBean() -> dispense( $type ); } ); function api_json_response( $data, $http_code = 200 ) { http_response_code( (int) $http_code ); echo json_encode( $data ); exit; } function api_validate_api_key( $mdb ) { $api_key = trim( (string) \S::get( 'api_key' ) ); $stored_key = trim( (string) $mdb -> get( 'settings', 'setting_value', [ 'setting_key' => 'api_key' ] ) ); if ( $api_key === '' || $stored_key === '' || !hash_equals( $stored_key, $api_key ) ) { api_json_response( [ 'result' => 'error', 'message' => 'Invalid api_key' ], 401 ); } } function api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id ) { return $mdb -> query( 'SELECT p.id, p.name, p.title, p.google_product_category, p.custom_label_3, p.custom_label_4 FROM products p JOIN clients cl ON p.client_id = cl.id WHERE p.offer_id = :offer_id AND cl.id = :client_id LIMIT 1', [ ':offer_id' => (string) $offer_id, ':client_id' => (int) $client_id ] ) -> fetch( \PDO::FETCH_ASSOC ); } function api_get_product_by_id_or_offer_id( $mdb, $product_id, $offer_id ) { if ( $product_id > 0 ) { return $mdb -> query( 'SELECT p.id, p.offer_id, p.min_roas FROM products p WHERE p.id = :product_id LIMIT 1', [ ':product_id' => (int) $product_id ] ) -> fetch( \PDO::FETCH_ASSOC ); } if ( $offer_id !== '' ) { return $mdb -> query( 'SELECT p.id, p.offer_id, p.min_roas FROM products p WHERE p.offer_id = :offer_id LIMIT 1', [ ':offer_id' => (string) $offer_id ] ) -> fetch( \PDO::FETCH_ASSOC ); } return null; } function api_normalize_product_text( $value ) { $value = trim( (string) $value ); if ( $value === '' ) { return null; } return $value; } // dodawanie domeny przez API if ( \S::get( 'action' ) == 'domain_tester_add' ) { if ( !$mdb -> count( 'domain_tester', [ 'AND' => [ 'url' => \S::get( 'domain' ), 'hidden' => 0 ] ] ) ) { $mdb -> insert( 'domain_tester', [ 'url' => \S::get( 'domain' ) ] ); } echo json_encode( ['result' => 'ok'] ); exit; } // Open Page Rank - pobieranie domeny if ( \S::get( 'action' ) == 'domain_opr_check' ) { $mdb -> delete( 'domain_tester', [ 'url' => '' ] ); $domain = $mdb -> get( 'domain_tester', '*', [ 'opr' => null, 'ORDER' => [ 'date_add' => 'DESC' ] ] ); if ( $domain ) { $result['id'] = $domain['id']; $result['url'] = $domain['url']; $result['domains_left'] = $mdb -> count( 'domain_tester', [ 'opr' => null ] ); $result['result'] = 'ok'; echo json_encode( $result ); } else echo json_encode( ['result' => 'bad'] ); exit; } // Dodawanie komentarza do kampanii przez API (z Claude Code) if ( \S::get( 'action' ) == 'campaign_comment_add' ) { $api_key = trim( \S::get( 'api_key' ) ); $stored_key = $mdb -> get( 'settings', 'setting_value', [ 'setting_key' => 'api_key' ] ); if ( !$api_key || !$stored_key || $api_key !== $stored_key ) { echo json_encode( [ 'result' => 'error', 'message' => 'Invalid api_key' ] ); exit; } $external_campaign_id = trim( \S::get( 'campaign_id' ) ); $client_id_param = trim( \S::get( 'client_id' ) ); $comment = trim( \S::get( 'comment' ) ); $date = \S::get( 'date' ) ?: date( 'Y-m-d' ); if ( !$external_campaign_id || !$client_id_param || !$comment ) { echo json_encode( [ 'result' => 'error', 'message' => 'Missing required params: campaign_id, client_id, comment' ] ); exit; } $client_id_clean = str_replace( '-', '', $client_id_param ); $local_campaign = $mdb -> query( 'SELECT c.id FROM campaigns c JOIN clients cl ON c.client_id = cl.id WHERE c.campaign_id = :campaign_id AND REPLACE( cl.google_ads_customer_id, \'-\', \'\' ) = :client_id LIMIT 1', [ ':campaign_id' => $external_campaign_id, ':client_id' => $client_id_clean ] ) -> fetch( \PDO::FETCH_ASSOC ); if ( !$local_campaign ) { echo json_encode( [ 'result' => 'error', 'message' => 'Campaign not found' ] ); exit; } \factory\Campaigns::add_campaign_comment( $local_campaign['id'], $comment, $date ); echo json_encode( [ 'result' => 'ok' ] ); exit; } // Zmiana custom_label_3 dla produktu przez API if ( \S::get( 'action' ) == 'product_custom_label_3_set' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); $custom_label_3 = trim( (string) ( \S::get( 'custom_label_3' ) ?? \S::get( 'value' ) ?? '' ) ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } $update_result = \factory\Products::set_product_data( (int) $product['id'], 'custom_label_3', $custom_label_3 ); if ( !$update_result ) { api_json_response( [ 'result' => 'error', 'message' => 'Failed to update custom_label_3' ], 500 ); } \factory\Products::add_product_comment( (int) $product['id'], 'Zmiana etykiety 3 na: ' . ( $custom_label_3 !== '' ? $custom_label_3 : '(puste)' ) . ' (API)' ); api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'custom_label_3' => $custom_label_3 ] ); } // Odczyt custom_label_3 dla produktu przez API if ( \S::get( 'action' ) == 'product_custom_label_3_get' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'custom_label_3' => trim( (string) ( $product['custom_label_3'] ?? '' ) ) ] ); } // Zmiana custom_label_4 dla produktu przez API if ( \S::get( 'action' ) == 'product_custom_label_4_set' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); $custom_label_4 = trim( (string) ( \S::get( 'custom_label_4' ) ?? \S::get( 'value' ) ?? '' ) ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } $update_result = \factory\Products::set_product_data( (int) $product['id'], 'custom_label_4', $custom_label_4 ); if ( !$update_result ) { api_json_response( [ 'result' => 'error', 'message' => 'Failed to update custom_label_4' ], 500 ); } \factory\Products::add_product_comment( (int) $product['id'], 'Zmiana etykiety 4 na: ' . ( $custom_label_4 !== '' ? $custom_label_4 : '(puste)' ) . ' (API)' ); api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'custom_label_4' => $custom_label_4 ] ); } // Odczyt custom_label_4 dla produktu przez API if ( \S::get( 'action' ) == 'product_custom_label_4_get' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'custom_label_4' => trim( (string) ( $product['custom_label_4'] ?? '' ) ) ] ); } // Zmiana tytulu produktu przez API if ( \S::get( 'action' ) == 'product_title_set' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); $new_title = api_normalize_product_text( \S::get( 'title' ) ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } $old_title = (string) ( $product['title'] ?? '' ); \factory\Products::set_product_data( (int) $product['id'], 'title', $new_title ); $old_title_for_log = trim( $old_title ) !== '' ? $old_title : '[pusty]'; $new_title_for_log = $new_title !== null ? $new_title : '[pusty]'; \factory\Products::add_product_comment( (int) $product['id'], 'Zmiana tytulu przez API: ' . $old_title_for_log . ' -> ' . $new_title_for_log ); api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'title' => $new_title ] ); } // Sprawdzenie, czy tytul produktu byl juz zmieniony if ( \S::get( 'action' ) == 'product_title_changed_check' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } $base_name = trim( (string) ( $product['name'] ?? '' ) ); $custom_title = trim( (string) ( $product['title'] ?? '' ) ); $is_changed = $custom_title !== '' && $custom_title !== $base_name; api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'title_changed' => $is_changed, 'default_name' => $base_name, 'custom_title' => $custom_title !== '' ? $custom_title : null ] ); } // Zmiana Google Product Category przez API if ( \S::get( 'action' ) == 'product_google_category_set' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); $google_category = api_normalize_product_text( \S::get( 'google_product_category' ) ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } $old_category = (string) ( $product['google_product_category'] ?? '' ); \factory\Products::set_product_data( (int) $product['id'], 'google_product_category', $google_category ); $old_category_for_log = trim( $old_category ) !== '' ? $old_category : '[pusty]'; $new_category_for_log = $google_category !== null ? $google_category : '[pusty]'; \factory\Products::add_product_comment( (int) $product['id'], 'Zmiana Google Product Category przez API: ' . $old_category_for_log . ' -> ' . $new_category_for_log ); api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'google_product_category' => $google_category ] ); } // Odczyt Google Product Category przez API if ( \S::get( 'action' ) == 'product_google_category_get' ) { api_validate_api_key( $mdb ); $offer_id = trim( (string) \S::get( 'offer_id' ) ); $client_id_param = (int) \S::get( 'client_id' ); if ( $offer_id === '' || $client_id_param <= 0 ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required params: offer_id, client_id' ], 422 ); } $product = api_get_product_by_offer_and_client( $mdb, $offer_id, $client_id_param ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } $google_category = trim( (string) ( $product['google_product_category'] ?? '' ) ); api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => $offer_id, 'client_id' => $client_id_param, 'google_product_category' => $google_category !== '' ? $google_category : null ] ); } // Odczyt minimalnego ROAS produktu przez API if ( \S::get( 'action' ) == 'product_min_roas_get' ) { api_validate_api_key( $mdb ); $product_id = (int) \S::get( 'product_id' ); $google_ads_product_id = trim( (string) \S::get( 'google_ads_product_id' ) ); if ( $product_id <= 0 && $google_ads_product_id === '' ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required param: product_id or google_ads_product_id' ], 422 ); } if ( $product_id > 0 && $google_ads_product_id !== '' ) { api_json_response( [ 'result' => 'error', 'message' => 'Provide only one identifier: product_id or google_ads_product_id' ], 422 ); } $product = api_get_product_by_id_or_offer_id( $mdb, $product_id, $google_ads_product_id ); if ( !$product ) { api_json_response( [ 'result' => 'error', 'message' => 'Product not found' ], 404 ); } $min_roas_raw = $product['min_roas'] ?? null; $min_roas = $min_roas_raw !== null && $min_roas_raw !== '' ? (float) $min_roas_raw : null; api_json_response( [ 'result' => 'ok', 'product_id' => (int) $product['id'], 'offer_id' => (string) ( $product['offer_id'] ?? '' ), 'min_roas' => $min_roas ] ); } // Odczyt sredniego minimalnego ROAS dla klienta if ( \S::get( 'action' ) == 'client_avg_min_roas_get' ) { api_validate_api_key( $mdb ); $client_id = (int) \S::get( 'client_id' ); $google_ads_id = trim( (string) \S::get( 'google_ads_id' ) ); if ( $client_id <= 0 && $google_ads_id === '' ) { api_json_response( [ 'result' => 'error', 'message' => 'Missing required param: client_id or google_ads_id' ], 422 ); } if ( $client_id > 0 && $google_ads_id !== '' ) { api_json_response( [ 'result' => 'error', 'message' => 'Provide only one identifier: client_id or google_ads_id' ], 422 ); } $resolved_client = null; if ( $client_id > 0 ) { $resolved_client = $mdb -> query( 'SELECT id, google_ads_customer_id FROM clients WHERE id = :client_id LIMIT 1', [ ':client_id' => $client_id ] ) -> fetch( \PDO::FETCH_ASSOC ); } else { $google_ads_id_clean = str_replace( '-', '', $google_ads_id ); $resolved_client = $mdb -> query( 'SELECT id, google_ads_customer_id FROM clients WHERE REPLACE( google_ads_customer_id, \'-\', \'\' ) = :google_ads_id LIMIT 1', [ ':google_ads_id' => $google_ads_id_clean ] ) -> fetch( \PDO::FETCH_ASSOC ); } if ( !$resolved_client ) { api_json_response( [ 'result' => 'error', 'message' => 'Client not found' ], 404 ); } $avg_row = $mdb -> query( 'SELECT AVG( p.min_roas ) AS avg_min_roas, COUNT( p.id ) AS products_with_min_roas FROM products p WHERE p.client_id = :client_id AND p.min_roas IS NOT NULL', [ ':client_id' => (int) $resolved_client['id'] ] ) -> fetch( \PDO::FETCH_ASSOC ); $avg_min_roas_raw = $avg_row['avg_min_roas'] ?? null; $avg_min_roas = $avg_min_roas_raw !== null && $avg_min_roas_raw !== '' ? round( (float) $avg_min_roas_raw, 6 ) : null; $products_with_min_roas = (int) ( $avg_row['products_with_min_roas'] ?? 0 ); api_json_response( [ 'result' => 'ok', 'client_id' => (int) $resolved_client['id'], 'google_ads_id' => (string) ( $resolved_client['google_ads_customer_id'] ?? '' ), 'products_with_min_roas' => $products_with_min_roas, 'avg_min_roas' => $avg_min_roas ] ); } // Pobranie 10 produktow do optymalizacji (wg klikniec, bez nowego tytulu lub kategorii Google) if ( \S::get( 'action' ) == 'products_to_optimize' ) { api_validate_api_key( $mdb ); $client_id = trim( (string) \S::get( 'client_id' ) ); $google_ads_id = trim( (string) \S::get( 'google_ads_id' ) ); $merchant_id = trim( (string) \S::get( 'merchant_id' ) ); $limit = (int) \S::get( 'limit' ); if ( $limit <= 0 || $limit > 100 ) { $limit = 10; } // Resolve client_id from whichever parameter was provided $resolved_client_id = null; if ( $client_id !== '' ) { $resolved_client_id = (int) $mdb -> get( 'clients', 'id', [ 'id' => (int) $client_id ] ); } elseif ( $google_ads_id !== '' ) { $google_ads_id_clean = str_replace( '-', '', $google_ads_id ); $resolved_client_id = (int) $mdb -> query( 'SELECT id FROM clients WHERE REPLACE( google_ads_customer_id, \'-\', \'\' ) = :gid LIMIT 1', [ ':gid' => $google_ads_id_clean ] ) -> fetchColumn(); } elseif ( $merchant_id !== '' ) { $merchant_id_clean = str_replace( '-', '', $merchant_id ); $resolved_client_id = (int) $mdb -> query( 'SELECT id FROM clients WHERE REPLACE( google_merchant_account_id, \'-\', \'\' ) = :mid LIMIT 1', [ ':mid' => $merchant_id_clean ] ) -> fetchColumn(); } else { api_json_response( [ 'result' => 'error', 'message' => 'Missing required param: client_id, google_ads_id or merchant_id' ], 422 ); } if ( !$resolved_client_id ) { api_json_response( [ 'result' => 'error', 'message' => 'Client not found' ], 404 ); } $products = $mdb -> query( 'SELECT p.id, p.offer_id, p.name AS original_name, p.title AS custom_title, p.google_product_category, p.product_url, SUM( pa.clicks_30 ) AS clicks_30, SUM( pa.clicks_all_time ) AS clicks_all_time, SUM( pa.impressions_30 ) AS impressions_30, SUM( pa.cost_30 ) AS cost_30, SUM( pa.conversions_30 ) AS conversions_30, SUM( pa.conversion_value_30 ) AS conversion_value_30 FROM products AS p INNER JOIN products_aggregate AS pa ON pa.product_id = p.id WHERE p.client_id = :client_id AND ( TRIM( COALESCE( p.title, \'\' ) ) = \'\' OR TRIM( COALESCE( p.google_product_category, \'\' ) ) = \'\' ) GROUP BY p.id HAVING clicks_all_time > 0 ORDER BY clicks_all_time DESC LIMIT :limit', [ ':client_id' => $resolved_client_id, ':limit' => $limit ] ) -> fetchAll( \PDO::FETCH_ASSOC ); api_json_response( [ 'result' => 'ok', 'client_id' => $resolved_client_id, 'count' => count( $products ), 'products' => $products ] ); } // Open Page Rank - zapis if ( \S::get( 'action' ) == 'domain_opr_save' ) { $mdb -> update( 'domain_tester', [ 'opr' => str_replace( ',', '.', \S::get( 'page_rank' ) ), 'opr_date' => date( 'Y-m-d H:i:s' ) ], [ 'id' => \S::get( 'domain_id' ) ] ); echo json_encode( ['result' => 'ok'] ); exit; }