import { Hono } from 'hono' import { serveStatic } from 'hono/bun' import { Cron } from 'croner' import { parseHTML } from 'linkedom' import { z } from 'zod' const units: Record = {} const app = new Hono() app.use('/*', serveStatic({ root: './dist' })) app.get('/api/units', (c) => c.json(units)) app.get('*', serveStatic({ root: './dist', path: 'index.html' })) const updateExchangeRates = async () => { const page = await fetch('https://www.cbr.ru/currency_base/daily/').then((d) => d.text()) const { document } = parseHTML(page) const table = document.querySelector('div.table table') if (table) { const [_header, ...rows] = document.querySelectorAll('tr') let amountOfUnits = 0 const codeParser = z.string().regex(/^[A-Z]{3}$/) const amountParser = z.coerce.number().int() const priceParser = z .string() .transform((val) => Number(val.replace('.', '$SPECIAL').replace(',', '.').replace('$SPECIAL', ','))) .pipe(z.number()) for (const row of rows) { const rowData: string[] = [...row.children].map((td) => td.textContent!).filter(Boolean) const [_numberCode, _code, _amount, _description, _price] = rowData try { const code = codeParser.parse(_code) const amount = amountParser.parse(_amount) const price = priceParser.parse(_price) units[code] = price / amount amountOfUnits++ } catch (e) { console.error(e) } } console.log(`Загружен курс для ${amountOfUnits} валют`) } else { console.error('При запросе "https://www.cbr.ru/currency_base/daily/" на странице не найдена таблица') } } updateExchangeRates() new Cron( '0 12 * * *', { name: 'updateExchangeRate', timezone: 'Europe/Moscow', catch: (e) => { console.error(e) }, }, updateExchangeRates, ) export default app