Spaces:
Sleeping
Sleeping
| import { | |
| Controller, | |
| Get, | |
| Post, | |
| Param, | |
| Query, | |
| Body, | |
| Res, | |
| Inject, | |
| Optional, | |
| NotFoundException, | |
| } from '@nestjs/common'; | |
| import { Response } from 'express'; | |
| import { MarketsService } from './markets.service'; | |
| import { MarketCacheService } from './market-cache.service'; | |
| ('markets') | |
| export class MarketsController { | |
| private readonly marketQueue: any; | |
| constructor( | |
| private readonly marketsService: MarketsService, | |
| private readonly cacheService: MarketCacheService, | |
| () ('BullQueue_market-processing') marketQueue?: any, | |
| ) { | |
| this.marketQueue = marketQueue || null; | |
| } | |
| () | |
| getMarkets(('q') query?: string) { | |
| if (query && query.trim().length > 0) { | |
| return this.marketsService.searchMarkets(query); | |
| } | |
| const markets = this.marketsService.getMarkets(); | |
| // Enrich with processing status | |
| return markets.map((m) => { | |
| const cached = this.cacheService.get(m.slug); | |
| return { | |
| ...m, | |
| processingStatus: cached?.status || 'unknown', | |
| }; | |
| }); | |
| } | |
| ('search') | |
| searchMarket(('query') query: string) { | |
| if (!query || query.trim().length < 2) { | |
| return { error: 'Query must be at least 2 characters' }; | |
| } | |
| const detail = this.marketsService.searchOrCreateMarket(query.trim()); | |
| return detail; | |
| } | |
| ('jobs/status') | |
| getJobsStatus() { | |
| const all = this.cacheService.getAll(); | |
| return { | |
| markets: all.map((c) => ({ | |
| slug: c.slug, | |
| status: c.status, | |
| updatedAt: c.updatedAt, | |
| error: c.error, | |
| })), | |
| summary: { | |
| total: all.length, | |
| ready: all.filter((c) => c.status === 'ready').length, | |
| processing: all.filter((c) => c.status === 'processing').length, | |
| pending: all.filter((c) => c.status === 'pending').length, | |
| error: all.filter((c) => c.status === 'error').length, | |
| }, | |
| }; | |
| } | |
| (':slug/refresh') | |
| async refreshMarket(('slug') slug: string) { | |
| const market = this.marketsService.getMarkets().find((m) => m.slug === slug); | |
| if (!market) { | |
| throw new NotFoundException('Market not found'); | |
| } | |
| if (this.marketQueue) { | |
| this.cacheService.setStatus(slug, 'pending'); | |
| await this.marketQueue.add( | |
| 'process-market', | |
| { slug }, | |
| { removeOnComplete: 100, removeOnFail: 50 }, | |
| ); | |
| return { message: 'Market refresh queued', slug, status: 'pending' }; | |
| } | |
| return { message: 'Market data refreshed (no queue)', slug, status: 'ready' }; | |
| } | |
| (':slug') | |
| getMarketDetail(('slug') slug: string) { | |
| // Return cached data if available (processed by BullMQ worker) | |
| const cached = this.cacheService.get(slug); | |
| if (cached?.status === 'ready' && cached.data) { | |
| return { | |
| ...cached.data, | |
| fromCache: true, | |
| processingStatus: 'ready', | |
| }; | |
| } | |
| // Try to get the market detail (works for both hardcoded + dynamic) | |
| const market = this.marketsService.getMarketDetail(slug); | |
| if (!market) { | |
| throw new NotFoundException('Market not found'); | |
| } | |
| return { | |
| ...market, | |
| fromCache: false, | |
| processingStatus: cached?.status || 'not-queued', | |
| }; | |
| } | |
| (':slug/export/odds-csv') | |
| exportOddsCsv(('slug') slug: string, () res: Response) { | |
| const csv = this.marketsService.getOddsHistoryCsv(slug); | |
| if (!csv) { | |
| throw new NotFoundException('Market not found'); | |
| } | |
| res.setHeader('Content-Type', 'text/csv'); | |
| res.setHeader( | |
| 'Content-Disposition', | |
| `attachment; filename=odds_history_${slug}.csv`, | |
| ); | |
| res.send(csv); | |
| } | |
| (':slug/export/integrity-csv') | |
| exportIntegrityCsv(('slug') slug: string, () res: Response) { | |
| const csv = this.marketsService.getIntegrityCsv(slug); | |
| if (!csv) { | |
| throw new NotFoundException('Market not found'); | |
| } | |
| res.setHeader('Content-Type', 'text/csv'); | |
| res.setHeader( | |
| 'Content-Disposition', | |
| `attachment; filename=integrity_${slug}.csv`, | |
| ); | |
| res.send(csv); | |
| } | |
| (':slug/export/dossier-json') | |
| exportDossierJson(('slug') slug: string) { | |
| const dossier = this.marketsService.getDossierJson(slug); | |
| if (!dossier) { | |
| throw new NotFoundException('Market not found'); | |
| } | |
| return dossier; | |
| } | |
| } | |