import { Hono } from 'hono'; import { serveStatic } from 'hono/bun'; import { getCookie, setCookie, deleteCookie } from 'hono/cookie'; import { authRoutes } from './routes/auth'; import { publicationRoutes } from './routes/publication'; import { documentRoutes } from './routes/documents'; import { layout } from './views/layouts/main'; import { homePage } from './views/home'; import { getSession } from './lib/session'; import { getClientMetadata, getJwks } from './lib/oauth'; import { csrfProtection, getCSRFToken } from './lib/csrf'; export const app = new Hono(); // Static files app.use('/public/*', serveStatic({ root: './' })); // OAuth metadata endpoints at root level // These MUST be publicly accessible (no authentication) app.get('/client-metadata.json', async (c) => { try { const metadata = await getClientMetadata(); // Set appropriate cache headers c.header('Cache-Control', 'public, max-age=600'); // Cache for 10 minutes c.header('Access-Control-Allow-Origin', '*'); return c.json(metadata); } catch (error) { console.error('Error getting client metadata:', error); return c.json({ error: 'Failed to get client metadata' }, 500); } }); app.get('/jwks.json', async (c) => { try { const jwks = await getJwks(); // Set appropriate cache headers c.header('Cache-Control', 'public, max-age=600'); // Cache for 10 minutes c.header('Access-Control-Allow-Origin', '*'); return c.json(jwks); } catch (error) { console.error('Error getting JWKS:', error); return c.json({ error: 'Failed to get JWKS' }, 500); } }); // Session middleware - adds session and CSRF token to context app.use('*', async (c, next) => { const session = await getSession(c); c.set('session', session); // Generate CSRF token for all requests (sets cookie if not present) const csrfToken = getCSRFToken(c); c.set('csrfToken', csrfToken); await next(); }); // CSRF protection for state-changing requests // Applied after session middleware but before routes app.use('/auth/*', csrfProtection); app.use('/publication/*', csrfProtection); app.use('/documents/*', csrfProtection); // Home page app.get('/', async (c) => { const session = c.get('session'); return c.html(layout(homePage(session), { session })); }); // Mount routes app.route('/auth', authRoutes); app.route('/publication', publicationRoutes); app.route('/documents', documentRoutes); const port = parseInt(process.env.PORT || '8000'); console.log(`Starting server on http://localhost:${port}`); console.log(`Public URL: ${process.env.PUBLIC_URL || 'http://localhost:' + port}`); export default { port, fetch: app.fetch, };