149 lines
4.7 KiB
JavaScript
149 lines
4.7 KiB
JavaScript
/**
|
||
* Quote & Invoice System - Main Entry Point
|
||
* Modularized Backend
|
||
*/
|
||
// ── Global timestamp logger – must be first line before any require ──
|
||
const _ts = () => new Date().toISOString().replace('T', ' ').substring(0, 19);
|
||
const _origLog = console.log.bind(console);
|
||
const _origWarn = console.warn.bind(console);
|
||
const _origError = console.error.bind(console);
|
||
console.log = (...a) => _origLog(`[${_ts()}]`, ...a);
|
||
console.warn = (...a) => _origWarn(`[${_ts()}]`, ...a);
|
||
console.error = (...a) => _origError(`[${_ts()}]`, ...a);
|
||
const express = require('express');
|
||
const path = require('path');
|
||
const puppeteer = require('puppeteer');
|
||
|
||
// Import config
|
||
const { pool } = require('./config/database');
|
||
const { OAuthClient, getOAuthClient, saveTokens } = require('./config/qbo');
|
||
|
||
// Import routes
|
||
const customerRoutes = require('./routes/customers');
|
||
const quoteRoutes = require('./routes/quotes');
|
||
const invoiceRoutes = require('./routes/invoices');
|
||
const paymentRoutes = require('./routes/payments');
|
||
const qboRoutes = require('./routes/qbo');
|
||
const settingsRoutes = require('./routes/settings');
|
||
|
||
// Import PDF service for browser initialization
|
||
const { setBrowser } = require('./services/pdf-service');
|
||
|
||
// Import recurring invoice scheduler
|
||
const { startRecurringScheduler } = require('./services/recurring-service');
|
||
const { startStripePolling } = require('./services/stripe-poll-service');
|
||
|
||
const app = express();
|
||
const PORT = process.env.PORT || 3000;
|
||
|
||
// Global browser instance
|
||
let browser = null;
|
||
|
||
// Initialize browser on startup
|
||
async function initBrowser() {
|
||
if (!browser) {
|
||
console.log('[BROWSER] Launching persistent browser...');
|
||
browser = await puppeteer.launch({
|
||
headless: 'new',
|
||
args: [
|
||
'--no-sandbox',
|
||
'--disable-setuid-sandbox',
|
||
'--disable-dev-shm-usage',
|
||
'--disable-gpu',
|
||
'--disable-software-rasterizer',
|
||
'--no-zygote'
|
||
// '--single-process' WURDE ENTFERNT!
|
||
],
|
||
protocolTimeout: 180000,
|
||
timeout: 180000
|
||
});
|
||
console.log('[BROWSER] Browser launched and ready');
|
||
|
||
// Pass browser to PDF service
|
||
setBrowser(browser);
|
||
|
||
// Restart browser if it crashes (mit Atempause!)
|
||
browser.on('disconnected', () => {
|
||
console.log('[BROWSER] Browser disconnected. Waiting 5 seconds before restarting...');
|
||
browser = null;
|
||
setBrowser(null);
|
||
|
||
// 5 Sekunden warten, bevor ein Neustart versucht wird
|
||
setTimeout(() => {
|
||
initBrowser();
|
||
}, 5000);
|
||
});
|
||
}
|
||
return browser;
|
||
}
|
||
|
||
// Middleware
|
||
app.use(express.json());
|
||
app.use(express.static(path.join(__dirname, '..', 'public')));
|
||
|
||
// =====================================================
|
||
// QBO OAuth Routes — mounted at root level (not under /api/qbo)
|
||
// These must match the Intuit callback URL configuration
|
||
// =====================================================
|
||
app.get('/auth/qbo', (req, res) => {
|
||
const client = getOAuthClient();
|
||
const authUri = client.authorizeUri({
|
||
scope: [OAuthClient.scopes.Accounting],
|
||
state: 'intuit-qbo-auth'
|
||
});
|
||
console.log('🔗 Redirecting to QBO Authorization:', authUri);
|
||
res.redirect(authUri);
|
||
});
|
||
|
||
app.get('/auth/qbo/callback', async (req, res) => {
|
||
const client = getOAuthClient();
|
||
try {
|
||
const authResponse = await client.createToken(req.url);
|
||
console.log('✅ QBO Authorization erfolgreich!');
|
||
saveTokens();
|
||
res.redirect('/#settings');
|
||
} catch (e) {
|
||
console.error('❌ QBO Authorization fehlgeschlagen:', e);
|
||
res.status(500).send(`
|
||
<h2>QBO Authorization Failed</h2>
|
||
<p>${e.message || e}</p>
|
||
<a href="/">Zurück zur App</a>
|
||
`);
|
||
}
|
||
});
|
||
|
||
// =====================================================
|
||
// API Routes
|
||
// =====================================================
|
||
app.use('/api/customers', customerRoutes);
|
||
app.use('/api/quotes', quoteRoutes);
|
||
app.use('/api/invoices', invoiceRoutes);
|
||
app.use('/api/payments', paymentRoutes);
|
||
app.use('/api/qbo', qboRoutes);
|
||
app.use('/api', settingsRoutes);
|
||
|
||
// Start server
|
||
async function startServer() {
|
||
await initBrowser();
|
||
|
||
app.listen(PORT, () => {
|
||
console.log(`Quote System running on port ${PORT}`);
|
||
});
|
||
|
||
// Start recurring invoice scheduler (checks every 24h)
|
||
startRecurringScheduler();
|
||
startStripePolling();
|
||
}
|
||
|
||
// Graceful shutdown
|
||
process.on('SIGTERM', async () => {
|
||
if (browser) {
|
||
await browser.close();
|
||
}
|
||
await pool.end();
|
||
process.exit(0);
|
||
});
|
||
|
||
startServer();
|
||
|
||
module.exports = app; |