From 398a61b92c41ec04db315c3cb9dd2a85ca9e97a6 Mon Sep 17 00:00:00 2001 From: yanghuanglin Date: Wed, 20 Sep 2023 15:55:50 +0800 Subject: [PATCH] show basic info --- assets/lang/en.json | 3 +- assets/lang/zh.json | 3 +- assets/static/css/index.css | 24 +- assets/static/js/index-configure.js | 34 ++ assets/static/js/index-overview.js | 95 ++++ assets/static/js/index-proxy-list.js | 231 ++------ assets/static/js/index-server-info.js | 190 ------- assets/static/js/index-user-list.js | 730 -------------------------- assets/static/js/index.js | 14 +- assets/templates/index.html | 98 +++- config/frpc-panel.toml | 6 +- go.mod | 9 +- go.sum | 7 + pkg/server/controller/controller.go | 62 ++- pkg/server/controller/variables.go | 2 +- 15 files changed, 362 insertions(+), 1146 deletions(-) create mode 100644 assets/static/js/index-configure.js create mode 100644 assets/static/js/index-overview.js delete mode 100644 assets/static/js/index-server-info.js delete mode 100644 assets/static/js/index-user-list.js diff --git a/assets/lang/en.json b/assets/lang/en.json index 4357cf0..600f55e 100644 --- a/assets/lang/en.json +++ b/assets/lang/en.json @@ -6,5 +6,6 @@ "Please input username": "Please input username", "Please input password": "Please input password", "Login success": "Login success", - "Username or password incorrect": "Username or password incorrect" + "Username or password incorrect": "Username or password incorrect", + "Empty data": "Empty data" } \ No newline at end of file diff --git a/assets/lang/zh.json b/assets/lang/zh.json index d3704ae..1b28c6a 100644 --- a/assets/lang/zh.json +++ b/assets/lang/zh.json @@ -6,5 +6,6 @@ "Please input username": "请填写用户名", "Please input password": "请填写密码", "Login success": "登录成功", - "Username or password incorrect": "用户名或密码错误" + "Username or password incorrect": "用户名或密码错误", + "Empty data": "无数据" } \ No newline at end of file diff --git a/assets/static/css/index.css b/assets/static/css/index.css index 1e41270..4f837e3 100644 --- a/assets/static/css/index.css +++ b/assets/static/css/index.css @@ -131,4 +131,26 @@ section { width: 100%; text-align: center; font-size: 12px; -} \ No newline at end of file +} + +section.common-info { + display: flex; +} + +section.common-info .text-info { + flex: 1; +} + +section.common-info .text-info { + padding: 0 20px; +} + +section.common-info .text-row { + display: flex; + font-size: 14px; + line-height: 40px; +} + +section.common-info .text-row .text-col { + flex: 1; +} diff --git a/assets/static/js/index-configure.js b/assets/static/js/index-configure.js new file mode 100644 index 0000000..e18a9a5 --- /dev/null +++ b/assets/static/js/index-configure.js @@ -0,0 +1,34 @@ +var loadCommon = (function ($) { + var i18n = {}; + + /** + * get client info + * @param lang {Map} language json + * @param title {string} page title + */ + function loadCommon(lang, title) { + i18n = lang; + $("#title").text(title); + $('#content').empty(); + var loading = layui.layer.load(); + + $.getJSON('/proxy/api/config', { + type: 'none' + }).done(function (result) { + if (result.success) { + renderCommonInfo(result.data); + } else { + layui.layer.msg(result.message); + } + }).always(function () { + layui.layer.close(loading); + }); + } + + function renderCommonInfo(data) { + var html = layui.laytpl($('#commonTemplate').html()).render(data); + $('#content').html(html); + } + + return loadCommon; +})(layui.$); \ No newline at end of file diff --git a/assets/static/js/index-overview.js b/assets/static/js/index-overview.js new file mode 100644 index 0000000..c64023e --- /dev/null +++ b/assets/static/js/index-overview.js @@ -0,0 +1,95 @@ +var loadOverview = (function ($) { + var i18n = {}; + + /** + * get proxy info + * @param lang {{}} language json + * @param title page title + */ + function loadOverview(lang, title) { + i18n = lang; + $("#title").text(title); + $('#content').empty(); + var loading = layui.layer.load(); + + $.getJSON('/proxy/api/status').done(function (result) { + if (result.success) { + $('#content').html($('#overviewTableTemplate').html()); + renderOverviewTable(JSON.parse(result.data)); + } else { + layui.layer.msg(result.message); + } + }).always(function () { + layui.layer.close(loading); + }); + } + + /** + * render proxy list table + * @param data {Map>} proxy data + * @param proxyType proxy type + */ + function renderOverviewTable(data, proxyType) { + var dataList = []; + for (var type in data) { + var temp = data[type]; + dataList = dataList.concat(temp); + } + + var $section = $('#content > section'); + var cols = [ + {field: 'name', title: 'Name', sort: true}, + {field: 'type', title: 'Type', width: 100, sort: true}, + { + field: 'local_addr', + title: 'Local Address', + templet: '{{= d.local_addr || "-" }}', + width: 220, + sort: true + }, + {field: 'plugin', title: 'plugin', templet: '{{= d.plugin || "-" }}', sort: true}, + {field: 'remote_addr', title: 'Remote Address', sort: true}, + {field: 'status', title: 'Status', width: 100, sort: true}, + {field: 'err', title: 'Info',templet: '{{= d.err || "-" }}', width: 200} + ]; + + var overviewTable = layui.table.render({ + elem: '#overviewTable', + height: $section.height(), + text: {none: i18n['EmptyData']}, + cols: [cols], + page: navigator.language.indexOf("zh") !== -1, + data: dataList, + initSort: { + field: 'name', + type: 'asc' + } + }); + + window.onresize = function () { + overviewTable.resize(); + } + + bindFormEvent(); + } + + /** + * bind event of {{@link layui.form}} + */ + function bindFormEvent() { + layui.table.on('tool(proxyListTable)', function (obj) { + var data = obj.data; + + switch (obj.event) { + case 'update': + break; + // updatePopup(data); + case 'remove': + // removePopup(data); + break; + } + }); + } + + return loadOverview; +})(layui.$); \ No newline at end of file diff --git a/assets/static/js/index-proxy-list.js b/assets/static/js/index-proxy-list.js index 6034530..0e168a0 100644 --- a/assets/static/js/index-proxy-list.js +++ b/assets/static/js/index-proxy-list.js @@ -1,5 +1,4 @@ var loadProxyInfo = (function ($) { - var size = filesize.partial({base: 2, standard: "jedec"}); var i18n = {}; /** @@ -14,10 +13,12 @@ var loadProxyInfo = (function ($) { $('#content').empty(); var loading = layui.layer.load(); - $.getJSON('/proxy/api/proxy/' + proxyType).done(function (result) { + $.getJSON('/proxy/api/config', { + type: proxyType + }).done(function (result) { if (result.success) { $('#content').html($('#proxyListTableTemplate').html()); - renderProxyListTable(JSON.parse(result.data)['proxies'], proxyType); + renderProxyListTable(result.data, proxyType); } else { layui.layer.msg(result.message); } @@ -28,75 +29,26 @@ var loadProxyInfo = (function ($) { /** * render proxy list table - * @param data proxy data + * @param data {Map>} proxy data * @param proxyType proxy type */ function renderProxyListTable(data, proxyType) { - proxyType = proxyType.toLowerCase(); - data.forEach(function (temp) { - temp.conf = temp.conf || { - remote_port: 0, - use_encryption: false, - use_compression: false, - custom_domains: null, - subdomain: null, - locations: null, - host_header_rewrite: null - }; + var dataList = []; + for (var key in data) { + var temp = data[key]; + temp.name = key; + temp.local_ip = temp.local_ip || '-'; + temp.local_port = temp.local_port || '-'; + dataList.push(temp); + } - temp.client_version = temp.client_version || '-'; - temp.conf.custom_domains = temp.conf.custom_domains || '-'; - temp.conf.subdomain = temp.conf.subdomain || '-'; - temp.conf.locations = temp.conf.locations || '-'; - temp.conf.host_header_rewrite = temp.conf.host_header_rewrite || '-'; - - if (temp.conf.custom_domains !== '-') { - temp.conf.custom_domains = JSON.stringify(temp.conf.custom_domains); - } - if (proxyType === 'http') { - temp.conf.remote_port = http_port; - } else if (proxyType === 'https') { - temp.conf.remote_port = https_port; - } - }); var $section = $('#content > section'); var cols = [ - {field: 'id', type: 'space', width: 60, align: 'center', templet: '#toggleProxyInfoArrowTemplate'}, - {field: 'name', title: i18n['Name'], sort: true}, - { - field: 'port', - title: i18n['Port'], - width: '12%', - sort: true, - templet: '{{= d.conf.remote_port }}' - }, - {field: 'cur_conns', title: i18n['Connections'], minWidth: 140, width: '12%', sort: true}, - { - field: 'today_traffic_in', - title: i18n['TrafficIn'], - minWidth: 140, - width: '12%', - sort: true, - templet: function (d) { - return size(d.today_traffic_in); - } - }, - { - field: 'today_traffic_out', - title: i18n['TrafficOut'], - minWidth: 140, - width: '12%', - sort: true, - templet: function (d) { - return size(d.today_traffic_out); - } - }, - {field: 'client_version', title: i18n['ClientVersion'], minWidth: 140, width: '12%', sort: true}, - { - field: 'status', title: i18n['Status'], width: '12%', sort: true, templet: function (d) { - return '' + i18n[d.status] + ''; - } - } + {field: 'name', title: 'Name', sort: true}, + {field: 'type', title: 'Type', sort: true}, + {field: 'local_ip', title: 'Local Ip', sort: true}, + {field: 'local_port', title: 'Local Port', sort: true}, + {title: 'Operation', width: 150, toolbar: '#proxyListOperationTemplate'} ]; var proxyListTable = layui.table.render({ @@ -105,156 +57,37 @@ var loadProxyInfo = (function ($) { text: {none: i18n['EmptyData']}, cols: [cols], page: navigator.language.indexOf("zh") !== -1, - data: data, + data: dataList, initSort: { field: 'name', type: 'asc' - }, - done: function (res, curr, count, origin) { - //向每一行tr后面追加显示子table的tr - var $tr = $('.layui-table-view[lay-id=' + this.id + '] tbody tr'); - var expandTrTemplateHtml = $('#expandTrTemplate').html(); - for (var i = 0; i < $tr.length; i++) { - var datum = res.data[i]; - var useEncryption = datum.conf.use_encryption; - var useCompression = datum.conf.use_compression; - datum.conf.use_encryption = i18n[useEncryption]; - datum.conf.use_compression = i18n[useCompression]; - var html = layui.laytpl(expandTrTemplateHtml).render({ - index: i, - colspan: cols.length - 1, - proxyType: proxyType, - data: datum - }); - $($tr[i]).after(html); - } } }); - layui.table.on('tool(proxyListTable)', function (obj) { - var index = obj.index; - $(this).toggleClass('open'); - var open = $(this).hasClass('open'); - $('#childTr_' + index).toggleClass('layui-hide', !open); - proxyListTable.resize(); - }); - window.onresize = function () { proxyListTable.resize(); } + + bindFormEvent(); } /** - * load traffic statistics data + * bind event of {{@link layui.form}} */ - function loadTrafficStatistics() { - var proxyName = $(this).closest('.layui-row').find('input').val(); - var loading = layui.layer.load(); - $.getJSON('/proxy/api/traffic/' + proxyName).done(function (result) { - if (result.success) { - renderTrafficChart(JSON.parse(result.data)); - } else { - layui.layer.msg(result.message); - } - }).always(function () { - layui.layer.close(loading); - }); - } + function bindFormEvent() { + layui.table.on('tool(proxyListTable)', function (obj) { + var data = obj.data; - /** - * render traffic statistics chart - * @param data traffic data - */ - function renderTrafficChart(data) { - var html = layui.laytpl($('#trafficStaticTemplate').html()).render(); - var dates = []; - var now = new Date(); - for (var i = 0; i < data.traffic_in.length; i++) { - dates.push(now.getFullYear() + "/" + (now.getMonth() + 1) + "/" + now.getDate()); - now.setDate(now.getDate() - 1); - } - layui.layer.open({ - title: i18n['TrafficStatistics'], - type: 1, - content: html, - area: ['800px', '400px'], - success: function () { - var chartDom = document.getElementById('trafficBarChart'); - var chart = echarts.init(chartDom); - var option = { - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow', - }, - formatter: function (data) { - var html = '' - if (data.length > 0) { - html += data[0].name + '
' - } - for (var v of data) { - var colorEl = '' - html += colorEl + v.seriesName + ': ' + size(v.value) + '
' - } - return html - } - }, - legend: { - data: [i18n['TrafficIn'], i18n['TrafficOut']], - textStyle: { - textBorderColor: '#fff', - textBorderWidth: 2 - } - }, - grid: { - left: '3%', - right: '4%', - bottom: '3%', - containLabel: true, - }, - xAxis: [ - { - type: 'category', - data: dates.reverse() - } - ], - yAxis: [ - { - type: 'value', - axisLabel: { - formatter: function (value) { - return size(value) - } - } - } - ], - series: [ - { - name: i18n['TrafficIn'], - type: 'bar', - data: data.traffic_in.reverse(), - }, - { - name: i18n['TrafficOut'], - type: 'bar', - data: data.traffic_out.reverse(), - } - ] - }; - - option && chart.setOption(option); + switch (obj.event) { + case 'update': + break; + // updatePopup(data); + case 'remove': + // removePopup(data); + break; } }); } - /** - * document event - */ - (function bindDocumentEvent() { - $(document).on('click.trafficStatistics', '.traffic-statistics', function () { - loadTrafficStatistics.call(this); - }); - })(); - return loadProxyInfo; })(layui.$); \ No newline at end of file diff --git a/assets/static/js/index-server-info.js b/assets/static/js/index-server-info.js deleted file mode 100644 index bf91641..0000000 --- a/assets/static/js/index-server-info.js +++ /dev/null @@ -1,190 +0,0 @@ -var loadServerInfo = (function ($) { - var size = filesize.partial({base: 2, standard: "jedec"}); - var i18n = {}; - - /** - * get server info - * @param lang {{}} language json - * @param title page title - */ - function loadServerInfo(lang, title) { - i18n = lang; - $("#title").text(title); - $('#content').empty(); - var loading = layui.layer.load(); - - $.getJSON('/proxy/api/serverinfo').done(function (result) { - if (result.success) { - var data = JSON.parse(result.data); - data.proxy_counts = 0; - http_port = data.vhost_http_port; - https_port = data.vhost_https_port; - for (var proxy in data.proxy_type_count) { - data.proxy_counts = data.proxy_counts + data.proxy_type_count[proxy]; - } - data.bind_port = data.bind_port || i18n['Disable']; - data.kcp_bind_port = data.kcp_bind_port || i18n['Disable']; - data.quic_bind_port = data.quic_bind_port || i18n['Disable']; - data.vhost_http_port = data.vhost_http_port || i18n['Disable']; - data.vhost_https_port = data.vhost_https_port || i18n['Disable']; - data.tcpmux_httpconnect_port = data.tcpmux_httpconnect_port || i18n['Disable']; - data.subdomain_host = data.subdomain_host || i18n['NotSet']; - data.max_pool_count = data.max_pool_count || i18n['NotSet']; - data.max_ports_per_client = data.max_ports_per_client || i18n['NotLimit']; - data.heart_beat_timeout = data.heart_beat_timeout || i18n['NotSet']; - data.allow_ports_str = data.allow_ports_str || i18n['NotLimit']; - data.tls_only = i18n[data.tls_only || false]; - renderServerInfo(data); - } else { - layui.layer.msg(result.message); - } - }).always(function () { - layui.layer.close(loading); - }); - } - - /** - * render server info page - * @param data server info data - */ - function renderServerInfo(data) { - var html = layui.laytpl($('#serverInfoTemplate').html()).render(data); - $('#content').html(html); - $('#frpVersion').text(data.version); - - renderTrafficChart(data); - renderCountChart(data); - } - - /** - * render traffic chart with echarts - * @param data traffic data - */ - function renderTrafficChart(data) { - var chartLegend = [i18n['TrafficIn'], i18n['TrafficOut']]; - var chartData = [ - {value: data.total_traffic_in, name: i18n['TrafficIn']}, - {value: data.total_traffic_out, name: i18n['TrafficOut']} - ]; - var chartDom = document.getElementById('trafficPieChart'); - var chart = echarts.init(chartDom); - var option = { - title: { - text: i18n['NetworkTraffic'], - subtext: i18n['today'], - left: 'center', - textStyle: { - textBorderColor: '#fff', - textBorderWidth: 2 - }, - subtextStyle: { - textBorderColor: '#fff', - textBorderWidth: 2 - } - }, - tooltip: { - trigger: 'item', - formatter: function (v) { - return size(v.value) + ' (' + v.percent + '%)'; - }, - }, - legend: { - orient: 'vertical', - left: 'left', - data: chartLegend, - textStyle: { - textBorderColor: '#fff', - textBorderWidth: 2 - } - }, - series: [ - { - type: 'pie', - radius: '55%', - center: ['50%', '60%'], - data: chartData, - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' - } - } - } - ] - }; - - option && chart.setOption(option); - } - - /** - * render proxy count chat with echarts - * @param data proxy count data - */ - function renderCountChart(data) { - var proxies = data.proxy_type_count; - var chartLegend = []; - var chartData = []; - - for (var type in proxies) { - var temp = { - name: type.toUpperCase(), - value: proxies[type] - }; - chartLegend.push(type.toUpperCase()); - chartData.push(temp); - } - - var chartDom = document.getElementById('countPieChart'); - var chart = echarts.init(chartDom); - var option = { - title: { - text: i18n['Proxy'], - subtext: i18n['now'], - left: 'center', - textStyle: { - textBorderColor: '#fff', - textBorderWidth: 2 - }, - subtextStyle: { - textBorderColor: '#fff', - textBorderWidth: 2 - } - }, - tooltip: { - trigger: 'item', - formatter: function (v) { - return v.value + ' (' + v.percent + '%)'; - } - }, - legend: { - orient: 'vertical', - left: 'left', - data: chartLegend, - textStyle: { - textBorderColor: '#fff', - textBorderWidth: 2 - } - }, - series: [ - { - type: 'pie', - radius: '55%', - center: ['50%', '60%'], - data: chartData, - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' - } - } - } - ] - }; - - option && chart.setOption(option); - } - - return loadServerInfo; -})(layui.$); \ No newline at end of file diff --git a/assets/static/js/index-user-list.js b/assets/static/js/index-user-list.js deleted file mode 100644 index 0b5ae3e..0000000 --- a/assets/static/js/index-user-list.js +++ /dev/null @@ -1,730 +0,0 @@ -var loadUserList = (function ($) { - var i18n = {}; - var apiType = { - Remove: 1, - Enable: 2, - Disable: 3 - }; - var verifyRules = { - user: function (value, item) { - var result = verifyUser(value); - if (!result.valid) { - return i18n['UserFormatError']; - } - if (item != null) { - if (typeof item === "function") { - item && item(result.trim); - } else { - $(item).val(result.trim); - } - } - }, - token: function (value, item) { - var result = verifyToken(value); - if (!result.valid) { - return i18n['TokenFormatError']; - } - if (item != null) { - if (typeof item === "function") { - item && item(result.trim); - } else { - $(item).val(result.trim); - } - } - }, - comment: function (value, item) { - var result = verifyComment(value); - if (!result.valid) { - return i18n['CommentInvalid']; - } - if (item != null) { - if (typeof item === "function") { - item && item(result.trim); - } else { - $(item).val(result.trim); - } - } - }, - ports: function (value, item) { - var result = verifyPorts(value); - if (!result.valid) { - return i18n['PortsInvalid']; - } - if (item != null) { - if (typeof item === "function") { - item && item(result.trim); - } else { - $(item).val(result.trim); - } - } - }, - domains: function (value, item) { - var result = verifyDomains(value); - if (!result.valid) { - return i18n['DomainsInvalid']; - } - if (item != null) { - if (typeof item === "function") { - item && item(result.trim); - } else { - $(item).val(result.trim); - } - } - }, - subdomains: function (value, item) { - var result = verifySubdomains(value); - if (!result.valid) { - return i18n['SubdomainsInvalid']; - } - if (item != null) { - if (typeof item === "function") { - item && item(result.trim); - } else { - $(item).val(result.trim); - } - } - } - }; - - /** - * verify user value - * @param username - */ - function verifyUser(username) { - var valid = true; - if (username.trim() === '' || !/^\w+$/.test(username)) { - valid = false; - } - return { - valid: valid, - trim: username.trim() - }; - } - - /** - * verify token value - * @param token - */ - function verifyToken(token) { - var valid = true; - if (token.trim() === '' || !/^[\w!@#$%^&*()]+$/.test(token)) { - valid = false; - } - return { - valid: valid, - trim: token.trim() - }; - } - - /** - * verify comment is valid - * @param comment - * - * @return {{valid:boolean, trim:string}} - */ - function verifyComment(comment) { - var valid = true; - if (comment.trim() !== '' && /[\n\t\r]/.test(comment)) { - valid = false; - } - return { - valid: valid, - trim: comment.trim().replace(/[\n\t\r]/g, '') - }; - } - - /** - * verify ports is valid - * @param ports - * - * @return {{valid:boolean, trim:string}} - */ - function verifyPorts(ports) { - var valid = true; - if (ports.trim() !== '') { - try { - ports.split(",").forEach(function (port) { - if (/^\s*\d{1,5}\s*$/.test(port)) { - if (parseInt(port) < 1 || parseInt(port) > 65535) { - valid = false; - } - } else if (/^\s*\d{1,5}\s*-\s*\d{1,5}\s*$/.test(port)) { - var portRange = port.split('-'); - if (parseInt(portRange[0]) < 1 || parseInt(portRange[0]) > 65535) { - valid = false; - } else if (parseInt(portRange[1]) < 1 || parseInt(portRange[1]) > 65535) { - valid = false; - } else if (parseInt(portRange[0]) > parseInt(portRange[1])) { - valid = false; - } - } else { - valid = false; - } - if (valid === false) { - throw 'break'; - } - }); - } catch (e) { - } - } - return { - valid: valid, - trim: ports.replace(/\s/g, '') - }; - } - - /** - * verify domains is valid - * @param domains - * - * @return {{valid:boolean, trim:string}} - */ - function verifyDomains(domains) { - var valid = true; - if (domains.trim() !== '') { - try { - domains.split(',').forEach(function (domain) { - if (!/^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,}$/.test(domain.trim())) { - valid = false; - throw 'break'; - } - }); - } catch (e) { - } - } - return { - valid: valid, - trim: domains.replace(/\s/g, '') - }; - } - - /** - * verify subdomains is valid - * @param subdomains - * - * @return {{valid:boolean, trim:string}} - */ - function verifySubdomains(subdomains) { - var valid = true; - if (subdomains.trim() !== '') { - try { - subdomains.split(',').forEach(function (subdomain) { - if (!/^[a-zA-z0-9][a-zA-z0-9-]{0,19}$/.test(subdomain.trim())) { - valid = false; - throw 'break'; - } - }); - } catch (e) { - } - } - return { - valid: valid, - trim: subdomains.replace(/\s/g, '') - }; - } - - /** - * set verify rule of layui.form - */ - (function setFormVerifyRule() { - layui.form.verify(verifyRules); - })(); - - /** - * load i18n language - * @param lang {{}} language json - * @param title page title - */ - function loadUserList(lang, title) { - i18n = lang; - $("#title").text(title); - var html = layui.laytpl($('#userListTemplate').html()).render(); - $('#content').html(html); - - var $section = $('#content > section'); - layui.table.render({ - elem: '#tokenTable', - height: $section.height() - $('#searchForm').height() + 8, - text: {none: i18n['EmptyData']}, - url: '/tokens', - method: 'get', - where: {}, - dataType: 'json', - editTrigger: 'dblclick', - page: { - layout: navigator.language.indexOf("zh") === -1 ? ['first', 'prev', 'next', 'last'] : ['prev', 'page', 'next', 'skip', 'count', 'limit'] - }, - toolbar: '#userListToolbarTemplate', - defaultToolbar: false, - cols: [[ - {type: 'checkbox'}, - {field: 'user', title: i18n['User'], width: 150, sort: true}, - {field: 'token', title: i18n['Token'], width: 200, sort: true, edit: true}, - {field: 'comment', title: i18n['Notes'], sort: true, edit: 'textarea'}, - {field: 'ports', title: i18n['AllowedPorts'], sort: true, edit: 'textarea'}, - {field: 'domains', title: i18n['AllowedDomains'], sort: true, edit: 'textarea'}, - {field: 'subdomains', title: i18n['AllowedSubdomains'], sort: true, edit: 'textarea'}, - { - field: 'enable', - title: i18n['Status'], - width: 100, - templet: '{{d.enable? "' + i18n['Enable'] + '":"' + i18n['Disable'] + '"}}', - sort: true - }, - {title: i18n['Operation'], width: 150, toolbar: '#userListOperationTemplate'} - ]], - parseData: function (res) { - res.data.forEach(function (data) { - data.ports = data.ports.join(','); - data.domains = data.domains.join(','); - data.subdomains = data.subdomains.join(','); - }); - } - }); - - bindFormEvent(); - } - - /** - * bind event of {{@link layui.form}} - */ - function bindFormEvent() { - layui.table.on('edit(tokenTable)', function (obj) { - var field = obj.field; - var value = obj.value; - var oldValue = obj.oldValue; - - var before = $.extend(true, {}, obj.data); - var after = $.extend(true, {}, obj.data); - var verifyMsg = false; - if (field === 'token') { - verifyMsg = verifyRules.token(value, function (trim) { - updateTableField(obj, field, trim) - }); - if (verifyMsg) { - layui.layer.msg(verifyMsg); - return obj.reedit(); - } - - before.token = oldValue; - after.token = value; - } else if (field === 'comment') { - verifyMsg = verifyRules.comment(value, function (trim) { - updateTableField(obj, field, trim) - }); - if (verifyMsg) { - layui.layer.msg(verifyMsg); - return obj.reedit(); - } - - before.comment = oldValue; - after.comment = value; - } else if (field === 'ports') { - verifyMsg = verifyRules.ports(value, function (trim) { - updateTableField(obj, field, trim) - }); - if (verifyMsg) { - layui.layer.msg(verifyMsg); - return obj.reedit(); - } - - before.ports = oldValue; - after.ports = value; - } else if (field === 'domains') { - verifyMsg = verifyRules.domains(value, function (trim) { - updateTableField(obj, field, trim) - }); - if (verifyMsg) { - layui.layer.msg(verifyMsg); - return obj.reedit(); - } - - before.domains = oldValue; - after.domains = value; - } else if (field === 'subdomains') { - verifyMsg = verifyRules.subdomains(value, function (trim) { - updateTableField(obj, field, trim) - }); - if (verifyMsg) { - layui.layer.msg(verifyMsg); - return obj.reedit(); - } - - before.subdomains = oldValue; - after.subdomains = value; - } - - before.ports = before.ports.split(',') - before.domains = before.domains.split(',') - before.subdomains = before.subdomains.split(',') - after.ports = after.ports.split(',') - after.domains = after.domains.split(',') - after.subdomains = after.subdomains.split(',') - - update(before, after); - }); - - layui.table.on('toolbar(tokenTable)', function (obj) { - var id = obj.config.id; - var checkStatus = layui.table.checkStatus(id); - var data = checkStatus.data; - - data.forEach(function (temp) { - temp.ports = temp.ports.split(',') - temp.ports.forEach(function (port, index) { - if (/^\d+$/.test(String(port))) { - temp.ports[index] = parseInt(String(port)); - } - }); - - temp.domains = temp.domains.split(',') - temp.subdomains = temp.subdomains.split(',') - }); - - switch (obj.event) { - case 'add': - addPopup(); - break - case 'remove': - batchRemovePopup(data); - break - case 'disable': - batchDisablePopup(data); - break - case 'enable': - batchEnablePopup(data); - break - } - }); - - layui.table.on('tool(tokenTable)', function (obj) { - var data = obj.data; - - data.ports = data.ports.split(',') - data.ports.forEach(function (port, index) { - if (/^\d+$/.test(String(port))) { - data.ports[index] = parseInt(String(port)); - } - }); - data.domains = data.domains.split(',') - data.subdomains = data.subdomains.split(',') - switch (obj.event) { - case 'remove': - removePopup(data); - break; - case 'disable': - disablePopup(data); - break; - case 'enable': - enablePopup(data); - break - } - }); - } - - /** - * update layui table data - * @param obj table update obj - * @param field update field - * @param trim new value - */ - function updateTableField(obj, field, trim) { - var newData = {}; - newData[field] = trim; - obj.update(newData); - } - - /** - * add user popup - */ - function addPopup() { - layui.layer.open({ - type: 1, - title: i18n['NewUser'], - area: ['500px'], - content: layui.laytpl(document.getElementById('addUserTemplate').innerHTML).render(), - btn: [i18n['Confirm'], i18n['Cancel']], - btn1: function (index) { - if (layui.form.validate('#addUserForm')) { - var formData = layui.form.val('addUserForm'); - if (formData.ports != null) { - formData.ports = formData.ports.split(',') - formData.ports.forEach(function (port, index) { - if (/^\d+$/.test(String(port))) { - formData.ports[index] = parseInt(String(port)); - } - }) - } - if (formData.domains != null) { - formData.domains = formData.domains.split(',') - } - if (formData.subdomains != null) { - formData.subdomains = formData.subdomains.split(',') - } - add(formData, index); - } - }, - btn2: function (index) { - layui.layer.close(index); - } - }); - } - - /** - * add user action - * @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data - * @param index popup index - */ - function add(data, index) { - var loading = layui.layer.load(); - $.ajax({ - url: '/add', - type: 'post', - contentType: 'application/json', - data: JSON.stringify(data), - success: function (result) { - if (result.success) { - reloadTable(); - layui.layer.close(index); - layui.layer.msg(i18n['OperateSuccess'], function (index) { - layui.layer.close(index); - }); - } else { - errorMsg(result); - } - }, - complete: function () { - layui.layer.close(loading); - } - }); - } - - /** - * update user action - * @param before {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} data before update - * @param after {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} data after update - */ - function update(before, after) { - before.ports.forEach(function (port, index) { - if (/^\d+$/.test(String(port))) { - before.ports[index] = parseInt(String(port)); - } - }); - after.ports.forEach(function (port, index) { - if (/^\d+$/.test(String(port)) && typeof port === "string") { - after.ports[index] = parseInt(String(port)); - } - }); - var loading = layui.layer.load(); - $.ajax({ - url: '/update', - type: 'post', - contentType: 'application/json', - data: JSON.stringify({ - before: before, - after: after, - }), - success: function (result) { - if (result.success) { - layui.layer.msg(i18n['OperateSuccess']); - } else { - errorMsg(result); - } - }, - complete: function () { - layui.layer.close(loading); - } - }); - } - - /** - * batch remove user popup - * @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list - */ - function batchRemovePopup(data) { - if (data.length === 0) { - layui.layer.msg(i18n['ShouldCheckUser']); - return; - } - layui.layer.confirm(i18n['ConfirmRemoveUser'], { - title: i18n['OperationConfirm'], - btn: [i18n['Confirm'], i18n['Cancel']] - }, function (index) { - operate(apiType.Remove, data, index); - }); - } - - /** - * batch disable user popup - * @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list - */ - function batchDisablePopup(data) { - if (data.length === 0) { - layui.layer.msg(i18n['ShouldCheckUser']); - return; - } - layui.layer.confirm(i18n['ConfirmDisableUser'], { - title: i18n['OperationConfirm'], - btn: [i18n['Confirm'], i18n['Cancel']] - }, function (index) { - operate(apiType.Disable, data, index); - }); - } - - /** - * batch enable user popup - * @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list - */ - function batchEnablePopup(data) { - if (data.length === 0) { - layui.layer.msg(i18n['ShouldCheckUser']); - return; - } - layui.layer.confirm(i18n['ConfirmEnableUser'], { - title: i18n['OperationConfirm'], - btn: [i18n['Confirm'], i18n['Cancel']] - }, function (index) { - operate(apiType.Enable, data, index); - }); - } - - /** - * remove one user popup - * @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data - */ - function removePopup(data) { - layui.layer.confirm(i18n['ConfirmRemoveUser'], { - title: i18n['OperationConfirm'], - btn: [i18n['Confirm'], i18n['Cancel']] - }, function (index) { - operate(apiType.Remove, [data], index); - }); - } - - /** - * disable one user popup - * @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data - */ - function disablePopup(data) { - layui.layer.confirm(i18n['ConfirmDisableUser'], { - title: i18n['OperationConfirm'], - btn: [i18n['Confirm'], i18n['Cancel']] - }, function (index) { - operate(apiType.Disable, [data], index); - }); - } - - /** - * enable one user popup - * @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data - */ - function enablePopup(data) { - layui.layer.confirm(i18n['ConfirmEnableUser'], { - title: i18n['OperationConfirm'], - btn: [i18n['Confirm'], i18n['Cancel']] - }, function (index) { - operate(apiType.Enable, [data], index); - }); - } - - /** - * operate actions - * @param type {apiType} action type - * @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list - * @param index popup index - */ - function operate(type, data, index) { - var url; - var extendMessage = ''; - if (type === apiType.Remove) { - url = "/remove"; - extendMessage = ', ' + i18n['RemoveUser'] + i18n['TakeTimeMakeEffective']; - } else if (type === apiType.Disable) { - url = "/disable"; - extendMessage = ', ' + i18n['RemoveUser'] + i18n['TakeTimeMakeEffective']; - } else if (type === apiType.Enable) { - url = "/enable"; - } else { - layer.layer.msg(i18n['OperateError']); - return; - } - var loading = layui.layer.load(); - $.post({ - url: url, - type: 'post', - contentType: 'application/json', - data: JSON.stringify({ - users: data - }), - success: function (result) { - if (result.success) { - reloadTable(); - layui.layer.close(index); - layui.layer.msg(i18n['OperateSuccess'] + extendMessage, function (index) { - layui.layer.close(index); - }); - } else { - errorMsg(result); - } - }, - complete: function () { - layui.layer.close(loading); - } - }); - } - - /** - * reload user table - */ - function reloadTable() { - var searchData = layui.form.val('searchForm'); - layui.table.reloadData('tokenTable', { - where: searchData - }, true) - } - - /** - * show error message popup - * @param result - */ - function errorMsg(result) { - var reason = i18n['OtherError']; - if (result.code === 1) - reason = i18n['ParamError']; - else if (result.code === 2) - reason = i18n['UserExist']; - else if (result.code === 3) - reason = i18n['UserNotExist']; - else if (result.code === 4) - reason = i18n['ParamError']; - else if (result.code === 5) - reason = i18n['UserFormatError']; - else if (result.code === 6) - reason = i18n['TokenFormatError']; - else if (result.code === 7) - reason = i18n['CommentInvalid']; - else if (result.code === 8) - reason = i18n['PortsInvalid']; - else if (result.code === 9) - reason = i18n['DomainsInvalid']; - else if (result.code === 10) - reason = i18n['SubdomainsInvalid']; - layui.layer.msg(i18n['OperateFailed'] + ',' + reason) - } - - /** - * document event - */ - (function bindDocumentEvent() { - $(document).on('click.search', '#searchBtn', function () { - reloadTable(); - return false; - }).on('click.reset', '#resetBtn', function () { - $('#searchForm')[0].reset(); - reloadTable(); - return false; - }); - })(); - - return loadUserList; -})(layui.$) diff --git a/assets/static/js/index.js b/assets/static/js/index.js index 351f299..759dab7 100644 --- a/assets/static/js/index.js +++ b/assets/static/js/index.js @@ -17,14 +17,16 @@ var http_port, https_port; layui.element.on('nav(leftNav)', function (elem) { var id = elem.attr('id'); var title = elem.text(); - if (id === 'serverInfo') { - loadServerInfo(lang, title.trim()); - } else if (id === 'userList') { - loadUserList(lang, title.trim()); - } else if (elem.closest('.layui-nav-item').attr('id') === 'proxyList') { + if (id === 'overview') { + loadOverview(lang, title.trim()); + } else if (elem.closest('.layui-nav-item').attr('id') === 'configure') { if (id != null && id.trim() !== '') { var suffix = elem.closest('.layui-nav-item').children('a').text().trim(); - loadProxyInfo(lang, title + " " + suffix, id); + if (id === 'common') { + loadCommon(lang, title + " " + suffix); + } else { + loadProxyInfo(lang, title + " " + suffix, id); + } } } }); diff --git a/assets/templates/index.html b/assets/templates/index.html index ff3d27b..278375f 100644 --- a/assets/templates/index.html +++ b/assets/templates/index.html @@ -7,8 +7,8 @@ - - + + @@ -29,12 +29,12 @@
  • Overview
  • -
  • - Configure -
  • -
  • - Proxies +
  • + Configure
    +
    + Common +
    TCP
    @@ -64,10 +64,82 @@
    - - + + @@ -77,5 +149,13 @@
    + + + diff --git a/config/frpc-panel.toml b/config/frpc-panel.toml index 9785b8c..7d6d4ba 100644 --- a/config/frpc-panel.toml +++ b/config/frpc-panel.toml @@ -14,8 +14,8 @@ tls_mode = false #tls_key_file = "cert.key" # frpc dashboard info -dashboard_addr = "127.0.0.1" -dashboard_port = 7400 +dashboard_addr = "home.frp.yanghuanglin.com" +dashboard_port = 80 dashboard_user = "admin" -dashboard_pwd = "admin" +dashboard_pwd = "19910621" diff --git a/go.mod b/go.mod index 5211096..4a10a7a 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/gin-contrib/i18n v1.0.0 github.com/gin-contrib/sessions v0.0.5 github.com/gin-gonic/gin v1.9.1 - github.com/pelletier/go-toml/v2 v2.0.9 github.com/spf13/cobra v0.0.3 golang.org/x/text v0.11.0 ) @@ -17,6 +16,7 @@ require ( github.com/bytedance/sonic v1.10.0-rc3 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.0 // indirect + github.com/coreos/go-oidc v2.2.1+incompatible // indirect github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb // indirect github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect @@ -25,6 +25,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/protobuf v1.5.0 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect @@ -36,13 +37,19 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nicksnyder/go-i18n/v2 v2.2.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect + github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.11.0 // indirect golang.org/x/net v0.12.0 // indirect + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect golang.org/x/sys v0.10.0 // indirect + google.golang.org/appengine v1.4.0 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/square/go-jose.v2 v2.4.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3aadbdb..27fd44b 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -85,6 +86,7 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -167,6 +169,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -209,6 +212,7 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks= github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -236,6 +240,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -284,6 +289,7 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -301,6 +307,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/server/controller/controller.go b/pkg/server/controller/controller.go index 9a443c0..25ea325 100644 --- a/pkg/server/controller/controller.go +++ b/pkg/server/controller/controller.go @@ -3,8 +3,10 @@ package controller import ( "crypto/tls" "fmt" + "github.com/fatedier/frp/pkg/config" ginI18n "github.com/gin-contrib/i18n" "github.com/gin-gonic/gin" + "github.com/vaughan0/go-ini" "io" "log" "net/http" @@ -68,7 +70,9 @@ func (c *HandleController) MakeIndexFunc() func(context *gin.Context) { func (c *HandleController) MakeLangFunc() func(context *gin.Context) { return func(context *gin.Context) { - context.JSON(http.StatusOK, gin.H{}) + context.JSON(http.StatusOK, gin.H{ + "EmptyData": ginI18n.MustGetMessage(context, "Empty data"), + }) } } @@ -386,7 +390,8 @@ func (c *HandleController) MakeProxyFunc() func(context *gin.Context) { res := ProxyResponse{} host := c.CommonInfo.DashboardAddr port := c.CommonInfo.DashboardPort - requestUrl := protocol + host + ":" + strconv.Itoa(port) + context.Param("serverApi") + serverApi := context.Param("serverApi") + requestUrl := protocol + host + ":" + strconv.Itoa(port) + serverApi request, _ := http.NewRequest("GET", requestUrl, nil) username := c.CommonInfo.DashboardUser password := c.CommonInfo.DashboardPwd @@ -416,13 +421,62 @@ func (c *HandleController) MakeProxyFunc() func(context *gin.Context) { if res.Code == http.StatusOK { res.Success = true res.Data = string(body) - res.Message = "Proxy to " + requestUrl + " success" + res.Message = fmt.Sprintf("Proxy to %s success", requestUrl) } else { res.Success = false - res.Message = "Proxy to " + requestUrl + " error: " + string(body) + if res.Code == http.StatusNotFound { + res.Message = fmt.Sprintf("Proxy to %s error: url not found", requestUrl) + } else { + res.Message = fmt.Sprintf("Proxy to %s error: %s", requestUrl, string(body)) + } } } log.Printf(res.Message) + + if serverApi == "/api/config" { + proxyType, _ := context.GetQuery("type") + content := fmt.Sprintf("%s", res.Data) + configure, err := parseConfigure(content, trimString(proxyType)) + + if err != nil { + res.Success = false + res.Message = err.Error() + } else { + res.Data = configure + } + } + context.JSON(http.StatusOK, &res) } } + +func parseConfigure(content, proxyType string) (interface{}, error) { + + if proxyType == "none" { + common, err := config.UnmarshalClientConfFromIni(content) + + if err != nil { + return nil, err + } + + return common, nil + } + + cfg, err := ini.Load(strings.NewReader(content)) + proxyList := make(map[string]ini.Section) + for name, section := range cfg { + if name == "common" { + continue + } + if proxyType != "" && strings.ToLower(section["type"]) != strings.ToLower(proxyType) { + continue + } + proxyList[name] = section + } + + if err != nil { + return nil, err + } else { + return proxyList, nil + } +} diff --git a/pkg/server/controller/variables.go b/pkg/server/controller/variables.go index 8b4d85b..9f7fd6f 100644 --- a/pkg/server/controller/variables.go +++ b/pkg/server/controller/variables.go @@ -105,7 +105,7 @@ type OperationResponse struct { type ProxyResponse struct { OperationResponse - Data string `json:"data"` + Data any `json:"data"` } type TokenSearch struct {