vantuz 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,8 +17,9 @@ class EIAMonitor {
17
17
  this.productUrls = this._parseUrls(this.env.EIA_PRODUCT_URLS);
18
18
  this.competitorUrls = this._parseUrls(this.env.EIA_COMPETITOR_URLS);
19
19
  this.targetProfitMargin = parseFloat(this.env.EIA_TARGET_PROFIT_MARGIN || '15'); // Default 15%
20
- this.scrapers = {
21
- trendyol: new TrendyolScraper() // Initialize other scrapers here
20
+ this.baselineData = {};
21
+ this.scraperFactories = {
22
+ trendyol: () => new TrendyolScraper() // Initialize other scrapers here
22
23
  };
23
24
  log('INFO', 'EIA Monitor initialized');
24
25
  }
@@ -101,9 +102,9 @@ class EIAMonitor {
101
102
  */
102
103
  async startMonitoring(productId, platform, productUrl, dataType, cronSchedule) {
103
104
  const jobName = `monitor-${platform}-${productId}-${dataType}`;
104
- const scraper = this.scrapers[platform];
105
+ const createScraper = this.scraperFactories[platform];
105
106
 
106
- if (!scraper) {
107
+ if (!createScraper) {
107
108
  log('ERROR', `No scraper found for platform: ${platform}`);
108
109
  return;
109
110
  }
@@ -111,6 +112,7 @@ class EIAMonitor {
111
112
  const task = async () => {
112
113
  log('INFO', `Monitoring ${dataType} for product ${productId} on ${platform}`);
113
114
  let rawData;
115
+ const scraper = createScraper();
114
116
  try {
115
117
  // Scrape Data
116
118
  switch (dataType) {
@@ -144,6 +146,12 @@ class EIAMonitor {
144
146
 
145
147
  } catch (error) {
146
148
  log('ERROR', `Error during monitoring job ${jobName}: ${error.message}`, { error });
149
+ } finally {
150
+ try {
151
+ await scraper.close();
152
+ } catch (e) {
153
+ log('WARN', `Failed to close scraper for ${jobName}: ${e.message}`);
154
+ }
147
155
  }
148
156
  };
149
157
 
@@ -165,10 +173,10 @@ class EIAMonitor {
165
173
  }
166
174
 
167
175
  // Simple example: if a price changes from baseline
168
- if (dataType === 'price_changes' && currentInsights.currentPrice !== this.baselineData.prices[productId].currentPrice) {
176
+ if (dataType === 'price_changes' && currentInsights.currentPrice !== this.baselineData[dataType][productId].currentPrice) {
169
177
  return {
170
178
  type: 'price_change',
171
- oldPrice: this.baselineData.prices[productId].currentPrice,
179
+ oldPrice: this.baselineData[dataType][productId].currentPrice,
172
180
  newPrice: currentInsights.currentPrice
173
181
  };
174
182
  }
@@ -7,16 +7,34 @@ export class TrendyolScraper extends Scraper {
7
7
  this.baseUrl = 'https://www.trendyol.com';
8
8
  }
9
9
 
10
+ _normalizePrice(text) {
11
+ if (!text) return null;
12
+ const cleaned = text.replace(/[^\d,]/g, '').replace(',', '.');
13
+ const num = parseFloat(cleaned);
14
+ return Number.isFinite(num) ? num : null;
15
+ }
16
+
17
+ async _extractFirstText(selectors) {
18
+ for (const selector of selectors) {
19
+ const text = await this.extractText(selector);
20
+ if (text) return text.trim();
21
+ }
22
+ return null;
23
+ }
24
+
10
25
  async getProductPrice(productUrl) {
11
26
  await this.init();
12
27
  await this.goTo(productUrl);
13
28
 
14
- // Example: Extract price. This selector might need adjustment based on actual Trendyol HTML.
15
- const priceSelector = '.pr-new-price'; // Or similar selector for price
16
- const priceText = await this.extractText(priceSelector);
29
+ const priceText = await this._extractFirstText([
30
+ '.prc-dsc',
31
+ '.prc-org',
32
+ '.prc-slg',
33
+ '.pr-new-price'
34
+ ]);
17
35
 
18
36
  await this.close();
19
- return priceText ? parseFloat(priceText.replace(/[^\d,]/g, '').replace(',', '.')) : null;
37
+ return this._normalizePrice(priceText);
20
38
  }
21
39
 
22
40
  async searchProducts(query) {
@@ -39,38 +57,75 @@ export class TrendyolScraper extends Scraper {
39
57
  async getPriceChanges(productUrl) {
40
58
  await this.init();
41
59
  await this.goTo(productUrl);
42
- // Placeholder for logic to detect price changes over time
43
- // This would involve comparing current price with a stored baseline
44
- const currentPrice = await this.getProductPrice(productUrl); // Re-use existing method
60
+
61
+ const priceText = await this._extractFirstText([
62
+ '.prc-dsc',
63
+ '.prc-org',
64
+ '.prc-slg',
65
+ '.pr-new-price'
66
+ ]);
67
+ const currentPrice = this._normalizePrice(priceText);
68
+
45
69
  await this.close();
46
- return { productUrl, currentPrice, status: 'Price change detection not fully implemented yet' };
70
+ return { productUrl, currentPrice, status: 'ok' };
47
71
  }
48
72
 
49
73
  async getBuyboxStatus(productUrl) {
50
74
  await this.init();
51
75
  await this.goTo(productUrl);
52
- // Placeholder for logic to determine buybox status (won/lost)
53
- // This might involve checking specific elements or seller names on the product page
76
+ // Attempt to read seller name and buybox indicator text.
77
+ const sellerText = await this._extractFirstText([
78
+ '[data-testid="seller-name"]',
79
+ '.seller-name-text',
80
+ '.merchant-name'
81
+ ]);
82
+ const buyboxText = await this._extractFirstText([
83
+ '[data-testid="buybox"]',
84
+ '.boutique-name',
85
+ '.buybox'
86
+ ]);
54
87
  await this.close();
55
- return { productUrl, status: 'Buybox status detection not fully implemented yet', won: false };
88
+ return {
89
+ productUrl,
90
+ seller: sellerText,
91
+ buybox: buyboxText,
92
+ won: !!sellerText,
93
+ status: 'ok'
94
+ };
56
95
  }
57
96
 
58
97
  async getStockMovements(productUrl) {
59
98
  await this.init();
60
99
  await this.goTo(productUrl);
61
- // Placeholder for logic to track stock changes
62
- // This might involve monitoring "out of stock" indicators or specific stock counts if available
100
+ const stockText = await this._extractFirstText([
101
+ '.out-of-stock',
102
+ '.sold-out',
103
+ '[data-testid="stock-info"]',
104
+ '.product-stock'
105
+ ]);
106
+ const inStock = stockText ? !/tükendi|stokta yok|tuken/i.test(stockText) : true;
63
107
  await this.close();
64
- return { productUrl, status: 'Stock movement detection not fully implemented yet', inStock: true };
108
+ return { productUrl, status: 'ok', inStock, stockText };
65
109
  }
66
110
 
67
111
  async getProductReviewsAndRatings(productUrl) {
68
112
  await this.init();
69
113
  await this.goTo(productUrl);
70
- // Placeholder for logic to scrape reviews and ratings
71
- // This often involves navigating to review sections or pagination
72
- const reviews = ['Placeholder review 1', 'Placeholder review 2'];
73
- const rating = 4.5; // Placeholder rating
114
+
115
+ const ratingText = await this._extractFirstText([
116
+ '[data-testid="rating-score"]',
117
+ '.rating-score',
118
+ '.pr-rnr-sm-p'
119
+ ]);
120
+ const rating = this._normalizePrice(ratingText);
121
+
122
+ const reviewTexts = await this.extractAllText('.comment-text, .review-text, .rnr-com-tx');
123
+ const reviews = reviewTexts
124
+ .map(t => t?.trim())
125
+ .filter(Boolean)
126
+ .slice(0, 10);
127
+
74
128
  await this.close();
75
- return { productUrl, reviews, rating, status: 'Review and rating scraping not fully implemented yet' };
129
+ return { productUrl, reviews, rating, status: 'ok' };
76
130
  }
131
+ }
package/package.json CHANGED
@@ -1,91 +1,91 @@
1
- {
2
- "name": "vantuz",
3
- "version": "3.3.0",
4
- "description": "Yapay Zeka Destekli E-Ticaret Yönetim Platformu - 7 Pazaryeri + WhatsApp/Telegram",
5
- "type": "module",
6
- "main": "cli.js",
7
- "bin": {
8
- "vantuz": "cli.js",
9
- "vantuz-onboard": "onboard.js"
10
- },
11
- "scripts": {
12
- "start": "node cli.js tui",
13
- "tui": "node cli.js tui",
14
- "config": "node cli.js config",
15
- "status": "node cli.js status",
16
- "onboard": "node onboard.js",
17
- "postinstall": "echo '🐙 Vantuz kuruldu! Başlatmak için: npx vantuz-onboard'",
18
- "test": "node --test",
19
- "lint": "eslint plugins/"
20
- },
21
- "keywords": [
22
- "ecommerce",
23
- "e-ticaret",
24
- "trendyol",
25
- "hepsiburada",
26
- "amazon",
27
- "n11",
28
- "ciceksepeti",
29
- "pttavm",
30
- "pazarama",
31
- "marketplace",
32
- "pazaryeri",
33
- "ai",
34
- "yapay-zeka",
35
- "repricer",
36
- "fiyatlama",
37
- "whatsapp",
38
- "telegram",
39
- "enterprise",
40
- "gateway",
41
- "management",
42
- "cli"
43
- ],
44
- "author": {
45
- "name": "Nuri Can Avşar",
46
- "email": "nuricanavsar2000@gmail.com"
47
- },
48
- "license": "SEE LICENSE IN LICENSE",
49
- "repository": {
50
- "type": "git",
51
- "url": "git+https://github.com/nuricanavsar/vantuz.git"
52
- },
53
- "homepage": "https://vantuz.ai",
54
- "bugs": {
55
- "url": "https://github.com/nuricanavsar/vantuz/issues"
56
- },
57
- "dependencies": {
58
- "axios": "^1.13.5",
59
- "cors": "^2.8.6",
60
- "cron": "^4.4.0",
61
- "dotenv": "^16.0.0",
62
- "express": "^5.2.1",
63
- "openclaw": "^2026.2.9",
64
- "sqlite3": "^5.1.7",
65
- "xml2js": "^0.6.2"
66
- },
67
- "devDependencies": {
68
- "eslint": "^8.0.0",
69
- "playwright": "^1.58.2"
70
- },
71
- "os": [
72
- "darwin",
73
- "linux",
74
- "win32"
75
- ],
76
- "engines": {
77
- "node": ">=18.0.0"
78
- },
79
- "files": [
80
- "cli.js",
81
- "onboard.js",
82
- "core",
83
- "server",
84
- "platforms",
85
- "plugins",
86
- ".openclaw",
87
- "LICENSE",
88
- "README.md",
89
- "DOCS_TR.md"
90
- ]
91
- }
1
+ {
2
+ "name": "vantuz",
3
+ "version": "3.3.1",
4
+ "description": "Yapay Zeka Destekli E-Ticaret Yönetim Platformu - 7 Pazaryeri + WhatsApp/Telegram",
5
+ "type": "module",
6
+ "main": "cli.js",
7
+ "bin": {
8
+ "vantuz": "cli.js",
9
+ "vantuz-onboard": "onboard.js"
10
+ },
11
+ "scripts": {
12
+ "start": "node cli.js tui",
13
+ "tui": "node cli.js tui",
14
+ "config": "node cli.js config",
15
+ "status": "node cli.js status",
16
+ "onboard": "node onboard.js",
17
+ "postinstall": "echo \u0027🐙 Vantuz kuruldu! Başlatmak için: npx vantuz-onboard\u0027",
18
+ "test": "node --test",
19
+ "lint": "eslint plugins/"
20
+ },
21
+ "keywords": [
22
+ "ecommerce",
23
+ "e-ticaret",
24
+ "trendyol",
25
+ "hepsiburada",
26
+ "amazon",
27
+ "n11",
28
+ "ciceksepeti",
29
+ "pttavm",
30
+ "pazarama",
31
+ "marketplace",
32
+ "pazaryeri",
33
+ "ai",
34
+ "yapay-zeka",
35
+ "repricer",
36
+ "fiyatlama",
37
+ "whatsapp",
38
+ "telegram",
39
+ "enterprise",
40
+ "gateway",
41
+ "management",
42
+ "cli"
43
+ ],
44
+ "author": {
45
+ "name": "Nuri Can Avşar",
46
+ "email": "nuricanavsar2000@gmail.com"
47
+ },
48
+ "license": "SEE LICENSE IN LICENSE",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/nuricanavsar/vantuz.git"
52
+ },
53
+ "homepage": "https://vantuz.ai",
54
+ "bugs": {
55
+ "url": "https://github.com/nuricanavsar/vantuz/issues"
56
+ },
57
+ "dependencies": {
58
+ "axios": "^1.13.5",
59
+ "cors": "^2.8.6",
60
+ "cron": "^4.4.0",
61
+ "dotenv": "^16.0.0",
62
+ "express": "^5.2.1",
63
+ "openclaw": "^2026.2.9",
64
+ "playwright": "^1.58.2",
65
+ "sqlite3": "^5.1.7",
66
+ "xml2js": "^0.6.2"
67
+ },
68
+ "devDependencies": {
69
+ "eslint": "^8.0.0"
70
+ },
71
+ "os": [
72
+ "darwin",
73
+ "linux",
74
+ "win32"
75
+ ],
76
+ "engines": {
77
+ "node": "\u003e=18.0.0"
78
+ },
79
+ "files": [
80
+ "cli.js",
81
+ "onboard.js",
82
+ "core",
83
+ "server",
84
+ "platforms",
85
+ "plugins",
86
+ ".openclaw",
87
+ "LICENSE",
88
+ "README.md",
89
+ "DOCS_TR.md"
90
+ ]
91
+ }
@@ -0,0 +1,34 @@
1
+ // plugins/vantuz/platforms/_request.js
2
+ import { setTimeout as sleep } from 'timers/promises';
3
+
4
+ export async function requestWithRetry(axiosInstance, config, options = {}) {
5
+ const {
6
+ retries = 3,
7
+ baseDelayMs = 500,
8
+ maxDelayMs = 5000,
9
+ retryOnStatuses = [429, 500, 502, 503, 504],
10
+ retryOnNetworkError = true
11
+ } = options;
12
+
13
+ let attempt = 0;
14
+ // eslint-disable-next-line no-constant-condition
15
+ while (true) {
16
+ try {
17
+ return await axiosInstance(config);
18
+ } catch (error) {
19
+ const status = error.response?.status;
20
+ const isNetworkError = !error.response;
21
+ const shouldRetry =
22
+ attempt < retries &&
23
+ ((retryOnNetworkError && isNetworkError) || retryOnStatuses.includes(status));
24
+
25
+ if (!shouldRetry) {
26
+ throw error;
27
+ }
28
+
29
+ const delay = Math.min(maxDelayMs, baseDelayMs * Math.pow(2, attempt));
30
+ await sleep(delay);
31
+ attempt += 1;
32
+ }
33
+ }
34
+ }
@@ -3,8 +3,9 @@
3
3
  * developer-docs.amazon.com/sp-api
4
4
  */
5
5
 
6
- import axios from 'axios';
7
- import crypto from 'crypto';
6
+ import axios from 'axios';
7
+ import crypto from 'crypto';
8
+ import { requestWithRetry } from './_request.js';
8
9
 
9
10
  const REGIONS = {
10
11
  eu: {
@@ -29,48 +30,70 @@ export class AmazonAPI {
29
30
  this.clientSecret = config.clientSecret;
30
31
  this.refreshToken = config.refreshToken;
31
32
 
32
- this.regionConfig = REGIONS[this.region];
33
- this.accessToken = null;
34
- this.tokenExpiry = null;
35
- }
36
-
37
- async _getAccessToken() {
33
+ this.regionConfig = REGIONS[this.region];
34
+ this.accessToken = null;
35
+ this.tokenExpiry = null;
36
+ this._tokenPromise = null;
37
+ }
38
+
39
+ async _getAccessToken() {
38
40
  if (this.accessToken && this.tokenExpiry > Date.now()) {
39
41
  return this.accessToken;
40
42
  }
41
-
42
- try {
43
- const response = await axios.post('https://api.amazon.com/auth/o2/token', {
44
- grant_type: 'refresh_token',
45
- refresh_token: this.refreshToken,
46
- client_id: this.clientId,
47
- client_secret: this.clientSecret
48
- });
49
-
50
- this.accessToken = response.data.access_token;
51
- this.tokenExpiry = Date.now() + (response.data.expires_in * 1000) - 60000;
52
- return this.accessToken;
53
- } catch (error) {
54
- throw new Error('Token alınamadı: ' + error.message);
43
+ if (this._tokenPromise) {
44
+ return await this._tokenPromise;
55
45
  }
46
+
47
+ this._tokenPromise = (async () => {
48
+ try {
49
+ const response = await requestWithRetry(axios, {
50
+ method: 'POST',
51
+ url: 'https://api.amazon.com/auth/o2/token',
52
+ data: {
53
+ grant_type: 'refresh_token',
54
+ refresh_token: this.refreshToken,
55
+ client_id: this.clientId,
56
+ client_secret: this.clientSecret
57
+ }
58
+ }, {
59
+ retries: 3,
60
+ baseDelayMs: 500,
61
+ maxDelayMs: 4000
62
+ });
63
+
64
+ this.accessToken = response.data.access_token;
65
+ this.tokenExpiry = Date.now() + (response.data.expires_in * 1000) - 60000;
66
+ return this.accessToken;
67
+ } catch (error) {
68
+ throw new Error('Token alınamadı: ' + error.message);
69
+ } finally {
70
+ this._tokenPromise = null;
71
+ }
72
+ })();
73
+
74
+ return await this._tokenPromise;
56
75
  }
57
76
 
58
77
  async _request(method, path, data = null, params = null) {
59
78
  try {
60
79
  const token = await this._getAccessToken();
61
- const response = await axios({
62
- method,
63
- url: `${this.regionConfig.endpoint}${path}`,
64
- headers: {
65
- 'x-amz-access-token': token,
66
- 'Content-Type': 'application/json'
67
- },
68
- data,
69
- params: {
70
- ...params,
71
- MarketplaceIds: this.regionConfig.marketplace
72
- }
73
- });
80
+ const response = await requestWithRetry(axios, {
81
+ method,
82
+ url: `${this.regionConfig.endpoint}${path}`,
83
+ headers: {
84
+ 'x-amz-access-token': token,
85
+ 'Content-Type': 'application/json'
86
+ },
87
+ data,
88
+ params: {
89
+ ...params,
90
+ MarketplaceIds: this.regionConfig.marketplace
91
+ }
92
+ }, {
93
+ retries: 3,
94
+ baseDelayMs: 500,
95
+ maxDelayMs: 4000
96
+ });
74
97
  return { success: true, data: response.data };
75
98
  } catch (error) {
76
99
  return {
@@ -237,3 +260,4 @@ export const amazonApi = {
237
260
  async updatePrice(sku, price, region) { return instances[region]?.updatePrice(sku, price); },
238
261
  async getOrders(params, region) { return instances[region]?.getOrders(params); }
239
262
  };
263
+
@@ -3,7 +3,8 @@
3
3
  * bayi.ciceksepeti.com
4
4
  */
5
5
 
6
- import axios from 'axios';
6
+ import axios from 'axios';
7
+ import { requestWithRetry } from './_request.js';
7
8
 
8
9
  const BASE_URL = 'https://apis.ciceksepeti.com/api/v1';
9
10
 
@@ -22,14 +23,18 @@ export class CiceksepetiAPI {
22
23
 
23
24
  async _request(method, endpoint, data = null, params = null) {
24
25
  try {
25
- const response = await axios({
26
- method,
27
- url: `${BASE_URL}${endpoint}`,
28
- headers: this._headers(),
29
- data,
30
- params
31
- });
32
- return { success: true, data: response.data };
26
+ const response = await requestWithRetry(axios, {
27
+ method,
28
+ url: `${BASE_URL}${endpoint}`,
29
+ headers: this._headers(),
30
+ data,
31
+ params
32
+ }, {
33
+ retries: 3,
34
+ baseDelayMs: 500,
35
+ maxDelayMs: 4000
36
+ });
37
+ return { success: true, data: response.data };
33
38
  } catch (error) {
34
39
  return {
35
40
  success: false,
@@ -3,7 +3,8 @@
3
3
  * developer.hepsiburada.com
4
4
  */
5
5
 
6
- import axios from 'axios';
6
+ import axios from 'axios';
7
+ import { requestWithRetry } from './_request.js';
7
8
 
8
9
  const BASE_URL = 'https://mpop-sit.hepsiburada.com'; // Production: mpop.hepsiburada.com
9
10
  const LISTING_URL = 'https://listing-external.hepsiburada.com';
@@ -27,14 +28,18 @@ export class HepsiburadaAPI {
27
28
 
28
29
  async _request(method, url, data = null, params = null) {
29
30
  try {
30
- const response = await axios({
31
- method,
32
- url,
33
- headers: this._headers(),
34
- data,
35
- params
36
- });
37
- return { success: true, data: response.data };
31
+ const response = await requestWithRetry(axios, {
32
+ method,
33
+ url,
34
+ headers: this._headers(),
35
+ data,
36
+ params
37
+ }, {
38
+ retries: 3,
39
+ baseDelayMs: 500,
40
+ maxDelayMs: 4000
41
+ });
42
+ return { success: true, data: response.data };
38
43
  } catch (error) {
39
44
  return {
40
45
  success: false,
@@ -133,68 +133,88 @@ export const platformHub = {
133
133
  /**
134
134
  * Çoklu platform fiyat güncelle
135
135
  */
136
- async updatePriceMulti(barcode, price, targetPlatforms = 'all') {
137
- const targets = targetPlatforms === 'all'
138
- ? this.getConnected()
139
- : targetPlatforms.split(',').map(p => PLATFORM_ALIASES[p.trim()]).filter(Boolean);
140
-
141
- const results = {};
142
- for (const platform of targets) {
143
- const api = platforms[platform];
144
- if (api?.isConnected()) {
145
- results[platform] = await api.updatePrice(barcode, price);
146
- }
147
- }
148
- return results;
149
- },
136
+ async updatePriceMulti(barcode, price, targetPlatforms = 'all') {
137
+ const targets = targetPlatforms === 'all'
138
+ ? this.getConnected()
139
+ : targetPlatforms.split(',').map(p => PLATFORM_ALIASES[p.trim()]).filter(Boolean);
140
+
141
+ const results = {};
142
+ const tasks = targets.map(async (platform) => {
143
+ const api = platforms[platform];
144
+ if (!api?.isConnected()) {
145
+ results[platform] = { success: false, error: 'Not connected' };
146
+ return;
147
+ }
148
+ try {
149
+ results[platform] = await api.updatePrice(barcode, price);
150
+ } catch (error) {
151
+ results[platform] = { success: false, error: error.message };
152
+ }
153
+ });
154
+ await Promise.allSettled(tasks);
155
+ return results;
156
+ },
150
157
 
151
158
  /**
152
159
  * Çoklu platform stok güncelle
153
160
  */
154
- async updateStockMulti(barcode, quantity, targetPlatforms = 'all') {
155
- const targets = targetPlatforms === 'all'
156
- ? this.getConnected()
157
- : targetPlatforms.split(',').map(p => PLATFORM_ALIASES[p.trim()]).filter(Boolean);
158
-
159
- const results = {};
160
- for (const platform of targets) {
161
- const api = platforms[platform];
162
- if (api?.isConnected()) {
163
- results[platform] = await api.updateStock(barcode, quantity);
164
- }
165
- }
166
- return results;
167
- },
161
+ async updateStockMulti(barcode, quantity, targetPlatforms = 'all') {
162
+ const targets = targetPlatforms === 'all'
163
+ ? this.getConnected()
164
+ : targetPlatforms.split(',').map(p => PLATFORM_ALIASES[p.trim()]).filter(Boolean);
165
+
166
+ const results = {};
167
+ const tasks = targets.map(async (platform) => {
168
+ const api = platforms[platform];
169
+ if (!api?.isConnected()) {
170
+ results[platform] = { success: false, error: 'Not connected' };
171
+ return;
172
+ }
173
+ try {
174
+ results[platform] = await api.updateStock(barcode, quantity);
175
+ } catch (error) {
176
+ results[platform] = { success: false, error: error.message };
177
+ }
178
+ });
179
+ await Promise.allSettled(tasks);
180
+ return results;
181
+ },
168
182
 
169
183
  /**
170
184
  * Tüm platformlardan sipariş çek
171
185
  */
172
- async getAllOrders(params = {}) {
173
- const connected = this.getConnected();
174
- const allOrders = [];
175
-
176
- const orderPromises = connected.map(async (platform) => {
177
- const api = platforms[platform];
178
- const result = await api.getOrders(params);
179
- if (result.success) {
180
- const orders = result.data.content || result.data.orders || result.data || [];
181
- return orders.map(order => ({
182
- ...order,
183
- _platform: platform,
184
- _icon: this.getIcon(platform)
185
- }));
186
- }
187
- return [];
188
- });
189
-
190
- const results = await Promise.all(orderPromises);
191
- results.forEach(platformOrders => {
192
- allOrders.push(...platformOrders);
193
- });
194
-
195
- // Tarihe göre sırala
196
- allOrders.sort((a, b) => new Date(b.createdDate || b.orderDate) - new Date(a.createdDate || a.orderDate));
197
- return allOrders;
186
+ async getAllOrders(params = {}) {
187
+ const connected = this.getConnected();
188
+ const allOrders = [];
189
+
190
+ const orderPromises = connected.map(async (platform) => {
191
+ const api = platforms[platform];
192
+ try {
193
+ const result = await api.getOrders(params);
194
+ if (result?.success) {
195
+ const orders = result.data.content || result.data.orders || result.data || [];
196
+ return orders.map(order => ({
197
+ ...order,
198
+ _platform: platform,
199
+ _icon: this.getIcon(platform)
200
+ }));
201
+ }
202
+ return [];
203
+ } catch {
204
+ return [];
205
+ }
206
+ });
207
+
208
+ const results = await Promise.allSettled(orderPromises);
209
+ results.forEach(platformOrders => {
210
+ if (platformOrders.status === 'fulfilled') {
211
+ allOrders.push(...platformOrders.value);
212
+ }
213
+ });
214
+
215
+ // Tarihe göre sırala
216
+ allOrders.sort((a, b) => new Date(b.createdDate || b.orderDate) - new Date(a.createdDate || a.orderDate));
217
+ return allOrders;
198
218
  }
199
219
  };
200
220
 
@@ -3,8 +3,9 @@
3
3
  * api.n11.com/ws/*.wsdl
4
4
  */
5
5
 
6
- import axios from 'axios';
7
- import { parseStringPromise, Builder } from 'xml2js';
6
+ import axios from 'axios';
7
+ import { parseStringPromise, Builder } from 'xml2js';
8
+ import { requestWithRetry } from './_request.js';
8
9
 
9
10
  const WSDL_URLS = {
10
11
  product: 'https://api.n11.com/ws/ProductService.wsdl',
@@ -50,12 +51,19 @@ export class N11API {
50
51
  async _soapRequest(service, method, body = '') {
51
52
  try {
52
53
  const envelope = this._buildSoapEnvelope(service, method, body);
53
- const response = await axios.post(SOAP_ENDPOINTS[service], envelope, {
54
- headers: {
55
- 'Content-Type': 'text/xml; charset=utf-8',
56
- 'SOAPAction': `${method}`
57
- }
58
- });
54
+ const response = await requestWithRetry(axios, {
55
+ method: 'POST',
56
+ url: SOAP_ENDPOINTS[service],
57
+ data: envelope,
58
+ headers: {
59
+ 'Content-Type': 'text/xml; charset=utf-8',
60
+ 'SOAPAction': `${method}`
61
+ }
62
+ }, {
63
+ retries: 3,
64
+ baseDelayMs: 500,
65
+ maxDelayMs: 4000
66
+ });
59
67
 
60
68
  const result = await parseStringPromise(response.data, { explicitArray: false });
61
69
  const bodyKey = Object.keys(result['SOAP-ENV:Envelope']['SOAP-ENV:Body'])[0];
@@ -3,7 +3,8 @@
3
3
  * sellers.pazarama.com
4
4
  */
5
5
 
6
- import axios from 'axios';
6
+ import axios from 'axios';
7
+ import { requestWithRetry } from './_request.js';
7
8
 
8
9
  const BASE_URL = 'https://isortagimapi.pazarama.com/api';
9
10
  const AUTH_URL = 'https://isortagimapi.pazarama.com/oauth/token';
@@ -22,11 +23,19 @@ export class PazaramaAPI {
22
23
  }
23
24
 
24
25
  try {
25
- const response = await axios.post(AUTH_URL, {
26
- grant_type: 'client_credentials',
27
- client_id: this.clientId,
28
- client_secret: this.clientSecret
29
- });
26
+ const response = await requestWithRetry(axios, {
27
+ method: 'POST',
28
+ url: AUTH_URL,
29
+ data: {
30
+ grant_type: 'client_credentials',
31
+ client_id: this.clientId,
32
+ client_secret: this.clientSecret
33
+ }
34
+ }, {
35
+ retries: 3,
36
+ baseDelayMs: 500,
37
+ maxDelayMs: 4000
38
+ });
30
39
 
31
40
  this.accessToken = response.data.access_token;
32
41
  this.tokenExpiry = Date.now() + (response.data.expires_in * 1000) - 60000;
@@ -39,17 +48,21 @@ export class PazaramaAPI {
39
48
  async _request(method, endpoint, data = null, params = null) {
40
49
  try {
41
50
  const token = await this._getAccessToken();
42
- const response = await axios({
43
- method,
44
- url: `${BASE_URL}${endpoint}`,
45
- headers: {
46
- 'Authorization': `Bearer ${token}`,
47
- 'Content-Type': 'application/json'
48
- },
49
- data,
50
- params
51
- });
52
- return { success: true, data: response.data };
51
+ const response = await requestWithRetry(axios, {
52
+ method,
53
+ url: `${BASE_URL}${endpoint}`,
54
+ headers: {
55
+ 'Authorization': `Bearer ${token}`,
56
+ 'Content-Type': 'application/json'
57
+ },
58
+ data,
59
+ params
60
+ }, {
61
+ retries: 3,
62
+ baseDelayMs: 500,
63
+ maxDelayMs: 4000
64
+ });
65
+ return { success: true, data: response.data };
53
66
  } catch (error) {
54
67
  return {
55
68
  success: false,
@@ -3,7 +3,8 @@
3
3
  * pttavm.com/entegrasyon
4
4
  */
5
5
 
6
- import axios from 'axios';
6
+ import axios from 'axios';
7
+ import { requestWithRetry } from './_request.js';
7
8
 
8
9
  const BASE_URL = 'https://api.pttavm.com/v1';
9
10
 
@@ -24,14 +25,18 @@ export class PttavmAPI {
24
25
 
25
26
  async _request(method, endpoint, data = null, params = null) {
26
27
  try {
27
- const response = await axios({
28
- method,
29
- url: `${BASE_URL}${endpoint}`,
30
- headers: this._headers(),
31
- data,
32
- params
33
- });
34
- return { success: true, data: response.data };
28
+ const response = await requestWithRetry(axios, {
29
+ method,
30
+ url: `${BASE_URL}${endpoint}`,
31
+ headers: this._headers(),
32
+ data,
33
+ params
34
+ }, {
35
+ retries: 3,
36
+ baseDelayMs: 500,
37
+ maxDelayMs: 4000
38
+ });
39
+ return { success: true, data: response.data };
35
40
  } catch (error) {
36
41
  return {
37
42
  success: false,
@@ -8,8 +8,9 @@
8
8
  * Rate Limit: 50 req / 10 sec per endpoint
9
9
  */
10
10
 
11
- import axios from 'axios';
12
- import { log } from '../../../core/ai-provider.js';
11
+ import axios from 'axios';
12
+ import { log } from '../../../core/ai-provider.js';
13
+ import { requestWithRetry } from './_request.js';
13
14
 
14
15
  const BASE_URL = 'https://apigw.trendyol.com/integration';
15
16
  const STAGE_URL = 'https://stageapigw.trendyol.com/integration';
@@ -56,15 +57,19 @@ export class TrendyolAPI {
56
57
  headers['Content-Type'] = 'application/json';
57
58
  }
58
59
 
59
- const response = await this.client({
60
- method,
61
- url,
62
- headers,
63
- data,
64
- params,
65
- timeout: 30000
66
- });
67
- return { success: true, data: response.data };
60
+ const response = await requestWithRetry(this.client, {
61
+ method,
62
+ url,
63
+ headers,
64
+ data,
65
+ params,
66
+ timeout: 30000
67
+ }, {
68
+ retries: 3,
69
+ baseDelayMs: 500,
70
+ maxDelayMs: 4000
71
+ });
72
+ return { success: true, data: response.data };
68
73
  } catch (error) {
69
74
  const errorMsg = error.response?.data?.errors?.[0]?.message || error.message;
70
75
  const statusCode = error.response?.status;