feat(backend): realtime event system, admin API ve metrics altyapısı
- socket.ts: ContentRealtimeEvent, CacheRealtimeEvent, MetricsRealtimeEvent
tipleri eklendi; emitContentEvent / emitCacheEvent / emitMetricsEvent
fonksiyonları ile tüm istemcilere broadcast desteği getirildi.
emitJobCompleted imzası GetInfoResponse + DataSource ile güçlendirildi.
- auth.middleware.ts: require() tabanlı env erişimi static import'a
dönüştürüldü; admin-only endpointler için adminOnlyMiddleware eklendi
(X-API-Key !== API_KEY_ADMIN → 403).
- cache.service.ts: set / delete / clearAll işlemlerinden sonra
emitCacheEvent çağrısı eklenerek cache mutasyonları anlık yayınlanıyor.
- content.service.ts: create / update / delete sonrasında emitContentEvent
çağrısı eklenerek DB yazımları Socket.IO üzerinden duyuruluyor.
- job.service.ts: async ve sync akışa MetricsService entegrasyonu eklendi;
cache hit/miss ve kaynak (cache/database/netflix) sayaçları her işlemde
artırılıyor.
- types/index.ts: AdminOverviewResponse ve AdminActionResponse tipleri
merkezi olarak tanımlandı.
- admin.service.ts (yeni): getOverview, clearCache, warmupCacheFromDatabase,
retryFailedJobs, refreshStaleContent operasyonları implement edildi.
Redis pipeline ile TTL/boyut analizi ve DB metrikleri paralel toplanıyor.
- metrics.service.ts (yeni): Redis hash tabanlı cache hit/miss ve kaynak
sayaçları; her artışta MetricsRealtimeEvent yayınlanıyor.
- api.routes.ts: Admin endpointleri eklendi:
GET /api/admin/overview
POST /api/admin/cache/clear
POST /api/admin/cache/warmup
POST /api/admin/jobs/retry-failed
POST /api/admin/content/refresh-stale
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Server as HttpServer } from 'http';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import logger from '../utils/logger.js';
|
||||
import type { DataSource, GetInfoResponse } from '../types/index.js';
|
||||
|
||||
/**
|
||||
* Socket.IO Server singleton
|
||||
@@ -11,6 +12,32 @@ export interface SocketData {
|
||||
subscribedJobs: Set<string>;
|
||||
}
|
||||
|
||||
export interface ContentRealtimeEvent {
|
||||
action: 'created' | 'updated' | 'deleted';
|
||||
url: string;
|
||||
content?: GetInfoResponse;
|
||||
occurredAt: string;
|
||||
}
|
||||
|
||||
export interface CacheRealtimeEvent {
|
||||
action: 'written' | 'deleted' | 'cleared';
|
||||
key?: string;
|
||||
ttlSeconds?: number;
|
||||
count?: number;
|
||||
occurredAt: string;
|
||||
}
|
||||
|
||||
export interface MetricsRealtimeEvent {
|
||||
cacheHits: number;
|
||||
cacheMisses: number;
|
||||
sourceCounts: {
|
||||
cache: number;
|
||||
database: number;
|
||||
netflix: number;
|
||||
};
|
||||
occurredAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Socket.IO server
|
||||
*/
|
||||
@@ -86,8 +113,8 @@ export function emitJobProgress(
|
||||
*/
|
||||
export function emitJobCompleted(
|
||||
jobId: string,
|
||||
data: unknown,
|
||||
source: string
|
||||
data: GetInfoResponse,
|
||||
source: DataSource
|
||||
): void {
|
||||
if (io) {
|
||||
io.to(`job:${jobId}`).emit('job:completed', {
|
||||
@@ -98,6 +125,33 @@ export function emitJobCompleted(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit realtime content mutation event to all clients
|
||||
*/
|
||||
export function emitContentEvent(event: ContentRealtimeEvent): void {
|
||||
if (io) {
|
||||
io.emit('content:event', event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit realtime cache event to all clients
|
||||
*/
|
||||
export function emitCacheEvent(event: CacheRealtimeEvent): void {
|
||||
if (io) {
|
||||
io.emit('cache:event', event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit realtime metrics event to all clients
|
||||
*/
|
||||
export function emitMetricsEvent(event: MetricsRealtimeEvent): void {
|
||||
if (io) {
|
||||
io.emit('metrics:updated', event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit job error event
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user