File size: 2,990 Bytes
5dfbe50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { compress } from 'hono/compress';
import { secureHeaders } from 'hono/secure-headers';
import { timeout } from 'hono/timeout';
import { config } from './config';
import { corsMiddleware } from './middleware/cors';
import { loggerMiddleware, requestIdMiddleware } from './middleware/logger';
import { rateLimitMiddleware } from './middleware/rateLimit';
import { api } from './routes/api';
import { backendApi } from './routes/backend-api';
import { healthApp } from './routes/health';

const app = new Hono();

// Global middleware
app.use('*', requestIdMiddleware);
app.use('*', loggerMiddleware);
app.use('*', corsMiddleware);
app.use('*', secureHeaders());
app.use('*', compress());

// Apply rate limiting to API routes only
app.use('/api/*', rateLimitMiddleware);

// Apply timeout to prevent hanging requests (30 seconds, except for SSE)
app.use('/api/*', async (c, next) => {
  if (c.req.path === '/api/chat') {
    // Skip timeout for SSE endpoints
    await next();
  } else {
    return timeout(30000)(c, next);
  }
});

// Mount routes - matching backend API structure at root level
app.route('/', backendApi);

// Also mount at /api for direct Next.js API access (optional)
app.route('/api', api);

// Health check
app.route('/health', healthApp);

// Root info endpoint
app.get('/info', (c) => {
  return c.json({
    name: 'ColPali Hono Proxy',
    version: '1.0.0',
    endpoints: {
      // Backend-compatible endpoints (Python API format)
      search: '/fetch_results',
      fullImage: '/full_image',
      suggestions: '/suggestions',
      similarityMaps: '/get_sim_map',
      chat: '/get-message',
      // Direct API endpoints
      apiSearch: '/api/colpali-search',
      apiFullImage: '/api/full-image',
      apiSuggestions: '/api/query-suggestions',
      apiSimilarityMaps: '/api/similarity-maps',
      apiChat: '/api/visual-rag-chat',
      health: '/health',
    },
  });
});

// 404 handler
app.notFound((c) => {
  return c.json({ error: 'Not found', path: c.req.path }, 404);
});

// Global error handler
app.onError((err, c) => {
  console.error(`Error handling request ${c.req.path}:`, err);
  
  if (err instanceof Error) {
    if (err.message.includes('timeout')) {
      return c.json({ error: 'Request timeout' }, 408);
    }
  }
  
  return c.json(
    { 
      error: 'Internal server error',
      requestId: c.get('requestId'),
    }, 
    500
  );
});

// Start server
const port = config.port;

console.log(`πŸš€ ColPali Hono Proxy starting...`);
console.log(`πŸ“ Backend URL: ${config.backendUrl}`);
console.log(`πŸ”’ CORS Origin: ${config.corsOrigin}`);
console.log(`πŸ’Ύ Cache: ${config.enableCache ? 'Enabled' : 'Disabled'}`);
console.log(`🚦 Rate Limit: ${config.rateLimit.max} requests per ${config.rateLimit.windowMs / 1000}s`);

serve({
  fetch: app.fetch,
  port,
}, (info) => {
  console.log(`βœ… Server running on http://localhost:${info.port}`);
});