Refactor: Remove audio processing and game state management modules
This commit is contained in:
121
src/server-deno/presentation/HttpServer.ts
Normal file
121
src/server-deno/presentation/HttpServer.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { Application, Router, send } from '@oak/oak';
|
||||
import type { AppConfig } from '../shared/config.ts';
|
||||
import { logger } from '../shared/logger.ts';
|
||||
import { createTrackRoutes } from './routes/trackRoutes.ts';
|
||||
import { createAudioRoutes } from './routes/audioRoutes.ts';
|
||||
import type { TrackService } from '../application/mod.ts';
|
||||
import type { AudioStreamingService, CoverArtService } from '../infrastructure/mod.ts';
|
||||
|
||||
/**
|
||||
* HTTP server using Oak
|
||||
*/
|
||||
export class HttpServer {
|
||||
private readonly app: Application;
|
||||
|
||||
constructor(
|
||||
private readonly config: AppConfig,
|
||||
private readonly trackService: TrackService,
|
||||
private readonly audioStreaming: AudioStreamingService,
|
||||
private readonly coverArt: CoverArtService,
|
||||
) {
|
||||
this.app = new Application();
|
||||
this.setupMiddleware();
|
||||
this.setupRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup middleware
|
||||
*/
|
||||
private setupMiddleware(): void {
|
||||
// Error handling
|
||||
this.app.use(async (ctx, next) => {
|
||||
try {
|
||||
await next();
|
||||
} catch (error) {
|
||||
logger.error(`Request error: ${error}`);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: 'Internal server error' };
|
||||
}
|
||||
});
|
||||
|
||||
// Logging
|
||||
this.app.use(async (ctx, next) => {
|
||||
const start = Date.now();
|
||||
await next();
|
||||
const ms = Date.now() - start;
|
||||
logger.info(`${ctx.request.method} ${ctx.request.url.pathname} - ${ctx.response.status} - ${ms}ms`);
|
||||
});
|
||||
|
||||
// CORS
|
||||
this.app.use(async (ctx, next) => {
|
||||
ctx.response.headers.set('Access-Control-Allow-Origin', this.config.corsOrigin);
|
||||
ctx.response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||
ctx.response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Range');
|
||||
|
||||
if (ctx.request.method === 'OPTIONS') {
|
||||
ctx.response.status = 204;
|
||||
return;
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup routes
|
||||
*/
|
||||
private setupRoutes(): void {
|
||||
const router = new Router();
|
||||
|
||||
// API routes
|
||||
const trackRoutes = createTrackRoutes(this.trackService);
|
||||
const audioRoutes = createAudioRoutes(this.audioStreaming, this.coverArt, this.config);
|
||||
|
||||
router.use(trackRoutes.routes(), trackRoutes.allowedMethods());
|
||||
router.use(audioRoutes.routes(), audioRoutes.allowedMethods());
|
||||
|
||||
this.app.use(router.routes());
|
||||
this.app.use(router.allowedMethods());
|
||||
|
||||
// Static file serving (public directory)
|
||||
this.app.use(async (ctx) => {
|
||||
try {
|
||||
await send(ctx, ctx.request.url.pathname, {
|
||||
root: this.config.publicDir,
|
||||
index: 'index.html',
|
||||
});
|
||||
} catch {
|
||||
// If file not found, serve index.html for SPA routing
|
||||
try {
|
||||
await send(ctx, '/index.html', {
|
||||
root: this.config.publicDir,
|
||||
});
|
||||
} catch (error) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = 'Not found';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the HTTP server
|
||||
*/
|
||||
async listen(): Promise<void> {
|
||||
const { host, port } = this.config;
|
||||
|
||||
logger.info(`Starting HTTP server on http://${host}:${port}`);
|
||||
|
||||
await this.app.listen({
|
||||
hostname: host,
|
||||
port,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying Oak application
|
||||
*/
|
||||
getApp(): Application {
|
||||
return this.app;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user