158 lines
4.3 KiB
JavaScript
158 lines
4.3 KiB
JavaScript
import express from 'express';
|
|
import cors from 'cors';
|
|
import bodyParser from 'body-parser';
|
|
import { createRequire } from 'module';
|
|
import wgService from './services/wgService.js';
|
|
import auth from './middleware/auth.js';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
|
|
const app = express();
|
|
app.use(cors());
|
|
app.use(bodyParser.json({ limit: '2mb' }));
|
|
|
|
// Health
|
|
app.get('/api/health', (_req, res) => {
|
|
res.json({ ok: true });
|
|
});
|
|
|
|
// Auth
|
|
app.post('/api/login', (req, res) => {
|
|
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD;
|
|
const { password } = req.body || {};
|
|
if (!ADMIN_PASSWORD) {
|
|
return res.status(500).json({ error: '未配置 ADMIN_PASSWORD 环境变量' });
|
|
}
|
|
if (password !== ADMIN_PASSWORD) {
|
|
return res.status(401).json({ error: '密码错误' });
|
|
}
|
|
const token = auth.issueToken();
|
|
res.json({ token });
|
|
});
|
|
|
|
// 保护以下接口
|
|
app.use('/api', auth.verifyToken);
|
|
|
|
// Server config
|
|
app.get('/api/server/config', async (req, res) => {
|
|
try {
|
|
const iface = req.query.interface || 'wg0';
|
|
const content = await wgService.readServerConfig(iface);
|
|
res.json({ interface: iface, content });
|
|
} catch (err) {
|
|
res.status(500).json({ error: String(err?.message || err) });
|
|
}
|
|
});
|
|
|
|
app.put('/api/server/config', async (req, res) => {
|
|
try {
|
|
const { interface: iface = 'wg0', content } = req.body || {};
|
|
if (typeof content !== 'string') {
|
|
return res.status(400).json({ error: 'content must be string' });
|
|
}
|
|
const result = await wgService.writeServerConfig(iface, content);
|
|
res.json({ ok: true, backupPath: result.backupPath, path: result.path });
|
|
} catch (err) {
|
|
res.status(500).json({ error: String(err?.message || err) });
|
|
}
|
|
});
|
|
|
|
// Server control
|
|
app.post('/api/server/control', async (req, res) => {
|
|
try {
|
|
const { interface: iface = 'wg0', action } = req.body || {};
|
|
if (!['start', 'stop', 'restart'].includes(action)) {
|
|
return res.status(400).json({ error: 'action must be start|stop|restart' });
|
|
}
|
|
const output = await wgService.controlService(iface, action);
|
|
res.json({ ok: true, output });
|
|
} catch (err) {
|
|
res.status(500).json({ error: String(err?.message || err) });
|
|
}
|
|
});
|
|
|
|
// Server status
|
|
app.get('/api/server/status', async (req, res) => {
|
|
try {
|
|
const iface = req.query.interface || 'wg0';
|
|
const status = await wgService.getStatus(iface);
|
|
res.json(status);
|
|
} catch (err) {
|
|
res.status(500).json({ error: String(err?.message || err) });
|
|
}
|
|
});
|
|
|
|
// Keys
|
|
app.post('/api/keys/generate', async (req, res) => {
|
|
try {
|
|
const { type = 'pair' } = req.body || {};
|
|
if (type === 'pair') {
|
|
const pair = await wgService.generateKeyPair();
|
|
res.json(pair);
|
|
} else if (type === 'psk') {
|
|
const psk = await wgService.generatePresharedKey();
|
|
res.json({ presharedKey: psk });
|
|
} else {
|
|
res.status(400).json({ error: 'type must be pair|psk' });
|
|
}
|
|
} catch (err) {
|
|
res.status(500).json({ error: String(err?.message || err) });
|
|
}
|
|
});
|
|
|
|
// Client config
|
|
app.post('/api/clients/generate', async (req, res) => {
|
|
try {
|
|
const {
|
|
interface: iface = 'wg0',
|
|
name,
|
|
clientAddress, // optional; auto-assign if omitted
|
|
dns = '1.1.1.1',
|
|
allowedIps = '0.0.0.0/0, ::/0',
|
|
keepalive = 25,
|
|
endpointHost,
|
|
endpointPort,
|
|
usePresharedKey = false,
|
|
} = req.body || {};
|
|
|
|
if (!name) return res.status(400).json({ error: 'name is required' });
|
|
if (!endpointHost || !endpointPort) {
|
|
return res.status(400).json({ error: 'endpointHost and endpointPort are required' });
|
|
}
|
|
|
|
const result = await wgService.generateClientConfig({
|
|
interfaceName: iface,
|
|
clientName: name,
|
|
clientAddress,
|
|
dns,
|
|
allowedIps,
|
|
keepalive,
|
|
endpointHost,
|
|
endpointPort,
|
|
usePresharedKey,
|
|
});
|
|
|
|
// 生成后自动写入服务端Peer
|
|
await wgService.appendPeerToServerConfig({
|
|
interfaceName: iface,
|
|
clientName: name,
|
|
clientPublicKey: result.publicKey,
|
|
clientAddress: result.address,
|
|
presharedKey: result.presharedKey,
|
|
allowedIps,
|
|
});
|
|
|
|
res.json(result);
|
|
} catch (err) {
|
|
res.status(500).json({ error: String(err?.message || err) });
|
|
}
|
|
});
|
|
|
|
const PORT = process.env.PORT || 51821;
|
|
app.listen(PORT, () => {
|
|
// eslint-disable-next-line no-console
|
|
console.log(`wg-easy backend listening on :${PORT}`);
|
|
});
|
|
|
|
|