77f8b59290
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
45 lines
1.8 KiB
Vue
45 lines
1.8 KiB
Vue
<template>
|
|
<div>
|
|
<h2 style="margin-top:0">连接日志</h2>
|
|
<div style="margin-bottom:12px">
|
|
<el-select v-model="instanceId" placeholder="选择实例" clearable @change="load" style="width:240px">
|
|
<el-option v-for="i in instances" :key="i.id" :value="i.id" :label="i.name" />
|
|
</el-select>
|
|
</div>
|
|
<el-table :data="list" stripe>
|
|
<el-table-column prop="common_name" label="用户" />
|
|
<el-table-column prop="real_ip" label="来源 IP" width="160" />
|
|
<el-table-column prop="vpn_ip" label="VPN IP" width="140" />
|
|
<el-table-column prop="bytes_in" label="下行" width="100">
|
|
<template #default="{row}">{{ fmt(row.bytes_in) }}</template>
|
|
</el-table-column>
|
|
<el-table-column prop="bytes_out" label="上行" width="100">
|
|
<template #default="{row}">{{ fmt(row.bytes_out) }}</template>
|
|
</el-table-column>
|
|
<el-table-column prop="connected_at" label="开始时间" width="170">
|
|
<template #default="{row}">{{ row.connected_at?.slice(0,19) }}</template>
|
|
</el-table-column>
|
|
<el-table-column prop="disconnected_at" label="结束时间" width="170">
|
|
<template #default="{row}">{{ row.disconnected_at?.slice(0,19) || '-' }}</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
</template>
|
|
<script setup>
|
|
import { onMounted, ref } from 'vue'
|
|
import { Logs, Inst } from '@/api'
|
|
|
|
const list = ref([])
|
|
const instances = ref([])
|
|
const instanceId = ref('')
|
|
|
|
async function load() { list.value = await Logs.conns(instanceId.value) }
|
|
function fmt(n) {
|
|
if (!n) return '0'
|
|
const u = ['B','KB','MB','GB','TB']; let i=0,v=n
|
|
while (v>=1024 && i<u.length-1) { v/=1024; i++ }
|
|
return v.toFixed(1)+' '+u[i]
|
|
}
|
|
|
|
onMounted(async () => { instances.value = await Inst.list(); load() })
|
|
</script> |