summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-frpc
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2020-02-02 22:30:28 +0100
committerGitHub <noreply@github.com>2020-02-02 22:30:28 +0100
commit3d63732c4e22260148761ef57afb3500cb5a0fab (patch)
treed9257b0c3c9230dd0d2527fdcb1e0094c25f01ce /applications/luci-app-frpc
parenta50e25ddfd7ff0671d505aeb39208de3378ad33c (diff)
parent51d38ee06dc871027387735185af5e2c4cb93736 (diff)
Merge pull request #3173 from ysc3839/frp
[RFC] luci-app-frp: add new packages
Diffstat (limited to 'applications/luci-app-frpc')
-rw-r--r--applications/luci-app-frpc/Makefile13
-rw-r--r--applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js173
-rw-r--r--applications/luci-app-frpc/po/templates/frpc.pot356
-rw-r--r--applications/luci-app-frpc/root/usr/share/luci/menu.d/luci-app-frpc.json9
-rw-r--r--applications/luci-app-frpc/root/usr/share/rpcd/acl.d/luci-app-frpc.json8
5 files changed, 559 insertions, 0 deletions
diff --git a/applications/luci-app-frpc/Makefile b/applications/luci-app-frpc/Makefile
new file mode 100644
index 000000000..90a20c4bf
--- /dev/null
+++ b/applications/luci-app-frpc/Makefile
@@ -0,0 +1,13 @@
+# This is free software, licensed under the Apache License, Version 2.0
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI Support for frp client
+LUCI_DEPENDS:=+frpc
+
+PKG_LICENSE:=Apache-2.0
+PKG_MAINTAINER:=Richard Yu <yurichard3839@gmail.com>
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js b/applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js
new file mode 100644
index 000000000..da86569f1
--- /dev/null
+++ b/applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js
@@ -0,0 +1,173 @@
+'use strict';
+'require ui';
+'require form';
+'require tools.widgets as widgets';
+
+// [Widget, Option, Title, Description, {Param: 'Value'}],
+var startupConf = [
+ [form.Flag, 'stdout', _('Log stdout')],
+ [form.Flag, 'stderr', _('Log stderr')],
+ [widgets.UserSelect, 'user', _('Run daemon as user')],
+ [widgets.GroupSelect, 'group', _('Run daemon as group')],
+ [form.Flag, 'respawn', _('Respawn when crashed')],
+ [form.DynamicList, 'env', _('Environment variable'), _('OS environments pass to frp for config file template, see <a href="https://github.com/fatedier/frp#configuration-file-template">frp README</a>'), {placeholder: 'ENV_NAME=value'}],
+ [form.DynamicList, 'conf_inc', _('Additional configs'), _('Config files include in temporary config file'), {placeholder: '/etc/frp/frpc.d/frpc_full.ini'}]
+];
+
+var commonConf = [
+ [form.Value, 'server_addr', _('Server address'), _('ServerAddr specifies the address of the server to connect to.<br>By default, this value is "0.0.0.0".'), {datatype: 'ipaddr'}],
+ [form.Value, 'server_port', _('Server port'), _('ServerPort specifies the port to connect to the server on.<br>By default, this value is 7000.'), {datatype: 'port'}],
+ [form.Value, 'http_proxy', _('HTTP proxy'), _('HttpProxy specifies a proxy address to connect to the server through. If this value is "", the server will be connected to directly.<br>By default, this value is read from the "http_proxy" environment variable.')],
+ [form.ListValue, 'log_level', _('Log level'), _('LogLevel specifies the minimum log level. Valid values are "trace", "debug", "info", "warn", and "error".<br>By default, this value is "info".'), {values: ['trace', 'debug', 'info', 'warn', 'error']}],
+ [form.Flag, 'disable_log_color', _('Disable log color'), _('DisableLogColor disables log colors when LogWay == "console" when set to true.'), {datatype: 'bool', default: 'true'}],
+ [form.Value, 'token', _('Token'), _('Token specifies the authorization token used to create keys to be sent to the server. The server must have a matching token for authorization to succeed. <br>By default, this value is "".')],
+ [form.Value, 'admin_addr', _('Admin address'), _('AdminAddr specifies the address that the admin server binds to.<br>By default, this value is "127.0.0.1".'), {datatype: 'ipaddr'}],
+ [form.Value, 'admin_port', _('Admin port'), _('AdminPort specifies the port for the admin server to listen on. If this value is 0, the admin server will not be started.<br>By default, this value is 0.'), {datatype: 'port'}],
+ [form.Value, 'admin_user', _('Admin user'), _('AdminUser specifies the username that the admin server will use for login.<br>By default, this value is "admin".')],
+ [form.Value, 'admin_pwd', _('Admin password'), _('AdminPwd specifies the password that the admin server will use for login.<br>By default, this value is "admin".'), {password: true}],
+ [form.Value, 'assets_dir', _('Assets dir'), _('AssetsDir specifies the local directory that the admin server will load resources from. If this value is "", assets will be loaded from the bundled executable using statik.<br>By default, this value is "".')],
+ [form.Flag, 'tcp_mux', _('TCP mux'), _('TcpMux toggles TCP stream multiplexing. This allows multiple requests from a client to share a single TCP connection. If this value is true, the server must have TCP multiplexing enabled as well.<br>By default, this value is true.'), {datatype: 'bool', default: 'true'}],
+ [form.Value, 'user', _('User'), _('User specifies a prefix for proxy names to distinguish them from other clients. If this value is not "", proxy names will automatically be changed to "{user}.{proxy_name}".<br>By default, this value is "".')],
+ [form.Flag, 'login_fail_exit', _('Exit when login fail'), _('LoginFailExit controls whether or not the client should exit after a failed login attempt. If false, the client will retry until a login attempt succeeds.<br>By default, this value is true.'), {datatype: 'bool', default: 'true'}],
+ [form.ListValue, 'protocol', _('Protocol'), _('Protocol specifies the protocol to use when interacting with the server. Valid values are "tcp", "kcp", and "websocket".<br>By default, this value is "tcp".'), {values: ['tcp', 'kcp', 'websocket']}],
+ [form.Flag, 'tls_enable', _('TLS'), _('TLSEnable specifies whether or not TLS should be used when communicating with the server.'), {datatype: 'bool'}],
+ [form.Value, 'heartbeat_interval', _('Heartbeat interval'), _('HeartBeatInterval specifies at what interval heartbeats are sent to the server, in seconds. It is not recommended to change this value.<br>By default, this value is 30.'), {datatype: 'uinteger'}],
+ [form.Value, 'heartbeat_timeout', _('Heartbeat timeout'), _('HeartBeatTimeout specifies the maximum allowed heartbeat response delay before the connection is terminated, in seconds. It is not recommended to change this value.<br>By default, this value is 90.'), {datatype: 'uinteger'}]
+];
+
+var baseProxyConf = [
+ [form.ListValue, 'type', _('Proxy type'), _('ProxyType specifies the type of this proxy. Valid values include "tcp", "udp", "http", "https", "stcp", and "xtcp".<br>By default, this value is "tcp".'), {values: ['tcp', 'udp', 'http', 'https', 'stcp', 'xtcp']}],
+ [form.Flag, 'use_encryption', _('Encryption'), _('UseEncryption controls whether or not communication with the server will be encrypted. Encryption is done using the tokens supplied in the server and client configuration.<br>By default, this value is false.'), {datatype: 'bool'}],
+ [form.Flag, 'use_compression', _('Compression'), _('UseCompression controls whether or not communication with the server will be compressed.<br>By default, this value is false.'), {datatype: 'bool'}],
+ [form.Value, 'local_ip', _('Local IP'), _('LocalIp specifies the IP address or host name to proxy to.'), {datatype: 'ipaddr'}],
+ [form.Value, 'local_port', _('Local port'), _('LocalPort specifies the port to proxy to.'), {datatype: 'port'}],
+];
+
+var bindInfoConf = [
+ [form.Value, 'remote_port', _('Remote port'), _('If remote_port is 0, frps will assign a random port for you'), {datatype: 'port'}]
+];
+
+var domainConf = [
+ [form.Value, 'custom_domains', _('Custom domains')],
+ [form.Value, 'sub_domain', _('Subdomain')],
+];
+
+var httpProxyConf = [
+ [form.Value, 'locations', _('Locations')],
+ [form.Value, 'http_user', _('HTTP user')],
+ [form.Value, 'http_pwd', _('HTTP password')],
+ [form.Value, 'host_header_rewrite', _('Host header rewrite')],
+ // [form.Value, 'headers', _('Headers')], // FIXME
+];
+
+var stcpProxyConf = [
+ [form.ListValue, 'role', _('Role'), undefined, {values: ['server', 'visitor']}],
+ [form.Value, 'sk', _('Sk')],
+];
+
+function setParams(o, params) {
+ if (!params) return;
+ for (var key in params) {
+ var val = params[key];
+ if (key === 'values') {
+ for (var j = 0; j < val.length; j++) {
+ var args = val[j];
+ if (!Array.isArray(args))
+ args = [args];
+ o.value.apply(o, args);
+ }
+ } else if (key === 'depends') {
+ if (!Array.isArray(val))
+ val = [val];
+ for (var j = 0; j < val.length; j++) {
+ var args = val[j];
+ if (!Array.isArray(args))
+ args = [args];
+ o.depends.apply(o, args);
+ }
+ } else {
+ o[key] = params[key];
+ }
+ }
+ if (params['datatype'] === 'bool') {
+ o.enabled = 'true';
+ o.disabled = 'false';
+ }
+}
+
+function defTabOpts(s, t, opts, params) {
+ for (var i = 0; i < opts.length; i++) {
+ var opt = opts[i];
+ var o = s.taboption(t, opt[0], opt[1], opt[2], opt[3]);
+ setParams(o, opt[4]);
+ setParams(o, params);
+ }
+}
+
+function defOpts(s, opts, params) {
+ for (var i = 0; i < opts.length; i++) {
+ var opt = opts[i];
+ var o = s.option(opt[0], opt[1], opt[2], opt[3]);
+ setParams(o, opt[4]);
+ setParams(o, params);
+ }
+}
+
+return L.view.extend({
+ render: function() {
+ var m, s, o;
+
+ m = new form.Map('frpc', _('frp Client'));
+
+ s = m.section(form.NamedSection, 'common', 'conf');
+ s.dynamic = true;
+
+ s.tab('common', _('Common Settings'));
+ s.tab('init', _('Startup Settings'));
+
+ defTabOpts(s, 'common', commonConf, {optional: true});
+
+ o = s.taboption('init', form.SectionValue, 'init', form.TypedSection, 'init', _('Startup Settings'));
+ s = o.subsection;
+ s.anonymous = true;
+ s.dynamic = true;
+
+ defOpts(s, startupConf);
+
+ s = m.section(form.GridSection, 'conf', _('Proxy Settings'));
+ s.addremove = true;
+ s.filter = function(s) { return s !== 'common'; };
+ s.renderSectionAdd = function(extra_class) {
+ var el = form.GridSection.prototype.renderSectionAdd.apply(this, arguments),
+ nameEl = el.querySelector('.cbi-section-create-name');
+ ui.addValidator(nameEl, 'uciname', true, function(v) {
+ if (v === 'common') return _('Name can not be "common"');
+ return true;
+ }, 'blur', 'keyup');
+ return el;
+ }
+
+ s.tab('general', _('General Settings'));
+ s.tab('http', _('HTTP Settings'));
+
+ s.option(form.Value, 'type', _('Proxy type')).modalonly = false;
+ s.option(form.Value, 'local_ip', _('Local IP')).modalonly = false;
+ s.option(form.Value, 'local_port', _('Local port')).modalonly = false;
+
+ defTabOpts(s, 'general', baseProxyConf, {modalonly: true});
+
+ // TCP and UDP
+ defTabOpts(s, 'general', bindInfoConf, {optional: true, modalonly: true, depends: [{type: 'tcp'}, {type: 'udp'}]});
+
+ // HTTP and HTTPS
+ defTabOpts(s, 'http', domainConf, {optional: true, modalonly: true, depends: [{type: 'http'}, {type: 'https'}]});
+
+ // HTTP
+ defTabOpts(s, 'http', httpProxyConf, {optional: true, modalonly: true, depends: {type: 'http'}});
+
+ // STCP and XTCP
+ defTabOpts(s, 'general', stcpProxyConf, {modalonly: true, depends: [{type: 'stcp'}, {type: 'xtcp'}]});
+
+ return m.render();
+ }
+});
diff --git a/applications/luci-app-frpc/po/templates/frpc.pot b/applications/luci-app-frpc/po/templates/frpc.pot
new file mode 100644
index 000000000..a9c956923
--- /dev/null
+++ b/applications/luci-app-frpc/po/templates/frpc.pot
@@ -0,0 +1,356 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:14
+msgid "Additional configs"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:24
+msgid "Admin address"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:27
+msgid "Admin password"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:25
+msgid "Admin port"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:26
+msgid "Admin user"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:24
+msgid ""
+"AdminAddr specifies the address that the admin server binds to.<br>By "
+"default, this value is \"127.0.0.1\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:25
+msgid ""
+"AdminPort specifies the port for the admin server to listen on. If this "
+"value is 0, the admin server will not be started.<br>By default, this value "
+"is 0."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:27
+msgid ""
+"AdminPwd specifies the password that the admin server will use for login."
+"<br>By default, this value is \"admin\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:26
+msgid ""
+"AdminUser specifies the username that the admin server will use for login."
+"<br>By default, this value is \"admin\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:28
+msgid "Assets dir"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:28
+msgid ""
+"AssetsDir specifies the local directory that the admin server will load "
+"resources from. If this value is \"\", assets will be loaded from the "
+"bundled executable using statik.<br>By default, this value is \"\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:125
+msgid "Common Settings"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:41
+msgid "Compression"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:14
+msgid "Config files include in temporary config file"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:51
+msgid "Custom domains"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:22
+msgid "Disable log color"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:22
+msgid ""
+"DisableLogColor disables log colors when LogWay == \"console\" when set to "
+"true."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:40
+msgid "Encryption"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:13
+msgid "Environment variable"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:31
+msgid "Exit when login fail"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:150
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:151
+msgid "HTTP Settings"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:58
+msgid "HTTP password"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:20
+msgid "HTTP proxy"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:57
+msgid "HTTP user"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:60
+msgid "Headers"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:34
+msgid ""
+"HeartBeatInterval specifies at what interval heartbeats are sent to the "
+"server, in seconds. It is not recommended to change this value.<br>By "
+"default, this value is 30."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:35
+msgid ""
+"HeartBeatTimeout specifies the maximum allowed heartbeat response delay "
+"before the connection is terminated, in seconds. It is not recommended to "
+"change this value.<br>By default, this value is 90."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:34
+msgid "Heartbeat interval"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:35
+msgid "Heartbeat timeout"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:59
+msgid "Host header rewrite"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:20
+msgid ""
+"HttpProxy specifies a proxy address to connect to the server through. If "
+"this value is \"\", the server will be connected to directly.<br>By default, "
+"this value is read from the \"http_proxy\" environment variable."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:47
+msgid "If remote_port is 0, frps will assign a random port for you"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:42
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:154
+msgid "Local IP"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:43
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:155
+msgid "Local port"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:42
+msgid "LocalIp specifies the IP address or host name to proxy to."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:43
+msgid "LocalPort specifies the port to proxy to."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:56
+msgid "Locations"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:21
+msgid "Log level"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:9
+msgid "Log stderr"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:8
+msgid "Log stdout"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:21
+msgid ""
+"LogLevel specifies the minimum log level. Valid values are \"trace\", \"debug"
+"\", \"info\", \"warn\", and \"error\".<br>By default, this value is \"info\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:31
+msgid ""
+"LoginFailExit controls whether or not the client should exit after a failed "
+"login attempt. If false, the client will retry until a login attempt "
+"succeeds.<br>By default, this value is true."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:144
+msgid "Name can not be \"common\""
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:13
+msgid ""
+"OS environments pass to frp for config file template, see <a href=\"https://"
+"github.com/fatedier/frp#configuration-file-template\">frp README</a>"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:32
+msgid "Protocol"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:32
+msgid ""
+"Protocol specifies the protocol to use when interacting with the server. "
+"Valid values are \"tcp\", \"kcp\", and \"websocket\".<br>By default, this "
+"value is \"tcp\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:137
+msgid "Proxy Settings"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:39
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:153
+msgid "Proxy type"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:39
+msgid ""
+"ProxyType specifies the type of this proxy. Valid values include \"tcp\", "
+"\"udp\", \"http\", \"https\", \"stcp\", and \"xtcp\".<br>By default, this "
+"value is \"tcp\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:47
+msgid "Remote port"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:12
+msgid "Respawn when crashed"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:64
+msgid "Role"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:11
+msgid "Run daemon as group"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:10
+msgid "Run daemon as user"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:18
+msgid "Server address"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:19
+msgid "Server port"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:18
+msgid ""
+"ServerAddr specifies the address of the server to connect to.<br>By default, "
+"this value is \"0.0.0.0\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:19
+msgid ""
+"ServerPort specifies the port to connect to the server on.<br>By default, "
+"this value is 7000."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:65
+msgid "Sk"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:126
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:130
+msgid "Startup Settings"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:52
+msgid "Subdomain"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:29
+msgid "TCP mux"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:33
+msgid "TLS"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:33
+msgid ""
+"TLSEnable specifies whether or not TLS should be used when communicating "
+"with the server."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:29
+msgid ""
+"TcpMux toggles TCP stream multiplexing. This allows multiple requests from a "
+"client to share a single TCP connection. If this value is true, the server "
+"must have TCP multiplexing enabled as well.<br>By default, this value is "
+"true."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:23
+msgid "Token"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:23
+msgid ""
+"Token specifies the authorization token used to create keys to be sent to "
+"the server. The server must have a matching token for authorization to "
+"succeed. <br>By default, this value is \"\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:41
+msgid ""
+"UseCompression controls whether or not communication with the server will be "
+"compressed.<br>By default, this value is false."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:40
+msgid ""
+"UseEncryption controls whether or not communication with the server will be "
+"encrypted. Encryption is done using the tokens supplied in the server and "
+"client configuration.<br>By default, this value is false."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:30
+msgid "User"
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:30
+msgid ""
+"User specifies a prefix for proxy names to distinguish them from other "
+"clients. If this value is not \"\", proxy names will automatically be "
+"changed to \"{user}.{proxy_name}\".<br>By default, this value is \"\"."
+msgstr ""
+
+#: applications/luci-app-frpc/htdocs/luci-static/resources/view/frpc.js:120
+#: applications/luci-app-frpc/root/usr/share/luci/menu.d/luci-app-frpc.json:3
+msgid "frp Client"
+msgstr ""
diff --git a/applications/luci-app-frpc/root/usr/share/luci/menu.d/luci-app-frpc.json b/applications/luci-app-frpc/root/usr/share/luci/menu.d/luci-app-frpc.json
new file mode 100644
index 000000000..26a0f2b1c
--- /dev/null
+++ b/applications/luci-app-frpc/root/usr/share/luci/menu.d/luci-app-frpc.json
@@ -0,0 +1,9 @@
+{
+ "admin/services/frpc": {
+ "title": "frp Client",
+ "action": {
+ "type": "view",
+ "path": "frpc"
+ }
+ }
+}
diff --git a/applications/luci-app-frpc/root/usr/share/rpcd/acl.d/luci-app-frpc.json b/applications/luci-app-frpc/root/usr/share/rpcd/acl.d/luci-app-frpc.json
new file mode 100644
index 000000000..970b95780
--- /dev/null
+++ b/applications/luci-app-frpc/root/usr/share/rpcd/acl.d/luci-app-frpc.json
@@ -0,0 +1,8 @@
+{
+ "luci-app-frpc": {
+ "description": "Grant access to LuCI app frpc",
+ "write": {
+ "uci": ["frpc"]
+ }
+ }
+}