Initial commit: OpenVPN Manager v1.0

OpenVPN Web management console with multi-instance support, client cert
issuance, traffic/connection auditing, certificate expiry reminders,
auto backup/restore.

Stack:
- Backend: Go 1.21+ (Gin + JWT)
- Frontend: Vue 3 + Element Plus + ECharts + Vite
- Storage: JSON file (db.json) + filesystem (pki/, instances/, clients/, backups/)

Features:
- Multi-instance OpenVPN management (independent port/proto/subnet/PKI)
- One-click client certificate issuance with .ovpn (embedded certs)
- Certificate expiry reminders (30-day threshold)
- Connection log parsing (status-version 3)
- Auto backup/restore (tar.gz)
- Audit log for all write operations
- JWT auth (12h TTL)
- One-line install.sh for Ubuntu/Debian/RHEL/Fedora
This commit is contained in:
cnbugs
2026-08-09 20:32:37 +08:00
commit 77f8b59290
34 changed files with 6063 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
const api = axios.create({ baseURL: '/api', timeout: 30000 })
api.interceptors.request.use(cfg => {
const t = localStorage.getItem('token')
if (t) cfg.headers.Authorization = `Bearer ${t}`
return cfg
})
api.interceptors.response.use(
r => r,
err => {
if (err.response?.status === 401) {
localStorage.removeItem('token')
if (router.currentRoute.value.path !== '/login') {
router.replace('/login')
}
} else {
ElMessage.error(err.response?.data?.error || err.message || '请求失败')
}
return Promise.reject(err)
}
)
export const Auth = {
login: (username, password) => api.post('/login', { username, password }).then(r => r.data),
me: () => api.get('/me').then(r => r.data),
logout: () => api.post('/logout').then(r => r.data),
}
export const Dash = {
get: () => api.get('/dashboard').then(r => r.data),
}
export const Inst = {
list: () => api.get('/instances').then(r => r.data),
get: id => api.get(`/instances/${id}`).then(r => r.data),
create: data => api.post('/instances', data).then(r => r.data),
update: (id, data) => api.put(`/instances/${id}`, data).then(r => r.data),
delete: id => api.delete(`/instances/${id}`).then(r => r.data),
start: id => api.post(`/instances/${id}/start`).then(r => r.data),
stop: id => api.post(`/instances/${id}/stop`).then(r => r.data),
online: id => api.get(`/instances/${id}/online`).then(r => r.data),
listUsers: id => api.get(`/instances/${id}/users`).then(r => r.data),
createUser: (id, data) => api.post(`/instances/${id}/users`, data).then(r => r.data),
revokeUser: (id, uid) => api.post(`/instances/${id}/users/${uid}/revoke`).then(r => r.data),
deleteUser: (id, uid) => api.delete(`/instances/${id}/users/${uid}`).then(r => r.data),
ovpnUrl: (id, uid, host) => `/api/instances/${id}/users/${uid}/ovpn?host=${encodeURIComponent(host||'')}`,
}
export const Certs = {
list: () => api.get('/certs').then(r => r.data),
}
export const Logs = {
conns: instanceId => api.get('/connlogs', { params: { instance: instanceId || '' }}).then(r => r.data),
audits: () => api.get('/audits').then(r => r.data),
}
export const Backup = {
list: () => api.get('/backups').then(r => r.data),
create: note => api.post('/backups', { note }).then(r => r.data),
restore: id => api.post(`/backups/${id}/restore`).then(r => r.data),
delete: id => api.delete(`/backups/${id}`).then(r => r.data),
}
export default api