init
This commit is contained in:
21
parser/scripts/check-session.js
Normal file
21
parser/scripts/check-session.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const config = require('../src/config');
|
||||
const { launchBrowser, createContext } = require('../src/session');
|
||||
const { fetchTargetHtmlWithSession } = require('../src/fetcher');
|
||||
|
||||
(async () => {
|
||||
const browser = await launchBrowser({ headless: config.headless });
|
||||
const context = await createContext({ browser, sessionFile: config.sessionFile });
|
||||
|
||||
const result = await fetchTargetHtmlWithSession({ context, config, targetUrl: config.targetUrl });
|
||||
console.log('HTTP status:', result.status);
|
||||
console.log('Current URL:', result.finalUrl);
|
||||
console.log('Title:', result.title);
|
||||
console.log('Auth required:', result.authRequired);
|
||||
console.log('\nHTML preview:\n');
|
||||
console.log(result.html.slice(0, 3000));
|
||||
|
||||
await browser.close();
|
||||
})().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
71
parser/scripts/debug-fetch.js
Normal file
71
parser/scripts/debug-fetch.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
const cheerio = require('cheerio');
|
||||
const config = require('../src/config');
|
||||
const { launchBrowser, createContext } = require('../src/session');
|
||||
const { fetchTargetHtmlWithSession, buildForecastUrl } = require('../src/fetcher');
|
||||
const { parseItemsFromHtml, countForecastRows } = require('../src/parser');
|
||||
const { sanitizeFilePart } = require('../src/utils');
|
||||
|
||||
async function main() {
|
||||
const botArg = process.argv[2] || 'raketafon';
|
||||
const activeTab = Number(process.argv[3] || 1);
|
||||
const currentPage = Number(process.argv[4] || 1);
|
||||
const botUrl = botArg.startsWith('http') ? botArg : `https://alpinbet.com/dispatch/antigol/${botArg}`;
|
||||
const targetUrl = buildForecastUrl(botUrl, {
|
||||
activeTab,
|
||||
perPage: config.forecastPerPage,
|
||||
page: currentPage
|
||||
});
|
||||
|
||||
const browser = await launchBrowser({ headless: true });
|
||||
const context = await createContext({ browser, sessionFile: config.sessionFile });
|
||||
|
||||
try {
|
||||
const result = await fetchTargetHtmlWithSession({ context, config, targetUrl });
|
||||
const items = parseItemsFromHtml(result.html, config.selectors);
|
||||
const rows = countForecastRows(result.html, config.selectors);
|
||||
const $ = cheerio.load(result.html);
|
||||
const htmlFile = path.join(
|
||||
config.htmlSnapshotDir,
|
||||
`${sanitizeFilePart(botArg)}_tab-${activeTab}_page-${currentPage}_manual-debug.html`
|
||||
);
|
||||
|
||||
await fs.mkdir(config.htmlSnapshotDir, { recursive: true });
|
||||
await fs.writeFile(htmlFile, result.html, 'utf8');
|
||||
|
||||
console.log('status:', result.status);
|
||||
console.log('title:', result.title);
|
||||
console.log('url:', result.finalUrl);
|
||||
console.log('rows:', rows);
|
||||
console.log('parsedItems:', items.length);
|
||||
console.log('htmlFile:', htmlFile);
|
||||
|
||||
const rowCandidates = $('.table-link')
|
||||
.slice(0, 5)
|
||||
.toArray()
|
||||
.map((element, index) => {
|
||||
const $row = $(element);
|
||||
return {
|
||||
index,
|
||||
classes: $row.attr('class') || '',
|
||||
text: $row.text().replace(/\s+/g, ' ').trim().slice(0, 200),
|
||||
hasLink: $row.find('.cell-team-title a').length > 0,
|
||||
hasFavorite: $row.find('.rating-mailings__favorite').length > 0,
|
||||
hasCoefficient: $row.find('.cell-coefficient__total, .cell-coefficient').length > 0
|
||||
};
|
||||
});
|
||||
|
||||
console.log('tableLinkCandidates:', JSON.stringify(rowCandidates, null, 2));
|
||||
if (items[0]) {
|
||||
console.log('firstItem:', JSON.stringify(items[0], null, 2));
|
||||
}
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
25
parser/scripts/healthcheck.js
Normal file
25
parser/scripts/healthcheck.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const fs = require('fs');
|
||||
const config = require('../src/config');
|
||||
|
||||
try {
|
||||
const raw = fs.readFileSync(config.heartbeatFile, 'utf8');
|
||||
const heartbeat = JSON.parse(raw);
|
||||
const ageMs = Date.now() - new Date(heartbeat.timestamp).getTime();
|
||||
const maxAgeMs = Math.max(config.pollIntervalMs * 3, 120000);
|
||||
|
||||
if (!heartbeat.timestamp || Number.isNaN(ageMs) || ageMs > maxAgeMs) {
|
||||
console.error('Heartbeat is stale');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (heartbeat.status === 'fatal') {
|
||||
console.error('Parser is in fatal state');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
33
parser/scripts/save-session.js
Normal file
33
parser/scripts/save-session.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const readline = require('readline');
|
||||
const config = require('../src/config');
|
||||
const logger = require('../src/logger');
|
||||
const { launchBrowser } = require('../src/session');
|
||||
|
||||
function waitForEnter() {
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
return new Promise((resolve) => {
|
||||
rl.question('Залогинься в браузере, потом нажми Enter здесь...\n', () => {
|
||||
rl.close();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const browser = await launchBrowser({ headless: config.authHeadless });
|
||||
const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
|
||||
const page = await context.newPage();
|
||||
|
||||
logger.info(`Opening base URL: ${config.baseUrl}`);
|
||||
await page.goto(config.baseUrl, { waitUntil: 'domcontentloaded' });
|
||||
|
||||
await waitForEnter();
|
||||
|
||||
await context.storageState({ path: config.sessionFile });
|
||||
logger.info(`Session saved to ${config.sessionFile}`);
|
||||
|
||||
await browser.close();
|
||||
})().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
51
parser/scripts/with-db-url.cjs
Normal file
51
parser/scripts/with-db-url.cjs
Normal file
@@ -0,0 +1,51 @@
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.error('Usage: node scripts/with-db-url.cjs <command> [...args]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function buildDatabaseUrl() {
|
||||
const host = (process.env.POSTGRES_HOST || 'localhost').trim();
|
||||
const port = (process.env.POSTGRES_PORT || '5432').trim();
|
||||
const database = process.env.POSTGRES_DB && process.env.POSTGRES_DB.trim();
|
||||
const user = process.env.POSTGRES_USER && process.env.POSTGRES_USER.trim();
|
||||
const password = process.env.POSTGRES_PASSWORD || '';
|
||||
const schema = (process.env.POSTGRES_SCHEMA || 'public').trim();
|
||||
|
||||
if (database && user) {
|
||||
const credentials = `${encodeURIComponent(user)}:${encodeURIComponent(password)}@`;
|
||||
return `postgresql://${credentials}${host}:${port}/${database}?schema=${encodeURIComponent(schema)}`;
|
||||
}
|
||||
|
||||
const explicitUrl = process.env.DATABASE_URL && process.env.DATABASE_URL.trim();
|
||||
if (explicitUrl) {
|
||||
return explicitUrl;
|
||||
}
|
||||
|
||||
throw new Error('DATABASE_URL is missing and POSTGRES_DB/POSTGRES_USER are not fully configured');
|
||||
}
|
||||
|
||||
try {
|
||||
process.env.DATABASE_URL = buildDatabaseUrl();
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const child = spawn(args[0], args.slice(1), {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
|
||||
process.exit(code == null ? 1 : code);
|
||||
});
|
||||
Reference in New Issue
Block a user