Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
c3ef51e87f | |||
72420f2ede | |||
a12552a608 | |||
5f200ea989 | |||
3f14df72be | |||
29902afe65 | |||
![]() |
ddef41dfca | ||
6c6b40593e | |||
45d83da5c7 | |||
4fa123baa8 | |||
c7a56b1dde | |||
37446ea958 | |||
1b28b196a0 | |||
1a5ab341ab | |||
299ec93199 | |||
32f61a0eef |
56
.gitignore
vendored
56
.gitignore
vendored
@@ -233,33 +233,33 @@ fabric.properties
|
|||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
./go.work
|
||||||
|
|
||||||
.idea
|
./.idea
|
||||||
web/.idea
|
./web/.idea
|
||||||
|
|
||||||
web/node_modules
|
./web/node_modules
|
||||||
web/.DS_Store
|
./web/.DS_Store
|
||||||
web/dist
|
./web/dist
|
||||||
web/dist-ssr
|
./web/dist-ssr
|
||||||
web/*.local
|
./web/*.local
|
||||||
web/.eslintcache
|
./web/.eslintcache
|
||||||
web/report.html
|
./web/report.html
|
||||||
web/vite.config.*.timestamp*
|
./web/vite.config.*.timestamp*
|
||||||
|
|
||||||
web/yarn.lock
|
./web/yarn.lock
|
||||||
web/npm-debug.log*
|
./web/npm-debug.log*
|
||||||
web/.pnpm-error.log*
|
./web/.pnpm-error.log*
|
||||||
web/.pnpm-debug.log
|
./web/.pnpm-debug.log
|
||||||
web/tests/**/coverage/
|
./web/tests/**/coverage/
|
||||||
web/.vscode/
|
./web/.vscode/
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
web/*.suo
|
./web/*.suo
|
||||||
web/*.ntvs*
|
./web/*.ntvs*
|
||||||
web/*.njsproj
|
./web/*.njsproj
|
||||||
web/*.sln
|
./web/*.sln
|
||||||
web/tsconfig.tsbuildinfo
|
./web/tsconfig.tsbuildinfo
|
||||||
|
|
||||||
dist/assets
|
dist/assets
|
||||||
dist/resource
|
dist/resource
|
||||||
@@ -267,10 +267,10 @@ dist/favicon.png
|
|||||||
dist/favicon.svg
|
dist/favicon.svg
|
||||||
dist/index.html
|
dist/index.html
|
||||||
|
|
||||||
template/tmp/*
|
./template/tmp/*
|
||||||
logs/*
|
./logs/*
|
||||||
app.yaml
|
./app.yaml
|
||||||
*.db
|
./*.db
|
||||||
.env
|
./.env
|
||||||
*.env
|
./*.env
|
||||||
|
|
||||||
|
91
README.md
Normal file
91
README.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Wireguard-UI
|
||||||
|
> wireguard的管理面板UI
|
||||||
|
|
||||||
|
## 安装(仅提供docker方式)
|
||||||
|
|
||||||
|
OS X & Linux & Windows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 先要创建一个配置文件 app.yaml
|
||||||
|
http:
|
||||||
|
port: 6687
|
||||||
|
endpoint: localhost:3100,localhost:6687
|
||||||
|
|
||||||
|
database:
|
||||||
|
driver: sqlite # sqlite时只填写db即可,目前仅支持sqlite | mysql | pgsql
|
||||||
|
host:
|
||||||
|
port:
|
||||||
|
user:
|
||||||
|
password:
|
||||||
|
db: wg
|
||||||
|
|
||||||
|
cache:
|
||||||
|
type: redis # 缓存类型 暂时仅支持redis
|
||||||
|
host: 192.168.1.1
|
||||||
|
port: 6379
|
||||||
|
password: pGhQKwj7DE7FbFL1
|
||||||
|
db: 15
|
||||||
|
|
||||||
|
file:
|
||||||
|
type: oss # 文件类型支持本地文件存储与阿里云oss存储
|
||||||
|
path: test/ # oss填写前缀目录
|
||||||
|
endpoint: # oss必填
|
||||||
|
accessId: # oss必填
|
||||||
|
accessSecret: # oss必填
|
||||||
|
bucketName: # oss必填
|
||||||
|
|
||||||
|
# 邮件设置
|
||||||
|
mail:
|
||||||
|
host:
|
||||||
|
port:
|
||||||
|
user:
|
||||||
|
password:
|
||||||
|
skipTls:
|
||||||
|
|
||||||
|
# 一些系统配置
|
||||||
|
wireguard:
|
||||||
|
restartMode: DELAY
|
||||||
|
delayTime: 20
|
||||||
|
|
||||||
|
# 其中依赖了redis等,自行安装一个即可
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 创建docker-compose.yaml
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
wg:
|
||||||
|
image: gitea.mrx.ltd/go-pkg/wireguard-srv:2.1.0
|
||||||
|
container_name: wg-srv
|
||||||
|
restart: always
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
network_mode: host
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
volumes:
|
||||||
|
- ./app.yaml:/app/app.yaml
|
||||||
|
- ./db:/app/db
|
||||||
|
- ./logs:/app/logs
|
||||||
|
- /etc/wireguard:/etc/wireguard
|
||||||
|
- /etc/localtime:/etc/localtime
|
||||||
|
```
|
||||||
|
```sh
|
||||||
|
默认账户密码
|
||||||
|
账户: admin
|
||||||
|
密码: admin123
|
||||||
|
```
|
||||||
|
|
||||||
|
## 示例
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"wireguard-ui/component"
|
"wireguard-ui/component"
|
||||||
"wireguard-ui/http/param"
|
"wireguard-ui/http/param"
|
||||||
@@ -77,17 +78,25 @@ func (DashboardApi) ConnectionList(c *gin.Context) {
|
|||||||
for _, iaip := range peer.AllowedIPs {
|
for _, iaip := range peer.AllowedIPs {
|
||||||
ipAllocation += iaip.String() + ","
|
ipAllocation += iaip.String() + ","
|
||||||
}
|
}
|
||||||
|
// 去除一下最右边的逗号
|
||||||
|
if len(ipAllocation) > 0 {
|
||||||
|
ipAllocation = strings.TrimRight(ipAllocation, ",")
|
||||||
|
}
|
||||||
connections = append(connections, vo.DataTraffic{
|
connections = append(connections, vo.DataTraffic{
|
||||||
Name: clientInfo.Name,
|
Name: clientInfo.Name,
|
||||||
Email: clientInfo.Email,
|
Email: clientInfo.Email,
|
||||||
IpAllocation: clientInfo.IpAllocation,
|
IpAllocation: ipAllocation,
|
||||||
Online: time.Since(peer.LastHandshakeTime).Minutes() < 3,
|
Online: time.Since(peer.LastHandshakeTime).Minutes() < 3,
|
||||||
ReceiveBytes: utils.FlowCalculation().Parse(peer.TransmitBytes),
|
ReceiveBytes: utils.FlowCalculation().Parse(peer.TransmitBytes),
|
||||||
TransmitBytes: utils.FlowCalculation().Parse(peer.ReceiveBytes),
|
TransmitBytes: utils.FlowCalculation().Parse(peer.ReceiveBytes),
|
||||||
ConnectEndpoint: ipAllocation,
|
ConnectEndpoint: peer.Endpoint.String(),
|
||||||
LastHandAt: peer.LastHandshakeTime.Format("2006-01-02 15:04:05"),
|
LastHandAt: peer.LastHandshakeTime.Format("2006-01-02 15:04:05"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(connections) <= 0 {
|
||||||
|
connections = []vo.DataTraffic{}
|
||||||
|
}
|
||||||
|
|
||||||
response.R(c).OkWithData(connections)
|
response.R(c).OkWithData(connections)
|
||||||
}
|
}
|
||||||
|
33
http/api/remote.go
Normal file
33
http/api/remote.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type remote struct{}
|
||||||
|
|
||||||
|
func Remote() remote {
|
||||||
|
return remote{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAuthClient
|
||||||
|
// @description: 添加授权客户端
|
||||||
|
// @receiver remote
|
||||||
|
// @param c
|
||||||
|
func (remote) SaveAuthClient(c *gin.Context) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAuthClient
|
||||||
|
// @description: 删除授权客户端
|
||||||
|
// @receiver remote
|
||||||
|
// @param c
|
||||||
|
func (remote) DeleteAuthClient(c *gin.Context) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientNodes
|
||||||
|
// @description: 获取客户端节点
|
||||||
|
// @receiver remote
|
||||||
|
// @param c
|
||||||
|
func (remote) GetClientNodes(c *gin.Context) {
|
||||||
|
return
|
||||||
|
}
|
@@ -50,13 +50,13 @@ func Authorization() gin.HandlerFunc {
|
|||||||
// 查询用户
|
// 查询用户
|
||||||
user, err := service.User().GetUserById(userClaims.ID)
|
user, err := service.User().GetUserById(userClaims.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.R(c).FailedWithError("用户不存在")
|
response.R(c).AuthorizationFailed("用户不存在")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Status != constant.Enabled {
|
if user.Status != constant.Enabled {
|
||||||
response.R(c).FailedWithError("用户状态异常,请联系管理员处理!")
|
response.R(c).AuthorizationFailed("用户状态异常,请联系管理员处理!")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -52,8 +52,8 @@ func RequestLog() gin.HandlerFunc {
|
|||||||
method := c.Request.Method // 请求方式
|
method := c.Request.Method // 请求方式
|
||||||
ip := c.ClientIP() // 取出IP
|
ip := c.ClientIP() // 取出IP
|
||||||
// 处理实际客户端IP
|
// 处理实际客户端IP
|
||||||
if c.Request.Header.Get("X-Real-IP") != "" {
|
if c.Request.Header.Get("X-Real-Ip") != "" {
|
||||||
ip = c.Request.Header.Get("X-Real-IP") // 这个是网关Nginx自定义的Header头
|
ip = c.Request.Header.Get("X-Real-Ip") // 这个是网关Nginx自定义的Header头
|
||||||
} else if c.Request.Header.Get("X-Forwarded-For") != "" {
|
} else if c.Request.Header.Get("X-Forwarded-For") != "" {
|
||||||
ip = c.Request.Header.Get("X-Forwarded-For") // 这个是网关Nginx自定义的Header头
|
ip = c.Request.Header.Get("X-Forwarded-For") // 这个是网关Nginx自定义的Header头
|
||||||
}
|
}
|
||||||
|
16
model/oauth_client.go
Normal file
16
model/oauth_client.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
// AuthClient
|
||||||
|
// @description: 认证客户端
|
||||||
|
type AuthClient struct {
|
||||||
|
Base
|
||||||
|
Name string `json:"name" gorm:"type:varchar(255);not null;comment: '客户端名称'"`
|
||||||
|
ClientID string `json:"clientID" gorm:"type:varchar(255);not null;comment: '客户端ID'"`
|
||||||
|
ClientKey string `json:"clientKey" gorm:"type:varchar(255);not null;comment: '客户端key'"`
|
||||||
|
ExpireAt string `json:"expireAt" gorm:"type:varchar(255);not null;comment: '过期时间'"`
|
||||||
|
IsEnabled int `json:"isEnabled" gorm:"type:int(1);not null;comment: '是否启用'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (AuthClient) TableName() string {
|
||||||
|
return "t_oauth_client"
|
||||||
|
}
|
@@ -45,7 +45,7 @@ func (script) GenerateConfig() error {
|
|||||||
Email: client.Email,
|
Email: client.Email,
|
||||||
PublicKey: client.Keys.PublicKey,
|
PublicKey: client.Keys.PublicKey,
|
||||||
PresharedKey: client.Keys.PresharedKey,
|
PresharedKey: client.Keys.PresharedKey,
|
||||||
AllowedIPS: client.AllowedIpsStr,
|
AllowedIPS: client.IpAllocationStr,
|
||||||
Endpoint: client.Endpoint,
|
Endpoint: client.Endpoint,
|
||||||
CreateUser: client.CreateUser,
|
CreateUser: client.CreateUser,
|
||||||
Enabled: cast.ToBool(client.Enabled),
|
Enabled: cast.ToBool(client.Enabled),
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
gdb "wireguard-ui/global/client"
|
gdb "wireguard-ui/global/client"
|
||||||
"wireguard-ui/global/constant"
|
"wireguard-ui/global/constant"
|
||||||
@@ -39,6 +40,11 @@ func (s user) CreateUser(user *model.User) (err error) {
|
|||||||
return s.Model(&model.User{}).Where("id = ?", user.Id).Updates(&updates).Error
|
return s.Model(&model.User{}).Where("id = ?", user.Id).Updates(&updates).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断账号是否已经存在
|
||||||
|
if _, err = s.GetUserByAccount(user.Account); err == nil {
|
||||||
|
return errors.New("账号已经存在,请勿重复创建!")
|
||||||
|
}
|
||||||
|
|
||||||
defaultPassword := utils.Password().GenerateHashPassword("admin123")
|
defaultPassword := utils.Password().GenerateHashPassword("admin123")
|
||||||
if user.Password == "" { // 没有密码给一个默认密码
|
if user.Password == "" { // 没有密码给一个默认密码
|
||||||
user.Password = defaultPassword
|
user.Password = defaultPassword
|
||||||
|
@@ -37,7 +37,6 @@ export async function addDynamicRoutes() {
|
|||||||
// 有token的情况
|
// 有token的情况
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const permissionStore = usePermissionStore()
|
const permissionStore = usePermissionStore()
|
||||||
!userStore.id && (await userStore.getUserInfo())
|
!userStore.id && (await userStore.getUserInfo())
|
||||||
|
|
||||||
@@ -49,8 +48,8 @@ export async function addDynamicRoutes() {
|
|||||||
router.addRoute(NOT_FOUND_ROUTE)
|
router.addRoute(NOT_FOUND_ROUTE)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
$message.error('初始化用户信息失败: ' + error)
|
|
||||||
userStore.logout()
|
userStore.logout()
|
||||||
|
$message.error('初始化用户信息失败: ' + error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ export default {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/client',
|
redirect: '/client',
|
||||||
meta: {
|
meta: {
|
||||||
|
title: '客户端',
|
||||||
order: 2,
|
order: 2,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@@ -6,6 +6,7 @@ export default {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/setting',
|
redirect: '/setting',
|
||||||
meta: {
|
meta: {
|
||||||
|
title: '设置',
|
||||||
order: 3,
|
order: 3,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@@ -6,6 +6,7 @@ export default {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/user',
|
redirect: '/user',
|
||||||
meta: {
|
meta: {
|
||||||
|
title: '管理员',
|
||||||
order: 1,
|
order: 1,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-40 text-14 opacity-60" style="cursor: pointer" @click="dailyPoe">{{ dailyPoetry.content || '莫向外求,但从心觅,行有不得,反求诸己。' }}</p>
|
<p class="mt-40 text-14 opacity-60">{{ dailyPoetry.content || '莫向外求,但从心觅,行有不得,反求诸己。' }}</p>
|
||||||
<p class="mt-32 text-right text-12 opacity-40">—— {{ dailyPoetry.author || '佚名' }}</p>
|
<p class="mt-32 text-right text-12 opacity-40">—— {{ dailyPoetry.author || '佚名' }}</p>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card class="ml-12 w-70%">
|
<n-card class="ml-12 w-70%">
|
||||||
@@ -233,7 +233,7 @@ async function getClientConnections() {
|
|||||||
|
|
||||||
const initFunc = debounce(() => {
|
const initFunc = debounce(() => {
|
||||||
getClientConnections()
|
getClientConnections()
|
||||||
dailyPoe()
|
// dailyPoe()
|
||||||
// connectionList()
|
// connectionList()
|
||||||
},500)
|
},500)
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ export default {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/workbench',
|
redirect: '/workbench',
|
||||||
meta: {
|
meta: {
|
||||||
|
title: '工作台',
|
||||||
order: 0,
|
order: 0,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
4838
web/stats.html
Normal file
4838
web/stats.html
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user