1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
'use strict';
'require view';
'require ui';
'require form';
'require rpc';
'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: 'host'}],
[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'}],
[form.DynamicList, '_', _('Additional settings'), _('This list can be used to specify some additional parameters which have not been included in this LuCI.'), {placeholder: 'Key-A=Value-A'}]
];
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, 'subdomain', _('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);
}
}
var callServiceList = rpc.declare({
object: 'service',
method: 'list',
params: ['name'],
expect: { '': {} }
});
function getServiceStatus() {
return L.resolveDefault(callServiceList('frpc'), {}).then(function (res) {
var isRunning = false;
try {
isRunning = res['frpc']['instances']['instance1']['running'];
} catch (e) { }
return isRunning;
});
}
function renderStatus(isRunning) {
var renderHTML = "";
var spanTemp = "<span style=\"color:%s;font-weight:bold;margin-left:15px\">%s - %s</span>";
if (isRunning) {
renderHTML += String.format(spanTemp, 'green', _("frp Client"), _("RUNNING"));
} else {
renderHTML += String.format(spanTemp, 'red', _("frp Client"), _("NOT RUNNING"));
}
return renderHTML;
}
return view.extend({
render: function() {
var m, s, o;
m = new form.Map('frpc', _('frp Client'));
s = m.section(form.NamedSection, '_status');
s.anonymous = true;
s.render = function (section_id) {
L.Poll.add(function () {
return L.resolveDefault(getServiceStatus()).then(function(res) {
var view = document.getElementById("service_status");
view.innerHTML = renderStatus(res);
});
});
return E('div', { class: 'cbi-map' },
E('div', { class: 'cbi-section'}, [
E('div', { id: 'service_status' },
_('Collecting data ...'))
])
);
}
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();
}
});
|