add an api proxy url to access frp server info

This commit is contained in:
杨黄林
2023-09-09 20:46:54 +08:00
parent 4636bd7e9f
commit 3eca20e927
18 changed files with 110 additions and 63 deletions

View File

@@ -14,6 +14,22 @@ section {
box-sizing: border-box; box-sizing: border-box;
} }
.layui-header{
line-height: 60px;
font-size: 16px;
}
.layui-title{
position: absolute;
left: 200px;
right: 0;
top: 0;
height: 100%;
color: #fff;
padding: 0 15px;
box-sizing: border-box;
}
#searchForm input { #searchForm input {
height: 30px; height: 30px;
line-height: 28px; line-height: 28px;

View File

@@ -5,37 +5,20 @@ var loadServerInfo = (function ($) {
* get server info * get server info
* @param lang {{}} language json * @param lang {{}} language json
*/ */
function loadServerInfo(lang) { function loadServerInfo(lang, title) {
console.log(title)
$("#title").text(title);
$('#content').empty(); $('#content').empty();
var loading = layui.layer.load(); var loading = layui.layer.load();
$.ajax({ $.getJSON('/proxy/api/serverinfo').done(function (result) {
url: 'http://127.0.0.1:7500/api/serverinfo', if (result.success) {
dataType: 'jsonp', renderServerInfo(JSON.parse(result.data));
success: function (result) { } else {
result = { layui.layer.msg(result.message);
"version": "0.51.3",
"bind_port": 7000,
"vhost_http_port": 80,
"vhost_https_port": 443,
"tcpmux_httpconnect_port": 0,
"kcp_bind_port": 7000,
"quic_bind_port": 0,
"subdomain_host": "frp.yanghuanglin.com",
"max_pool_count": 100,
"max_ports_per_client": 0,
"heart_beat_timeout": 90,
"total_traffic_in": 1669491,
"total_traffic_out": 54422369,
"cur_conns": 0,
"client_counts": 1,
"proxy_type_count": {"http": 9, "https": 8, "tcp": 7}
};
renderServerInfo(result);
},
complete: function () {
layui.layer.close(loading);
} }
}).always(function () {
layui.layer.close(loading);
}); });
} }

View File

@@ -140,7 +140,8 @@ var loadUserList = (function ($) {
* load i18n language * load i18n language
* @param lang {{}} language json * @param lang {{}} language json
*/ */
function loadUserList(lang) { function loadUserList(lang, title) {
$("#title").text(title);
var html = layui.laytpl($('#userListTemplate').html()).render(); var html = layui.laytpl($('#userListTemplate').html()).render();
$('#content').html(html); $('#content').html(html);

View File

@@ -4,9 +4,9 @@
$.getJSON('/lang').done(function (lang) { $.getJSON('/lang').done(function (lang) {
layui.element.on('nav(leftNav)', function (elem) { layui.element.on('nav(leftNav)', function (elem) {
if (elem.attr('id') === 'serverInfo') { if (elem.attr('id') === 'serverInfo') {
loadServerInfo(lang); loadServerInfo(lang, elem.text().trim());
} else if (elem.attr('id') === 'userList') { } else if (elem.attr('id') === 'userList') {
loadUserList(lang); loadUserList(lang, elem.text().trim());
} }
}); });

View File

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 322 KiB

View File

@@ -2,13 +2,13 @@
<html lang=""> <html lang="">
<head> <head>
<title>${ .UserManage }</title> <title>${ .UserManage }</title>
<link rel="stylesheet" href="./static/layui/css/layui.css"> <link rel="stylesheet" href="./static/lib/layui/css/layui.css">
<link rel="stylesheet" href="./static/css/layui-theme-dark.css"> <link rel="stylesheet" href="./static/css/layui-theme-dark.css">
<link rel="stylesheet" href="./static/css/index.css"> <link rel="stylesheet" href="./static/css/index.css">
<link rel="stylesheet" href="./static/css/index-color.css"> <link rel="stylesheet" href="./static/css/index-color.css">
<script src="./static/layui/layui.js"></script> <script src="./static/lib/layui/layui.js"></script>
<script src="./static/js/echarts.min.js"></script> <script src="./static/lib/echarts.min.js"></script>
<script src="./static/js/filesize.min.js"></script> <script src="./static/lib/filesize.min.js"></script>
<script src="./static/js/index-server-info.js"></script> <script src="./static/js/index-server-info.js"></script>
<script src="./static/js/index-user-list.js"></script> <script src="./static/js/index-user-list.js"></script>
<script src="./static/js/index.js"></script> <script src="./static/js/index.js"></script>
@@ -22,13 +22,7 @@
} }
</style> </style>
<style> <style>
.layui-header {
width: 200px;
right: auto;
}
.layui-layout-admin .layui-body { .layui-layout-admin .layui-body {
top: 0;
padding: 0; padding: 0;
} }
</style> </style>
@@ -37,6 +31,7 @@
<div class="layui-layout layui-layout-admin"> <div class="layui-layout layui-layout-admin">
<div class="layui-header"> <div class="layui-header">
<div class="layui-logo layui-hide-xs layui-bg-black">${ .FrpsMultiuser }</div> <div class="layui-logo layui-hide-xs layui-bg-black">${ .FrpsMultiuser }</div>
<div class="layui-title" id="title"></div>
</div> </div>
<div class="layui-side layui-bg-black"> <div class="layui-side layui-bg-black">
<div class="layui-side-scroll"> <div class="layui-side-scroll">

View File

@@ -10,7 +10,6 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
) )
@@ -103,24 +102,14 @@ func ParseConfigFile(file string) (controller.CommonInfo, map[string]controller.
log.Printf("fail to get [common] section from file %s : %v", file, err) log.Printf("fail to get [common] section from file %s : %v", file, err)
return common, nil, nil, nil, nil, iniFile, err return common, nil, nil, nil, nil, iniFile, err
} }
pluginAddr := commonSection.Key("plugin_addr").Value() common.PluginAddr = commonSection.Key("plugin_addr").MustString("0.0.0.0")
if len(pluginAddr) != 0 { common.PluginPort = commonSection.Key("plugin_port").MustInt(7200)
common.PluginAddr = pluginAddr
} else {
common.PluginAddr = "0.0.0.0"
}
pluginPort := commonSection.Key("plugin_port").Value()
if len(pluginPort) != 0 {
port, err := strconv.Atoi(pluginPort)
if err != nil {
return common, nil, nil, nil, nil, iniFile, err
}
common.PluginPort = port
} else {
common.PluginPort = 7200
}
common.User = commonSection.Key("admin_user").Value() common.User = commonSection.Key("admin_user").Value()
common.Pwd = commonSection.Key("admin_pwd").Value() common.Pwd = commonSection.Key("admin_pwd").Value()
common.DashboardAddr = commonSection.Key("dashboard_addr").MustString("127.0.0.1")
common.DashboardPort = commonSection.Key("dashboard_port").MustInt(7500)
common.DashboardUser = commonSection.Key("dashboard_user").Value()
common.DashboardPwd = commonSection.Key("dashboard_pwd").Value()
portsSection, err := iniFile.GetSection("ports") portsSection, err := iniFile.GetSection("ports")
if err != nil { if err != nil {

View File

@@ -1,9 +1,16 @@
; basic options ; basic options
[common] [common]
; frps config info
plugin_addr = 127.0.0.1 plugin_addr = 127.0.0.1
plugin_port = 7200 plugin_port = 7200
admin_user = admin admin_user = admin
admin_pwd = admin admin_pwd = admin
; frp dashboard config info
dashboard_addr = 127.0.0.1
dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = admin
; user tokens ; user tokens
[users] [users]

View File

@@ -1,16 +1,19 @@
package controller package controller
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
plugin "github.com/fatedier/frp/pkg/plugin/server" plugin "github.com/fatedier/frp/pkg/plugin/server"
ginI18n "github.com/gin-contrib/i18n" ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"io"
"log" "log"
"net/http" "net/http"
"regexp" "regexp"
"sort" "sort"
"strconv"
"strings" "strings"
) )
@@ -21,6 +24,7 @@ const (
SaveError = 3 SaveError = 3
UserFormatError = 4 UserFormatError = 4
TokenFormatError = 5 TokenFormatError = 5
FrpServerError = 6
) )
var UserFormatReg = regexp.MustCompile("^\\w+$") var UserFormatReg = regexp.MustCompile("^\\w+$")
@@ -38,10 +42,14 @@ type HTTPError struct {
} }
type CommonInfo struct { type CommonInfo struct {
PluginAddr string PluginAddr string
PluginPort int PluginPort int
User string User string
Pwd string Pwd string
DashboardAddr string
DashboardPort int
DashboardUser string
DashboardPwd string
} }
type TokenInfo struct { type TokenInfo struct {
@@ -67,6 +75,11 @@ type OperationResponse struct {
Message string `json:"message"` Message string `json:"message"`
} }
type ProxyResponse struct {
OperationResponse
Data string `json:"data"`
}
type TokenSearch struct { type TokenSearch struct {
TokenInfo TokenInfo
Page int `form:"page"` Page int `form:"page"`
@@ -632,3 +645,45 @@ func (c *HandleController) MakeEnableTokensFunc() func(context *gin.Context) {
context.JSON(http.StatusOK, &response) context.JSON(http.StatusOK, &response)
} }
} }
func (c *HandleController) MakeProxyFunc() func(context *gin.Context) {
return func(context *gin.Context) {
res := ProxyResponse{}
host := c.CommonInfo.DashboardAddr
port := c.CommonInfo.DashboardPort
requestUrl := "http://" + host + ":" + strconv.Itoa(port) + context.Param("serverApi")
request, _ := http.NewRequest("GET", requestUrl, nil)
username := c.CommonInfo.DashboardUser
if len(strings.TrimSpace(username)) != 0 {
password := c.CommonInfo.DashboardPwd
auth := []byte(username + ":" + password)
request.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString(auth))
}
response, err := http.DefaultClient.Do(request)
if err != nil {
res.Code = FrpServerError
res.Success = false
res.Message = err.Error()
context.JSON(http.StatusOK, &res)
return
}
res.Code = response.StatusCode
body, err := io.ReadAll(response.Body)
if err != nil {
res.Success = false
res.Message = err.Error()
} else {
if res.Code == http.StatusOK {
res.Success = true
res.Data = string(body)
} else {
res.Success = false
res.Message = string(body)
}
}
context.JSON(http.StatusOK, &res)
}
}

View File

@@ -54,6 +54,7 @@ func (c *HandleController) Register(rootDir string, engine *gin.Engine) {
group.POST("/remove", c.MakeRemoveTokensFunc()) group.POST("/remove", c.MakeRemoveTokensFunc())
group.POST("/disable", c.MakeDisableTokensFunc()) group.POST("/disable", c.MakeDisableTokensFunc())
group.POST("/enable", c.MakeEnableTokensFunc()) group.POST("/enable", c.MakeEnableTokensFunc())
group.GET("/proxy/*serverApi", c.MakeProxyFunc())
} }
func (c *HandleController) HandleLogin(content *plugin.LoginContent) plugin.Response { func (c *HandleController) HandleLogin(content *plugin.LoginContent) plugin.Response {