PortListController.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. //
  2. // Created by xcbosa on 2023/1/30.
  3. //
  4. #include "../processor/processor.h"
  5. #include "../utils/utils.h"
  6. #include "../webuiconf.h"
  7. #include "../processor/templates/framework7/Framework7Document.hpp"
  8. #include "../user.hpp"
  9. #include "../frp.h"
  10. #define RequireLogin(username) \
  11. string username = user::getTokenUserName(request.getCookie("Token")); if (username.empty()) { return new RedirectResponse("/"); }
  12. using namespace std;
  13. using namespace xc::processor;
  14. using namespace xc::processor::templates;
  15. using namespace xc::processor::templates::framework7;
  16. using namespace xc::utils;
  17. using namespace configor;
  18. namespace xc::controller {
  19. double divide(double a, double b, double zeroIf) {
  20. if (b == 0) return zeroIf;
  21. return a / b;
  22. }
  23. ResponseData *PortListController(RequestData request) {
  24. RequireLogin(username)
  25. auto profiles = frp::listUserAvailableProfiles(username);
  26. int totalPortCnt(0), usedPortCnt(0);
  27. for (auto profile : profiles) {
  28. totalPortCnt += profile.getAllowPortCount();
  29. usedPortCnt += profile.ports.size();
  30. }
  31. double guagePercent = 1 - usedPortCnt / (double)totalPortCnt;
  32. if (totalPortCnt == 0) {
  33. guagePercent = 0;
  34. }
  35. auto resp = new TemplateResponseData({
  36. Framework7Document({
  37. a("退出登陆").classAdd("link").onclick("window.location='/quitLogin'")
  38. }, {
  39. If(!request.getCookie("message").empty(), {
  40. OnLoadScript("app.dialog.alert('" + request.getCookie("message") + "')")
  41. }),
  42. BlockTitleView("总览"),
  43. BlockView({
  44. h1("端口总览").style("text-align", "center"),
  45. GuageView(to_string(usedPortCnt) + " / " + to_string(totalPortCnt) + "端口", "已开通", guagePercent, 300)
  46. }),
  47. Foreach(profiles, [] (frp::ProfileInfo profile) {
  48. return View("", {
  49. BlockTitleView("IP地址 " + profile.getServerAddr() + " (端口" + to_string(profile.getAllowPortLow()) + "-" + to_string(profile.getAllowPortLow() + profile.getAllowPortCount() - 1) + ")"),
  50. Foreach(profile.ports, [&profile] (frp::ProfilePortInfo portInfo) {
  51. return View("", {
  52. templates::div({
  53. templates::div({
  54. templates::div({
  55. templates::div({
  56. profile.getServerAddr() + ":" + to_string(portInfo.remotePort),
  57. VerticalSpacer(10),
  58. small({
  59. "转发给: ",
  60. portInfo.localIp,
  61. ":",
  62. to_string(portInfo.localPort)
  63. }).style("opacity", "0.7")
  64. }).classAdd("card-header text-color-white display-block"),
  65. Link("关闭").href("#").classAdd("link card-close card-opened-fade-in color-white").prop("style", "position: absolute; right: 15px; top: 15px")
  66. }).style("height", "300px").style("background-color", "#123456"),
  67. templates::div({
  68. FormView({
  69. input().name("uuid").value(portInfo.uuid).prop("hidden", "true"),
  70. FormInputView("address", "内部IP地址", "text", "例如:10.0.0.2", portInfo.localIp),
  71. FormInputView("port", "内部端口", "number", "例如:22 (SSH服务端口)", to_string(portInfo.localPort)),
  72. FormCustomInputView("外部端口 (在" + profile.getServerAddr() + "上)", {
  73. templates::select({
  74. Foreach(profile.getFreeRemotePortsAndAppend(portInfo.remotePort), [&portInfo] (int port) {
  75. return If(portInfo.remotePort == port, {
  76. View("option", to_string(port)).value(to_string(port)).prop("selected", "selected")
  77. }, {
  78. View("option", to_string(port)).value(to_string(port))
  79. });
  80. })
  81. }).name("remotePort")
  82. })
  83. }).id("modifyForm_" + portInfo.uuid),
  84. VerticalSpacer(25),
  85. button("保存端口设置").classAdd("button button-fill").onclick("doModify('" + portInfo.uuid + "')"),
  86. VerticalSpacer(10),
  87. button("删除此条").classAdd("button button-fill color-red").onclick("doDelete('" + portInfo.uuid +"')")
  88. }).classAdd("card-content-padding")
  89. }).classAdd("card-content")
  90. }).classAdd("card card-expandable"),
  91. });
  92. }),
  93. });
  94. }),
  95. BlockView({
  96. ButtonView("创建新的端口").classAdd("button-large button-fill").classAdd("sheet-open").prop("data-sheet", ".create-new")
  97. }),
  98. templates::div({
  99. templates::div({
  100. templates::div({
  101. templates::div({
  102. a("取消").classAdd("link sheet-close")
  103. }).classAdd("left"),
  104. templates::div({
  105. p("创建端口")
  106. }).classAdd("center"),
  107. templates::div({
  108. a("创建").classAdd("link").onclick("doCreate()")
  109. }).classAdd("right")
  110. }).classAdd("toolbar-inner")
  111. }).classAdd("toolbar"),
  112. templates::div({
  113. templates::div({
  114. BlockTitleView("端口信息"),
  115. FormView({
  116. FormInputView("address", "内部IP地址", "text", "例如:10.0.0.2"),
  117. FormInputView("port", "内部端口", "number", "例如:22 (SSH服务端口)"),
  118. FormCustomInputView("转发到", {
  119. templates::select({
  120. Foreach(frp::listingAvailableServerAndPortForUser(username), [] (string pair) {
  121. return View("option", pair);
  122. })
  123. }).name("remotePair")
  124. })
  125. }).id("createNewForm")
  126. }).classAdd("page-content")
  127. }).classAdd("sheet-modal-inner")
  128. }).classAdd("sheet-modal sheet-modal-push create-new")
  129. }, {
  130. a("2023 © Frp-WebUI by XCBOSA").classAdd("link").onclick("window.open('https://github.com/XCBOSA/frp-webui-500k.git')")
  131. })
  132. });
  133. return resp;
  134. }
  135. ResponseData *CreatePortController(RequestData request) {
  136. RequireLogin(username)
  137. string arg = request.getURLArgument("v");
  138. JsonModel model = json::parse(arg);
  139. string localAddress = model["address"];
  140. int localPort = to_int(model["port"], -1);
  141. string remotePair = model["remotePair"];
  142. auto remoteList = split(remotePair, ":");
  143. if (remoteList.size() != 2) {
  144. return new RedirectResponse("/");
  145. }
  146. string serverIp = remoteList[0];
  147. int serverPort = to_int(remoteList[1], -1);
  148. auto profiles = frp::listUserAvailableProfiles(username);
  149. for (auto &profile : profiles) {
  150. if (profile.getServerAddr() != serverIp) { continue; }
  151. if (!utils::is_in(serverPort, profile.getAllowPortLow(), profile.getAllowPortLow() + profile.getAllowPortCount())) { continue; }
  152. auto freePorts = profile.getFreeRemotePorts();
  153. if (std::count(freePorts.begin(), freePorts.end(), serverPort)) {
  154. profile.addPortInfo(frp::ProfilePortInfo(localAddress, localPort, serverPort));
  155. profile.save();
  156. auto resp = new RedirectResponse("/");
  157. resp->addCookie("message", "添加成功。");
  158. return resp;
  159. } else {
  160. auto resp = new RedirectResponse("/");
  161. resp->addCookie("message", "该端口已被占用,可能在您编辑期间,他人占用了这个端口,请重新选择。");
  162. return resp;
  163. }
  164. }
  165. auto resp = new RedirectResponse("/");
  166. resp->addCookie("message", "添加失败,您可能没有权限使用指定服务器和端口号段。");
  167. return resp;
  168. }
  169. ResponseData *ChangePortSettings(RequestData request) {
  170. RequireLogin(username)
  171. string arg = request.getURLArgument("v");
  172. JsonModel model = json::parse(arg);
  173. string uuid = model["uuid"];
  174. int remotePort = to_int(model["remotePort"], -1);
  175. string localAddress = model["address"];
  176. int localPort = to_int(model["port"], -1);
  177. auto profiles = frp::listUserAvailableProfiles(username);
  178. for (auto &profile : profiles) {
  179. for (auto &port : profile.ports) {
  180. if (port.uuid == uuid) {
  181. auto allowPortsIncludedMe = profile.getFreeRemotePortsAndAppend(port.remotePort);
  182. if (std::count(allowPortsIncludedMe.begin(), allowPortsIncludedMe.end(), remotePort)) {
  183. port.remotePort = remotePort;
  184. port.localPort = localPort;
  185. port.localIp = localAddress;
  186. profile.save();
  187. auto resp = new RedirectResponse("/");
  188. resp->addCookie("message", "修改成功");
  189. return resp;
  190. } else {
  191. auto resp = new RedirectResponse("/");
  192. resp->addCookie("message", "您指定的远程端口" + to_string(remotePort) + "并不在其所在的配置文件允许的端口范围中,或者正在使用。");
  193. return resp;
  194. }
  195. }
  196. }
  197. }
  198. auto resp = new RedirectResponse("/");
  199. resp->addCookie("message", "找不到您刚刚修改的端口配置,它可能在您编辑的时候被其他人删除了。");
  200. return resp;
  201. }
  202. ResponseData *RemovePort(RequestData request) {
  203. RequireLogin(username)
  204. string arg = request.getURLArgument("v");
  205. JsonModel model = json::parse(arg);
  206. string uuid = model["uuid"];
  207. auto profiles = frp::listUserAvailableProfiles(username);
  208. for (auto &profile : profiles) {
  209. for (auto port : profile.ports) {
  210. if (port.uuid == uuid) {
  211. profile.removePort(port.remotePort);
  212. profile.save();
  213. auto resp = new RedirectResponse("/");
  214. resp->addCookie("message", "删除成功。");
  215. return resp;
  216. }
  217. }
  218. }
  219. auto resp = new RedirectResponse("/");
  220. resp->addCookie("message", "找不到您要删除的端口,它可能已经被删除。");
  221. return resp;
  222. }
  223. ContentGeneratorDefineWithNameS("PortListController", false, PortListController(request))
  224. ContentGeneratorDefineWithNameS("/createPort", request.getURLPath() == "/createPort", CreatePortController(request))
  225. ContentGeneratorDefineWithNameS("/changePortSettings", request.getURLPath() == "/changePortSettings", ChangePortSettings(request))
  226. ContentGeneratorDefineWithNameS("/removePort", request.getURLPath() == "/removePort", RemovePort(request))
  227. }