const express = require('express') const cors = require('cors') const bodyParser = require('body-parser') const mysql = require('mysql2/promise') const bcrypt = require('bcryptjs') const app = express() app.use(cors({ origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE' })) app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) const ok = data => ({ success: true, data }) const now = () => new Date().toISOString() let pool = null const initDb = async () => { const url = process.env.DATABASE_URL if (!url) return const u = new URL(url) const config = { host: u.hostname, port: Number(u.port || 3306), user: decodeURIComponent(u.username || ''), password: decodeURIComponent(u.password || ''), database: (u.pathname || '').replace(/^\//, '') || process.env.DB_NAME || 'smtp', waitForConnections: true, connectionLimit: Number(process.env.DB_CONN_LIMIT || 10), queueLimit: 0 } pool = mysql.createPool(config) } const dbPing = async () => { if (!pool) return { connected: false, message: 'DATABASE_URL not set' } const [rows] = await pool.query('SELECT 1 AS ok') return { connected: true, rows } } app.get('/api/db/ping', async (req, res) => { try { const r = await dbPing() res.json(ok(r)) } catch (e) { res.status(500).json({ success: false, message: String(e) }) } }) app.get('/api/settings.php', (req, res) => { res.json(ok({ smtp: { port: 25, host: '0.0.0.0', max_connections: 100 }, pop3: { port: 110, host: '0.0.0.0', max_connections: 100 }, server: { domain: 'test.com', max_email_size: 10485760, max_mailbox_size: 104857600 }, log: { path: '../logs/', level: 'info' } })) }) app.post('/api/settings.php', (req, res) => res.json(ok({ saved: true, at: now(), payload: req.body || {} }))) app.get('/api/users.php', (req, res) => { const users = [ { username: 'admin', email: 'admin@test.com', is_admin: true, is_enabled: true, created_at: now(), updated_at: now() }, { username: 'user', email: 'user@test.com', is_admin: false, is_enabled: true, created_at: now(), updated_at: now() } ] const page = Number(req.query.page || 1), perPage = Number(req.query.perPage || 10) res.json(ok({ users, total: users.length, page, perPage, totalPages: 1 })) }) app.post('/api/users.php', async (req, res) => { const { login, username, password } = req.body || {} if (login) { try { if (!pool) return res.status(500).json({ success: false, message: 'Database not initialized' }) const [rows] = await pool.query('SELECT * FROM `user` WHERE username = ? AND is_deleted = 0 AND is_enabled = 1 LIMIT 1', [username]) const user = rows && rows[0] if (!user) return res.status(401).json({ success: false, message: 'Invalid credentials' }) const stored = user.password || '' const okPass = stored.startsWith('$2') ? bcrypt.compareSync(password, stored) : (stored === password) if (!okPass) return res.status(401).json({ success: false, message: 'Invalid credentials' }) const token = Buffer.from(`${username}:${now()}`).toString('base64') return res.json(ok({ token, username, login_at: now() })) } catch (e) { return res.status(500).json({ success: false, message: String(e) }) } } res.json(ok({ created: true, user: req.body || {} })) }) app.delete('/api/users.php', (req, res) => res.json(ok({ deleted: true, username: req.query.username || '' }))) app.get('/api/emails.php', (req, res) => { const { id, username, folder = 'inbox', page = 1, perPage = 10 } = req.query if (id) return res.json(ok({ email: null })) const emails = [ { id: '1234567890', from: 'sender@example.com', to: 'admin@test.com', subject: '测试邮件', date: now(), folder, is_read: 0, is_deleted: 0, data: '这是邮件内容', created_at: now() }, ] res.json(ok({ emails, total: emails.length, page: Number(page), perPage: Number(perPage), totalPages: 1 })) }) app.post('/api/emails.php', (req, res) => res.json(ok({ sent: !req.body?.isDraft, draft: !!req.body?.isDraft, payload: req.body || {} }))) app.put('/api/emails.php', (req, res) => res.json(ok({ updated: true, payload: req.body || {} }))) app.delete('/api/emails.php', (req, res) => res.json(ok({ deleted: true, id: req.query.id || '', username: req.query.username || '' }))) app.get('/api/logs.php', (req, res) => { const logs = [{ id: 1, type: 'info', message: 'Server started', ip: '127.0.0.1', user_id: 'system', created_at: now() }] res.json(ok({ logs, total: logs.length, page: Number(req.query.page || 1), perPage: Number(req.query.perPage || 10), totalPages: 1 })) }) app.delete('/api/logs.php', (req, res) => res.json(ok({ deleted: req.query.all ? 'all' : req.query.id || '' }))) app.get('/api/contacts.php', (req, res) => { const { id } = req.query const item = { id: 1, name: '张三', email: 'zhangsan@example.com', phone: '13800138000', company: '示例公司', department: '技术部', position: '工程师', create_time: now(), update_time: now() } if (id) return res.json(ok({ contact: item })) res.json(ok({ contacts: [item], total: 1, page: Number(req.query.page || 1), perPage: Number(req.query.perPage || 20), totalPages: 1 })) }) app.post('/api/contacts.php', (req, res) => res.json(ok({ created: true, contact: req.body || {} }))) app.put('/api/contacts.php', (req, res) => res.json(ok({ updated: true, contact: req.body || {} }))) app.delete('/api/contacts.php', (req, res) => res.json(ok({ deleted: true, id: Number(req.query.id || 0) }))) app.get('/api/stats.php', (req, res) => { res.json(ok({ typeStats: { inbox: 0, sent: 0, draft: 0, trash: 0, total: 0 }, readStats: { read: 0, unread: 0 } })) }) app.get('/api/mailbox.php', (req, res) => { const { action } = req.query if (action === 'quota') return res.json(ok({ quota: 1073741824, used: 0, remaining: 1073741824, percentage: 0 })) res.json(ok({ email: 'admin@test.com', stats: { inbox: 0, sent: 0, draft: 0, trash: 0, unread: 0 }, customFolders: [] })) }) const port = process.env.PORT || 8000 initDb() .catch(e => console.error('DB init error', e)) .finally(() => { app.listen(port, () => { console.log(`API server on http://localhost:${port}/`) }) })