instances.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. 'use strict';
  2. 'require form';
  3. 'require uci';
  4. 'require fs';
  5. 'require network';
  6. 'require rpc';
  7. 'require shadowsocks-libev as ss';
  8. var conf = 'shadowsocks-libev';
  9. var cfgtypes = ['ss_local', 'ss_redir', 'ss_server', 'ss_tunnel'];
  10. var callServiceList = rpc.declare({
  11. object: 'service',
  12. method: 'list',
  13. params: [ 'name' ],
  14. expect: { '': {} }
  15. });
  16. return L.view.extend({
  17. render: function(stats) {
  18. var m, s, o;
  19. m = new form.Map(conf,
  20. _('Local Instances'),
  21. _('Instances of shadowsocks-libev components, e.g. ss-local, \
  22. ss-redir, ss-tunnel, ss-server, etc. To enable an instance it \
  23. is required to enable both the instance itself and the remote \
  24. server it refers to.'));
  25. s = m.section(form.GridSection);
  26. s.addremove = true;
  27. s.cfgsections = function() {
  28. return this.map.data.sections(this.map.config)
  29. .filter(function(s) { return cfgtypes.indexOf(s['.type']) !== -1; })
  30. .map(function(s) { return s['.name']; });
  31. };
  32. s.sectiontitle = function(section_id) {
  33. var s = uci.get(conf, section_id);
  34. return (s ? s['.type'] + '.' : '') + section_id;
  35. };
  36. s.renderSectionAdd = function(extra_class) {
  37. var el = form.GridSection.prototype.renderSectionAdd.apply(this, arguments),
  38. optionEl = [E('option', { value: '_dummy' }, [_('-- instance type --')])];
  39. cfgtypes.forEach(function(t) {
  40. optionEl.push(E('option', { value: t }, [t.replace('_', '-')]));
  41. });
  42. var selectEl = E('select', {
  43. class: 'cbi-input-select',
  44. change: function(ev) {
  45. ev.target.parentElement.nextElementSibling.nextElementSibling
  46. .toggleAttribute('disabled', ev.target.value === '_dummy');
  47. }
  48. }, optionEl);
  49. el.lastElementChild.setAttribute('disabled', '');
  50. el.prepend(E('div', {}, selectEl));
  51. return el;
  52. };
  53. s.handleAdd = function(ev, name) {
  54. var selectEl = ev.target.parentElement.firstElementChild.firstElementChild,
  55. type = selectEl.value;
  56. this.sectiontype = type;
  57. var promise = form.GridSection.prototype.handleAdd.apply(this, arguments);
  58. this.sectiontype = undefined;
  59. return promise;
  60. };
  61. s.addModalOptions = function(s, section_id, ev) {
  62. var sdata = uci.get(conf, section_id),
  63. stype = sdata ? sdata['.type'] : null;
  64. if (stype) {
  65. s.sectiontype = stype;
  66. return Promise.all([
  67. L.resolveDefault(fs.stat('/usr/bin/' + stype.replace('_', '-')), null),
  68. network.getDevices()
  69. ]).then(L.bind(function(res) {
  70. s.tab('general', _('General Settings'));
  71. s.tab('advanced', _('Advanced Settings'));
  72. s.taboption('general', form.Flag, 'disabled', _('Disable'));
  73. if (!res[0]) {
  74. ss.option_install_package(s, 'general');
  75. }
  76. ss.options_common(s, 'advanced');
  77. if (stype === 'ss_server') {
  78. ss.options_server(s, { tab: 'general' });
  79. o = s.taboption('general', form.Value, 'bind_address',
  80. _('Bind address'),
  81. _('The address ss-server will initiate connection from'));
  82. o.datatype = 'ipaddr';
  83. o.placeholder = '0.0.0.0';
  84. ss.values_ipaddr(o, res[1]);
  85. } else {
  86. ss.options_client(s, 'general', res[1]);
  87. if (stype === 'ss_tunnel') {
  88. o = s.taboption('general', form.Value, 'tunnel_address',
  89. _('Tunnel address'),
  90. _('The address ss-tunnel will forward traffic to'));
  91. o.datatype = 'hostport';
  92. }
  93. }
  94. }, this));
  95. }
  96. };
  97. o = s.option(form.DummyValue, 'overview', _('Overview'));
  98. o.modalonly = false;
  99. o.editable = true;
  100. o.rawhtml = true;
  101. o.renderWidget = function(section_id, option_index, cfgvalue) {
  102. var sdata = uci.get(conf, section_id);
  103. if (sdata) {
  104. return form.DummyValue.prototype.renderWidget.call(this, section_id, option_index, ss.cfgvalue_overview(sdata));
  105. }
  106. return null;
  107. };
  108. o = s.option(form.DummyValue, 'running', _('Running'));
  109. o.modalonly = false;
  110. o.editable = true;
  111. o.default = '';
  112. o = s.option(form.Button, 'disabled', _('Enable/Disable'));
  113. o.modalonly = false;
  114. o.editable = true;
  115. o.inputtitle = function(section_id) {
  116. var s = uci.get(conf, section_id);
  117. if (ss.ucival_to_bool(s['disabled'])) {
  118. this.inputstyle = 'reset';
  119. return _('Disabled');
  120. }
  121. this.inputstyle = 'save';
  122. return _('Enabled');
  123. }
  124. o.onclick = function(ev) {
  125. var inputEl = ev.target.parentElement.nextElementSibling;
  126. inputEl.value = ss.ucival_to_bool(inputEl.value) ? '0' : '1';
  127. return this.map.save();
  128. }
  129. return m.render().finally(function() {
  130. L.Poll.add(function() {
  131. return L.resolveDefault(callServiceList(conf), {})
  132. .then(function(res) {
  133. var instances = null;
  134. try {
  135. instances = res[conf]['instances'];
  136. } catch (e) {}
  137. if (!instances) return;
  138. uci.sections(conf)
  139. .filter(function(s) { return cfgtypes.indexOf(s['.type']) !== -1; })
  140. .forEach(function(s) {
  141. var el = document.getElementById('cbi-shadowsocks-libev-' + s['.name'] + '-running');
  142. if (el) {
  143. var name = s['.type'] + '.' + s['.name'],
  144. running = instances.hasOwnProperty(name)? instances[name].running : false;
  145. el.innerText = running ? 'yes' : 'no';
  146. }
  147. });
  148. });
  149. });
  150. });
  151. },
  152. });