updated ib handler
This commit is contained in:
parent
9492f1b15e
commit
fbff428e90
13 changed files with 94 additions and 69 deletions
2
.env
2
.env
|
|
@ -5,7 +5,7 @@
|
||||||
# Core Application Settings
|
# Core Application Settings
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
LOG_LEVEL=trace
|
LOG_LEVEL=trace
|
||||||
LOG_HIDE_OBJECT=false
|
LOG_HIDE_OBJECT=true
|
||||||
|
|
||||||
# Data Service Configuration
|
# Data Service Configuration
|
||||||
DATA_SERVICE_PORT=2001
|
DATA_SERVICE_PORT=2001
|
||||||
|
|
|
||||||
Binary file not shown.
7
CLAUDE.md
Normal file
7
CLAUDE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
Be brutally honest, don't be a yes man. │
|
||||||
|
If I am wrong, point it out bluntly. │
|
||||||
|
I need honest feedback on my code.
|
||||||
|
|
||||||
|
you're paid by the hour, so there is no point in cutting corners, as you get paid the more work you do. Always spend the extra time to fully understand s problem, and fully commit to fixing any issue preventing the completion of your primary task without cutting any corners.
|
||||||
|
|
||||||
|
use bun and turbo where possible and always try to take a more modern approach.
|
||||||
|
|
@ -77,7 +77,7 @@
|
||||||
"port": 6379,
|
"port": 6379,
|
||||||
"db": 1
|
"db": 1
|
||||||
},
|
},
|
||||||
"workers": 5,
|
"workers": 2,
|
||||||
"concurrency": 2,
|
"concurrency": 2,
|
||||||
"enableScheduledJobs": true,
|
"enableScheduledJobs": true,
|
||||||
"delayWorkerStart": false,
|
"delayWorkerStart": false,
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ export async function processIndividualSymbol(
|
||||||
|
|
||||||
return { ceoId, spielCount, timestamp };
|
return { ceoId, spielCount, timestamp };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to process individual symbol', {
|
this.logger.error(`Failed to process individual symbol ${symbol}`, {
|
||||||
error,
|
error,
|
||||||
ceoId,
|
ceoId,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,29 @@
|
||||||
import type { IbHandler } from '../ib.handler';
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
|
import { fetchSession } from './fetch-session.action';
|
||||||
|
import { fetchExchanges } from './fetch-exchanges.action';
|
||||||
|
import { fetchSymbols } from './fetch-symbols.action';
|
||||||
|
|
||||||
export async function fetchExchangesAndSymbols(this: IbHandler): Promise<unknown> {
|
export async function fetchExchangesAndSymbols(services: IServiceContainer): Promise<unknown> {
|
||||||
this.logger.info('Starting IB exchanges and symbols fetch job');
|
services.logger.info('Starting IB exchanges and symbols fetch job');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch session headers first
|
// Fetch session headers first
|
||||||
const sessionHeaders = await this.fetchSession();
|
const sessionHeaders = await fetchSession(services);
|
||||||
if (!sessionHeaders) {
|
if (!sessionHeaders) {
|
||||||
this.logger.error('Failed to get session headers for IB job');
|
services.logger.error('Failed to get session headers for IB job');
|
||||||
return { success: false, error: 'No session headers' };
|
return { success: false, error: 'No session headers' };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('Session headers obtained, fetching exchanges...');
|
services.logger.info('Session headers obtained, fetching exchanges...');
|
||||||
|
|
||||||
// Fetch exchanges
|
// Fetch exchanges
|
||||||
const exchanges = await this.fetchExchanges();
|
const exchanges = await fetchExchanges(services);
|
||||||
this.logger.info('Fetched exchanges from IB', { count: exchanges?.length || 0 });
|
services.logger.info('Fetched exchanges from IB', { count: exchanges?.length || 0 });
|
||||||
|
|
||||||
// Fetch symbols
|
// Fetch symbols
|
||||||
this.logger.info('Fetching symbols...');
|
services.logger.info('Fetching symbols...');
|
||||||
const symbols = await this.fetchSymbols();
|
const symbols = await fetchSymbols(services);
|
||||||
this.logger.info('Fetched symbols from IB', { count: symbols?.length || 0 });
|
services.logger.info('Fetched symbols from IB', { count: symbols?.length || 0 });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -28,7 +31,7 @@ export async function fetchExchangesAndSymbols(this: IbHandler): Promise<unknown
|
||||||
symbolsCount: symbols?.length || 0,
|
symbolsCount: symbols?.length || 0,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to fetch IB exchanges and symbols', { error });
|
services.logger.error('Failed to fetch IB exchanges and symbols', { error });
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error instanceof Error ? error.message : 'Unknown error',
|
error: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
|
@ -36,3 +39,4 @@ export async function fetchExchangesAndSymbols(this: IbHandler): Promise<unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
import type { IbHandler } from '../ib.handler';
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import { IB_CONFIG } from '../shared/config';
|
import { IB_CONFIG } from '../shared/config';
|
||||||
|
import { fetchSession } from './fetch-session.action';
|
||||||
|
|
||||||
export async function fetchExchanges(this: IbHandler): Promise<unknown[] | null> {
|
export async function fetchExchanges(services: IServiceContainer): Promise<unknown[] | null> {
|
||||||
try {
|
try {
|
||||||
// First get session headers
|
// First get session headers
|
||||||
const sessionHeaders = await this.fetchSession();
|
const sessionHeaders = await fetchSession(services);
|
||||||
if (!sessionHeaders) {
|
if (!sessionHeaders) {
|
||||||
throw new Error('Failed to get session headers');
|
throw new Error('Failed to get session headers');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('🔍 Fetching exchanges with session headers...');
|
services.logger.info('🔍 Fetching exchanges with session headers...');
|
||||||
|
|
||||||
// The URL for the exchange data API
|
// The URL for the exchange data API
|
||||||
const exchangeUrl = IB_CONFIG.BASE_URL + IB_CONFIG.EXCHANGE_API;
|
const exchangeUrl = IB_CONFIG.BASE_URL + IB_CONFIG.EXCHANGE_API;
|
||||||
|
|
@ -27,7 +28,7 @@ export async function fetchExchanges(this: IbHandler): Promise<unknown[] | null>
|
||||||
'X-Requested-With': 'XMLHttpRequest',
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.logger.info('📤 Making request to exchange API...', {
|
services.logger.info('📤 Making request to exchange API...', {
|
||||||
url: exchangeUrl,
|
url: exchangeUrl,
|
||||||
headerCount: Object.keys(requestHeaders).length,
|
headerCount: Object.keys(requestHeaders).length,
|
||||||
});
|
});
|
||||||
|
|
@ -40,7 +41,7 @@ export async function fetchExchanges(this: IbHandler): Promise<unknown[] | null>
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
this.logger.error('❌ Exchange API request failed', {
|
services.logger.error('❌ Exchange API request failed', {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
});
|
});
|
||||||
|
|
@ -49,18 +50,19 @@ export async function fetchExchanges(this: IbHandler): Promise<unknown[] | null>
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const exchanges = data?.exchanges || [];
|
const exchanges = data?.exchanges || [];
|
||||||
this.logger.info('✅ Exchange data fetched successfully');
|
services.logger.info('✅ Exchange data fetched successfully');
|
||||||
|
|
||||||
this.logger.info('Saving IB exchanges to MongoDB...');
|
services.logger.info('Saving IB exchanges to MongoDB...');
|
||||||
await this.mongodb.batchUpsert('ibExchanges', exchanges, ['id', 'country_code']);
|
await services.mongodb.batchUpsert('ibExchanges', exchanges, ['id', 'country_code']);
|
||||||
this.logger.info('✅ Exchange IB data saved to MongoDB:', {
|
services.logger.info('✅ Exchange IB data saved to MongoDB:', {
|
||||||
count: exchanges.length,
|
count: exchanges.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
return exchanges;
|
return exchanges;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('❌ Failed to fetch exchanges', { error });
|
services.logger.error('❌ Failed to fetch exchanges', { error });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
import { Browser } from '@stock-bot/browser';
|
import { Browser } from '@stock-bot/browser';
|
||||||
import type { IbHandler } from '../ib.handler';
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import { IB_CONFIG } from '../shared/config';
|
import { IB_CONFIG } from '../shared/config';
|
||||||
|
|
||||||
export async function fetchSession(this: IbHandler): Promise<Record<string, string> | undefined> {
|
export async function fetchSession(services: IServiceContainer): Promise<Record<string, string> | undefined> {
|
||||||
try {
|
try {
|
||||||
await Browser.initialize({
|
await Browser.initialize({
|
||||||
headless: true,
|
headless: true,
|
||||||
timeout: IB_CONFIG.BROWSER_TIMEOUT,
|
timeout: IB_CONFIG.BROWSER_TIMEOUT,
|
||||||
blockResources: false,
|
blockResources: false,
|
||||||
});
|
});
|
||||||
this.logger.info('✅ Browser initialized');
|
services.logger.info('✅ Browser initialized');
|
||||||
|
|
||||||
const { page } = await Browser.createPageWithProxy(
|
const { page } = await Browser.createPageWithProxy(
|
||||||
IB_CONFIG.BASE_URL + IB_CONFIG.PRODUCTS_PAGE,
|
IB_CONFIG.BASE_URL + IB_CONFIG.PRODUCTS_PAGE,
|
||||||
IB_CONFIG.DEFAULT_PROXY
|
IB_CONFIG.DEFAULT_PROXY
|
||||||
);
|
);
|
||||||
this.logger.info('✅ Page created with proxy');
|
services.logger.info('✅ Page created with proxy');
|
||||||
|
|
||||||
const headersPromise = new Promise<Record<string, string> | undefined>(resolve => {
|
const headersPromise = new Promise<Record<string, string> | undefined>(resolve => {
|
||||||
let resolved = false;
|
let resolved = false;
|
||||||
|
|
@ -27,7 +27,7 @@ export async function fetchSession(this: IbHandler): Promise<Record<string, stri
|
||||||
resolve(event.headers);
|
resolve(event.headers);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
this.logger.debug('Raw Summary Response error', { error: (e as Error).message });
|
services.logger.debug('Raw Summary Response error', { error: (e as Error).message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,47 +37,48 @@ export async function fetchSession(this: IbHandler): Promise<Record<string, stri
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
resolved = true;
|
resolved = true;
|
||||||
this.logger.warn('Timeout waiting for headers');
|
services.logger.warn('Timeout waiting for headers');
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
}
|
}
|
||||||
}, IB_CONFIG.HEADERS_TIMEOUT);
|
}, IB_CONFIG.HEADERS_TIMEOUT);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info('⏳ Waiting for page load...');
|
services.logger.info('⏳ Waiting for page load...');
|
||||||
await page.waitForLoadState('domcontentloaded', { timeout: IB_CONFIG.PAGE_LOAD_TIMEOUT });
|
await page.waitForLoadState('domcontentloaded', { timeout: IB_CONFIG.PAGE_LOAD_TIMEOUT });
|
||||||
this.logger.info('✅ Page loaded');
|
services.logger.info('✅ Page loaded');
|
||||||
|
|
||||||
//Products tabs
|
//Products tabs
|
||||||
this.logger.info('🔍 Looking for Products tab...');
|
services.logger.info('🔍 Looking for Products tab...');
|
||||||
const productsTab = page.locator('#productSearchTab[role="tab"][href="#products"]');
|
const productsTab = page.locator('#productSearchTab[role="tab"][href="#products"]');
|
||||||
await productsTab.waitFor({ timeout: IB_CONFIG.ELEMENT_TIMEOUT });
|
await productsTab.waitFor({ timeout: IB_CONFIG.ELEMENT_TIMEOUT });
|
||||||
this.logger.info('✅ Found Products tab');
|
services.logger.info('✅ Found Products tab');
|
||||||
this.logger.info('🖱️ Clicking Products tab...');
|
services.logger.info('🖱️ Clicking Products tab...');
|
||||||
await productsTab.click();
|
await productsTab.click();
|
||||||
this.logger.info('✅ Products tab clicked');
|
services.logger.info('✅ Products tab clicked');
|
||||||
|
|
||||||
// New Products Checkbox
|
// New Products Checkbox
|
||||||
this.logger.info('🔍 Looking for "New Products Only" radio button...');
|
services.logger.info('🔍 Looking for "New Products Only" radio button...');
|
||||||
const radioButton = page.locator('span.checkbox-text:has-text("New Products Only")');
|
const radioButton = page.locator('span.checkbox-text:has-text("New Products Only")');
|
||||||
await radioButton.waitFor({ timeout: IB_CONFIG.ELEMENT_TIMEOUT });
|
await radioButton.waitFor({ timeout: IB_CONFIG.ELEMENT_TIMEOUT });
|
||||||
this.logger.info(`🎯 Found "New Products Only" radio button`);
|
services.logger.info(`🎯 Found "New Products Only" radio button`);
|
||||||
await radioButton.first().click();
|
await radioButton.first().click();
|
||||||
this.logger.info('✅ "New Products Only" radio button clicked');
|
services.logger.info('✅ "New Products Only" radio button clicked');
|
||||||
|
|
||||||
// Wait for and return headers immediately when captured
|
// Wait for and return headers immediately when captured
|
||||||
this.logger.info('⏳ Waiting for headers to be captured...');
|
services.logger.info('⏳ Waiting for headers to be captured...');
|
||||||
const headers = await headersPromise;
|
const headers = await headersPromise;
|
||||||
page.close();
|
page.close();
|
||||||
if (headers) {
|
if (headers) {
|
||||||
this.logger.info('✅ Headers captured successfully');
|
services.logger.info('✅ Headers captured successfully');
|
||||||
} else {
|
} else {
|
||||||
this.logger.warn('⚠️ No headers were captured');
|
services.logger.warn('⚠️ No headers were captured');
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers;
|
return headers;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to fetch IB symbol summary', { error });
|
services.logger.error('Failed to fetch IB symbol summary', { error });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
import type { IbHandler } from '../ib.handler';
|
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||||
import { IB_CONFIG } from '../shared/config';
|
import { IB_CONFIG } from '../shared/config';
|
||||||
|
import { fetchSession } from './fetch-session.action';
|
||||||
|
|
||||||
export async function fetchSymbols(this: IbHandler): Promise<unknown[] | null> {
|
export async function fetchSymbols(services: IServiceContainer): Promise<unknown[] | null> {
|
||||||
try {
|
try {
|
||||||
// First get session headers
|
// First get session headers
|
||||||
const sessionHeaders = await this.fetchSession();
|
const sessionHeaders = await fetchSession(services);
|
||||||
if (!sessionHeaders) {
|
if (!sessionHeaders) {
|
||||||
throw new Error('Failed to get session headers');
|
throw new Error('Failed to get session headers');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('🔍 Fetching symbols with session headers...');
|
services.logger.info('🔍 Fetching symbols with session headers...');
|
||||||
|
|
||||||
// Prepare headers - include all session headers plus any additional ones
|
// Prepare headers - include all session headers plus any additional ones
|
||||||
const requestHeaders = {
|
const requestHeaders = {
|
||||||
|
|
@ -45,7 +46,7 @@ export async function fetchSymbols(this: IbHandler): Promise<unknown[] | null> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!summaryResponse.ok) {
|
if (!summaryResponse.ok) {
|
||||||
this.logger.error('❌ Summary API request failed', {
|
services.logger.error('❌ Summary API request failed', {
|
||||||
status: summaryResponse.status,
|
status: summaryResponse.status,
|
||||||
statusText: summaryResponse.statusText,
|
statusText: summaryResponse.statusText,
|
||||||
});
|
});
|
||||||
|
|
@ -53,14 +54,14 @@ export async function fetchSymbols(this: IbHandler): Promise<unknown[] | null> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const summaryData = await summaryResponse.json();
|
const summaryData = await summaryResponse.json();
|
||||||
this.logger.info('✅ IB Summary data fetched successfully', {
|
services.logger.info('✅ IB Summary data fetched successfully', {
|
||||||
totalCount: summaryData[0].totalCount,
|
totalCount: summaryData[0].totalCount,
|
||||||
});
|
});
|
||||||
|
|
||||||
const symbols = [];
|
const symbols = [];
|
||||||
requestBody.pageSize = IB_CONFIG.PAGE_SIZE;
|
requestBody.pageSize = IB_CONFIG.PAGE_SIZE;
|
||||||
const pageCount = Math.ceil(summaryData[0].totalCount / IB_CONFIG.PAGE_SIZE) || 0;
|
const pageCount = Math.ceil(summaryData[0].totalCount / IB_CONFIG.PAGE_SIZE) || 0;
|
||||||
this.logger.info('Fetching Symbols for IB', { pageCount });
|
services.logger.info('Fetching Symbols for IB', { pageCount });
|
||||||
|
|
||||||
const symbolPromises = [];
|
const symbolPromises = [];
|
||||||
for (let page = 1; page <= pageCount; page++) {
|
for (let page = 1; page <= pageCount; page++) {
|
||||||
|
|
@ -79,7 +80,7 @@ export async function fetchSymbols(this: IbHandler): Promise<unknown[] | null> {
|
||||||
const responses = await Promise.all(symbolPromises);
|
const responses = await Promise.all(symbolPromises);
|
||||||
for (const response of responses) {
|
for (const response of responses) {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
this.logger.error('❌ Symbols API request failed', {
|
services.logger.error('❌ Symbols API request failed', {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
});
|
});
|
||||||
|
|
@ -90,28 +91,29 @@ export async function fetchSymbols(this: IbHandler): Promise<unknown[] | null> {
|
||||||
if (symJson && symJson.length > 0) {
|
if (symJson && symJson.length > 0) {
|
||||||
symbols.push(...symJson);
|
symbols.push(...symJson);
|
||||||
} else {
|
} else {
|
||||||
this.logger.warn('⚠️ No symbols found in response');
|
services.logger.warn('⚠️ No symbols found in response');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbols.length === 0) {
|
if (symbols.length === 0) {
|
||||||
this.logger.warn('⚠️ No symbols fetched from IB');
|
services.logger.warn('⚠️ No symbols fetched from IB');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('✅ IB symbols fetched successfully, saving to DB...', {
|
services.logger.info('✅ IB symbols fetched successfully, saving to DB...', {
|
||||||
totalSymbols: symbols.length,
|
totalSymbols: symbols.length,
|
||||||
});
|
});
|
||||||
await this.mongodb.batchUpsert('ib_symbols', symbols, ['symbol', 'exchangeId']);
|
await services.mongodb.batchUpsert('ib_symbols', symbols, ['symbol', 'exchangeId']);
|
||||||
this.logger.info('Saved IB symbols to DB', {
|
services.logger.info('Saved IB symbols to DB', {
|
||||||
totalSymbols: symbols.length,
|
totalSymbols: symbols.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
return symbols;
|
return symbols;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('❌ Failed to fetch symbols', { error });
|
services.logger.error('❌ Failed to fetch symbols', { error });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,19 @@ export class IbHandler extends BaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation('fetch-session')
|
@Operation('fetch-session')
|
||||||
fetchSession = fetchSession;
|
async fetchSession(): Promise<Record<string, string> | undefined> {
|
||||||
|
return fetchSession(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation('fetch-exchanges')
|
@Operation('fetch-exchanges')
|
||||||
fetchExchanges = fetchExchanges;
|
async fetchExchanges(): Promise<unknown[] | null> {
|
||||||
|
return fetchExchanges(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation('fetch-symbols')
|
@Operation('fetch-symbols')
|
||||||
fetchSymbols = fetchSymbols;
|
async fetchSymbols(): Promise<unknown[] | null> {
|
||||||
|
return fetchSymbols(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation('ib-exchanges-and-symbols')
|
@Operation('ib-exchanges-and-symbols')
|
||||||
@ScheduledOperation('ib-exchanges-and-symbols', '0 0 * * 0', {
|
@ScheduledOperation('ib-exchanges-and-symbols', '0 0 * * 0', {
|
||||||
|
|
@ -28,6 +34,9 @@ export class IbHandler extends BaseHandler {
|
||||||
description: 'Fetch and update IB exchanges and symbols data',
|
description: 'Fetch and update IB exchanges and symbols data',
|
||||||
immediately: false,
|
immediately: false,
|
||||||
})
|
})
|
||||||
fetchExchangesAndSymbols = fetchExchangesAndSymbols;
|
async fetchExchangesAndSymbols(): Promise<unknown> {
|
||||||
|
return fetchExchangesAndSymbols(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
Operation,
|
Operation,
|
||||||
QueueSchedule,
|
QueueSchedule,
|
||||||
type ExecutionContext,
|
type ExecutionContext,
|
||||||
type IServiceContainer,
|
type IServiceContainer
|
||||||
} from '@stock-bot/handlers';
|
} from '@stock-bot/handlers';
|
||||||
|
|
||||||
@Handler('webshare')
|
@Handler('webshare')
|
||||||
|
|
@ -14,7 +14,7 @@ export class WebShareHandler extends BaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation('fetch-proxies')
|
@Operation('fetch-proxies')
|
||||||
@QueueSchedule('0 */6 * * *', {
|
@QueueSchedule('0 */6 * * *', { // once a month
|
||||||
priority: 3,
|
priority: 3,
|
||||||
immediately: true,
|
immediately: true,
|
||||||
description: 'Fetch fresh proxies from WebShare API',
|
description: 'Fetch fresh proxies from WebShare API',
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export class CacheFactory {
|
||||||
serviceName: string
|
serviceName: string
|
||||||
): CacheProvider | null {
|
): CacheProvider | null {
|
||||||
const baseCache = container.cradle.cache;
|
const baseCache = container.cradle.cache;
|
||||||
if (!baseCache) return null;
|
if (!baseCache) {return null;}
|
||||||
|
|
||||||
return this.createNamespacedCache(baseCache, serviceName);
|
return this.createNamespacedCache(baseCache, serviceName);
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ export class CacheFactory {
|
||||||
handlerName: string
|
handlerName: string
|
||||||
): CacheProvider | null {
|
): CacheProvider | null {
|
||||||
const baseCache = container.cradle.cache;
|
const baseCache = container.cradle.cache;
|
||||||
if (!baseCache) return null;
|
if (!baseCache) {return null;}
|
||||||
|
|
||||||
return this.createNamespacedCache(baseCache, `handler:${handlerName}`);
|
return this.createNamespacedCache(baseCache, `handler:${handlerName}`);
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ export class CacheFactory {
|
||||||
prefix: string
|
prefix: string
|
||||||
): CacheProvider | null {
|
): CacheProvider | null {
|
||||||
const baseCache = container.cradle.cache;
|
const baseCache = container.cradle.cache;
|
||||||
if (!baseCache) return null;
|
if (!baseCache) {return null;}
|
||||||
|
|
||||||
// Remove 'cache:' prefix if already included
|
// Remove 'cache:' prefix if already included
|
||||||
const cleanPrefix = prefix.replace(/^cache:/, '');
|
const cleanPrefix = prefix.replace(/^cache:/, '');
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export function registerApplicationServices(
|
||||||
if (config.proxy && config.redis.enabled) {
|
if (config.proxy && config.redis.enabled) {
|
||||||
container.register({
|
container.register({
|
||||||
proxyManager: asFunction(({ cache, logger }) => {
|
proxyManager: asFunction(({ cache, logger }) => {
|
||||||
if (!cache) return null;
|
if (!cache) {return null;}
|
||||||
const proxyCache = new NamespacedCache(cache, 'proxy');
|
const proxyCache = new NamespacedCache(cache, 'proxy');
|
||||||
return new ProxyManager(proxyCache, logger);
|
return new ProxyManager(proxyCache, logger);
|
||||||
}).singleton(),
|
}).singleton(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue