const express = require('express'); const cors = require('cors'); const path = require('path'); const fs = require('fs'); require('dotenv').config(); const db = require('./db'); const { generateLicense } = require('./licenseManager'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const cookieParser = require('cookie-parser'); const app = express(); const PORT = process.env.LICENSE_SERVER_PORT || 3006; const JWT_SECRET = process.env.JWT_SECRET || 'fallback_secret'; app.use(cors({ origin: true, credentials: true })); app.use(express.json()); app.use(cookieParser()); // Auth Middleware const authenticateToken = (req, res, next) => { const token = req.cookies.token; if (!token) return res.status(401).json({ error: 'Unauthorized: No token provided' }); jwt.verify(token, JWT_SECRET, (err, user) => { if (err) return res.status(403).json({ error: 'Forbidden: Invalid token' }); req.user = user; next(); }); }; // Load Private Key for Generation const privateKeyPath = path.join(__dirname, '../config/private_key.pem'); let privateKey = null; try { if (fs.existsSync(privateKeyPath)) { privateKey = fs.readFileSync(privateKeyPath, 'utf8'); } else { console.error('WARNING: private_key.pem not found in config. Generation will fail.'); } } catch (e) { console.error('Error loading private key:', e); } // --- Auth Routes --- app.post('/api/auth/login', async (req, res) => { const { loginId, password } = req.body; try { const [users] = await db.query('SELECT * FROM users WHERE login_id = ?', [loginId]); if (users.length === 0) return res.status(401).json({ error: 'Invalid ID or Password' }); const user = users[0]; const validPassword = await bcrypt.compare(password, user.password); if (!validPassword) return res.status(401).json({ error: 'Invalid ID or Password' }); const token = jwt.sign({ id: user.id, loginId: user.login_id, name: user.name }, JWT_SECRET, { expiresIn: '24h' }); res.cookie('token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 24 * 60 * 60 * 1000 // 24 hours }); res.json({ success: true, user: { id: user.id, loginId: user.login_id, name: user.name } }); } catch (err) { console.error(err); res.status(500).json({ error: 'Login failed' }); } }); app.get('/api/auth/me', authenticateToken, (req, res) => { res.json(req.user); }); app.post('/api/auth/logout', (req, res) => { res.clearCookie('token'); res.json({ success: true }); }); // --- License Routes --- // 1. Get All Issued Licenses app.get('/api/licenses', authenticateToken, async (req, res) => { try { const [rows] = await db.query('SELECT * FROM issued_licenses ORDER BY created_at DESC'); res.json(rows); } catch (err) { console.error(err); res.status(500).json({ error: 'Database error' }); } }); // 1.1 Delete License app.delete('/api/licenses/:id', authenticateToken, async (req, res) => { const { id } = req.params; try { await db.query('DELETE FROM issued_licenses WHERE id = ?', [id]); res.json({ success: true, message: 'License deleted' }); } catch (err) { console.error(err); res.status(500).json({ error: 'Failed to delete license' }); } }); // 1.2 Activate License by Key (called by SmartIMS) app.post('/api/licenses/activate', async (req, res) => { const { licenseKey } = req.body; if (!licenseKey) return res.status(400).json({ error: 'License key is required' }); try { const [result] = await db.query('UPDATE issued_licenses SET status = "ACTIVATED" WHERE license_key = ?', [licenseKey]); if (result.affectedRows === 0) { return res.status(404).json({ error: 'License key not found' }); } res.json({ success: true, message: 'License status updated to ACTIVATED' }); } catch (err) { console.error(err); res.status(500).json({ error: 'Database error' }); } }); // 2. Generate New License app.post('/api/licenses/generate', authenticateToken, async (req, res) => { const { moduleCode, type, subscriberId, expiryDate } = req.body; if (!moduleCode || !type || !subscriberId) { return res.status(400).json({ error: 'Missing required fields' }); } if (!privateKey) { return res.status(500).json({ error: 'Server key configuration error' }); } try { // Generate the key // For DEV, we don't pass expiryDate to make it permanent const finalExpiryDate = type === 'dev' ? null : expiryDate; const licenseKey = generateLicense( moduleCode, type, subscriberId, null, // activationDays finalExpiryDate, privateKey ); // Store in DB const sql = ` INSERT INTO issued_licenses (module_code, license_key, license_type, subscriber_id, status, created_at) VALUES (?, ?, ?, ?, 'WAITING', NOW()) `; await db.query(sql, [moduleCode, licenseKey, type, subscriberId]); res.json({ success: true, licenseKey }); } catch (err) { console.error(err); res.status(500).json({ error: err.message || 'Failed to generate license' }); } }); // Serve Static Files (Production) const clientDistPath = path.join(__dirname, '../client/dist'); if (fs.existsSync(clientDistPath)) { app.use(express.static(clientDistPath)); // SPA Fallback: Any route not handled by API should return index.html app.get('*', (req, res) => { if (!req.path.startsWith('/api')) { res.sendFile(path.join(clientDistPath, 'index.html')); } }); console.log(`📁 Serving client from ${clientDistPath}`); } else { console.log('⚠️ Client dist folder not found. API mode only.'); } app.listen(PORT, () => { console.log(`🚀 License Manager Server running on port ${PORT}`); });