102 lines
3.4 KiB
JavaScript
102 lines
3.4 KiB
JavaScript
const express = require('express');
|
|
const path = require('path');
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Middleware
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
// CORS middleware
|
|
app.use((req, res, next) => {
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
if (req.method === 'OPTIONS') {
|
|
return res.status(200).end();
|
|
}
|
|
next();
|
|
});
|
|
|
|
// API routes - wrap Vercel serverless functions
|
|
const waitlistHandler = require('./api/waitlist-db');
|
|
const applicationHandler = require('./api/application');
|
|
const gameChatHandler = require('./api/game-chat');
|
|
const shareToGithubHandler = require('./api/share-to-github');
|
|
const { handleWebhook, getPaymentStatus } = require('./api/mollie');
|
|
|
|
// Adapter to convert Vercel handler to Express
|
|
const vercelToExpress = (handler) => async (req, res) => {
|
|
try {
|
|
await handler(req, res);
|
|
} catch (error) {
|
|
console.error('API Error:', error);
|
|
if (!res.headersSent) {
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
}
|
|
};
|
|
|
|
app.all('/api/waitlist', vercelToExpress(waitlistHandler));
|
|
app.all('/api/application', vercelToExpress(applicationHandler));
|
|
app.all('/api/game-chat', vercelToExpress(gameChatHandler));
|
|
app.all('/api/share-to-github', vercelToExpress(shareToGithubHandler));
|
|
app.post('/api/mollie/webhook', vercelToExpress(handleWebhook));
|
|
app.all('/api/mollie/status', vercelToExpress(getPaymentStatus));
|
|
|
|
// Static files
|
|
app.use(express.static(path.join(__dirname), {
|
|
extensions: ['html'],
|
|
index: 'index.html'
|
|
}));
|
|
|
|
// SPA fallback - serve index.html for non-API routes
|
|
app.get('*', (req, res) => {
|
|
if (!req.path.startsWith('/api/')) {
|
|
res.sendFile(path.join(__dirname, 'index.html'));
|
|
}
|
|
});
|
|
|
|
// Run database migrations on startup
|
|
async function runMigrations() {
|
|
const { Pool } = require('pg');
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
ssl: process.env.DATABASE_SSL === 'true' ? { rejectUnauthorized: false } : false
|
|
});
|
|
try {
|
|
await pool.query(`
|
|
ALTER TABLE applications
|
|
ADD COLUMN IF NOT EXISTS mollie_payment_id VARCHAR(255),
|
|
ADD COLUMN IF NOT EXISTS payment_status VARCHAR(50) DEFAULT 'unpaid',
|
|
ADD COLUMN IF NOT EXISTS payment_amount DECIMAL(10, 2),
|
|
ADD COLUMN IF NOT EXISTS payment_paid_at TIMESTAMP WITH TIME ZONE
|
|
`);
|
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_applications_mollie_id ON applications(mollie_payment_id)');
|
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_applications_payment_status ON applications(payment_status)');
|
|
|
|
// Rename resend_id → message_id in email_log (legacy column name)
|
|
const colCheck = await pool.query(`
|
|
SELECT column_name FROM information_schema.columns
|
|
WHERE table_name = 'email_log' AND column_name = 'resend_id'
|
|
`);
|
|
if (colCheck.rows.length > 0) {
|
|
await pool.query('ALTER TABLE email_log RENAME COLUMN resend_id TO message_id');
|
|
console.log('Renamed email_log.resend_id → message_id');
|
|
}
|
|
|
|
console.log('Database migrations complete');
|
|
} catch (err) {
|
|
console.error('Migration error:', err.message);
|
|
} finally {
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
runMigrations().then(() => {
|
|
app.listen(PORT, '0.0.0.0', () => {
|
|
console.log(`Valley of the Commons server running on port ${PORT}`);
|
|
});
|
|
});
|