diff --git a/pom.xml b/pom.xml index 08809d3..cd929eb 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,7 @@ org.projectlombok lombok 1.18.14 + compile @@ -70,12 +71,19 @@ compile - + org.apache.commons commons-lang3 3.12.0 + + + commons-io + commons-io + 2.11.0 + + cn.hutool @@ -157,25 +165,60 @@ - org.openjfx + com.zenjava javafx-maven-plugin - 0.0.8 + 8.8.3 + + analysis + top.octopusyan.YanFrpLuncher + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.2.0 - - default-cli - - top.octopusyan/top.octopusyan.YanFrpLauncher - app - app - app - true - true - true - + make-assembly + + package + + + single + + + + + top.octopusyan.YanFrpLuncher + + + + jar-with-dependencies + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/top/octopusyan/YanFrpApplication.java b/src/main/java/top/octopusyan/YanFrpApplication.java index 9f0e6ca..0912428 100644 --- a/src/main/java/top/octopusyan/YanFrpApplication.java +++ b/src/main/java/top/octopusyan/YanFrpApplication.java @@ -1,21 +1,25 @@ package top.octopusyan; -import com.sun.org.slf4j.internal.Logger; -import com.sun.org.slf4j.internal.LoggerFactory; import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXMLLoader; +import javafx.fxml.JavaFXBuilderFactory; import javafx.scene.Scene; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.stage.StageStyle; -import top.octopusyan.common.http.HttpConfig; -import top.octopusyan.common.http.config.LogStrategy; -import top.octopusyan.common.http.request.RequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import top.octopusyan.base.BaseController; +import top.octopusyan.manager.http.HttpConfig; +import top.octopusyan.manager.http.config.LogStrategy; +import top.octopusyan.manager.http.request.RequestHandler; import top.octopusyan.http.OkHttpClientConfig; import top.octopusyan.utils.AlertUtil; import top.octopusyan.utils.ApplicatonStore; +import top.octopusyan.utils.FrpUtil; +import top.octopusyan.utils.FxmlUtil; /** * @author : octopus yan @@ -24,12 +28,15 @@ import top.octopusyan.utils.ApplicatonStore; * @create : 2022-3-29 15:00 */ public class YanFrpApplication extends Application { - private static final Logger LOGGER = LoggerFactory.getLogger(YanFrpApplication.class); + private static final Logger logger = LoggerFactory.getLogger(YanFrpApplication.class); @Override public void init() throws Exception { super.init(); - LOGGER.debug("init..."); + logger.info("init..."); + + // 初始化frp客户端临时文件 + FrpUtil.initFrpc(); // 网络请求设置 HttpConfig.with(OkHttpClientConfig.httpClient()) @@ -49,7 +56,7 @@ public class YanFrpApplication extends Application { @Override public void start(Stage stage) { - LOGGER.debug("start..."); + logger.info("start..."); // TODO 全局异常处理... (emm有点草率,先这样了。。) Thread.setDefaultUncaughtExceptionHandler((t, e) -> Platform.runLater(() -> showErrorDialog(t, e))); @@ -61,8 +68,12 @@ public class YanFrpApplication extends Application { ApplicatonStore.setRegisterSuccess(false); try { + // 静态写法无法获取controler +// StackPane root = new FXMLLoader(this.getClass().getResource("/fxml/login.fxml")).load();//底层面板 + FXMLLoader fxmlLoader = FxmlUtil.init("/fxml/login.fxml"); + + StackPane root = fxmlLoader.load();//底层面板 stage.initStyle(StageStyle.TRANSPARENT); - StackPane root = new FXMLLoader(this.getClass().getResource("/fxml/login.fxml")).load();//底层面板 Scene scene = new Scene( root, root.getPrefWidth() + 20, @@ -73,22 +84,26 @@ public class YanFrpApplication extends Application { stage.setScene(scene); stage.show(); + BaseController controller = fxmlLoader.getController(); + controller.setApplication(this); + } catch (Throwable t) { showErrorDialog(Thread.currentThread(), t); } - LOGGER.debug("start end..."); + logger.debug("start end..."); } private void showErrorDialog(Thread t, Throwable e) { e.printStackTrace(); - AlertUtil.exceptionAlert(new Exception(e)).show(); + Platform.runLater(() -> AlertUtil.exceptionAlert(new Exception(e)).show()); } @Override public void stop() throws Exception { super.stop(); - LOGGER.debug("stop..."); + FrpUtil.clearTmp(); + logger.info("stop..."); } public static void main(String[] args) { diff --git a/src/main/java/top/octopusyan/YanFrpLuncher.java b/src/main/java/top/octopusyan/YanFrpLuncher.java new file mode 100644 index 0000000..76db4fe --- /dev/null +++ b/src/main/java/top/octopusyan/YanFrpLuncher.java @@ -0,0 +1,33 @@ +package top.octopusyan; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import top.octopusyan.base.BaseController; +import top.octopusyan.http.OkHttpClientConfig; +import top.octopusyan.manager.http.HttpConfig; +import top.octopusyan.manager.http.config.LogStrategy; +import top.octopusyan.manager.http.request.RequestHandler; +import top.octopusyan.utils.AlertUtil; +import top.octopusyan.utils.ApplicatonStore; +import top.octopusyan.utils.FrpUtil; +import top.octopusyan.utils.FxmlUtil; + +/** + * @author : octopus yan + * @email : octopus_yan@foxmail.com + * @description : YanFrp Application + * @create : 2022-3-29 15:00 + */ +public class YanFrpLuncher { + public static void main(String[] args) { + Application.launch(YanFrpApplication.class, args); + } +} diff --git a/src/main/java/top/octopusyan/base/BaseController.java b/src/main/java/top/octopusyan/base/BaseController.java index cef2045..174ac65 100644 --- a/src/main/java/top/octopusyan/base/BaseController.java +++ b/src/main/java/top/octopusyan/base/BaseController.java @@ -1,6 +1,7 @@ package top.octopusyan.base; import com.jfoenix.controls.JFXButton; +import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; @@ -11,6 +12,7 @@ import javafx.scene.layout.Pane; import javafx.stage.Stage; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; +import top.octopusyan.utils.FxmlUtil; import top.octopusyan.utils.Loading; import java.io.IOException; @@ -25,21 +27,35 @@ import java.util.ResourceBundle; */ public abstract class BaseController

implements Initializable { + private Application application; + private double xOffSet = 0, yOffSet = 0; private volatile Loading loading; - public void jumpTo(BaseController

controller){ + public void jumpTo(BaseController

controller) throws IOException { + FXMLLoader fxmlLoader = FxmlUtil.init(controller.getRootFxmlPath()); + Scene scene = getRootPanel().getScene(); - try { - Pane root = FXMLLoader.load(getClass().getResource(controller.getRootFxmlPath())); - scene.setRoot(root); - Stage stage = (Stage) scene.getWindow(); - stage.setWidth(root.getPrefWidth() + 20); - stage.setHeight(root.getPrefHeight() + 20); - } catch (IOException e) { - e.printStackTrace(); - } + double oldHeight = getRootPanel().getPrefHeight(); + double oldWidth = getRootPanel().getPrefWidth(); + + Pane root = fxmlLoader.load(); + Stage stage = (Stage) scene.getWindow(); + // 窗口大小 + double newWidth = root.getPrefWidth() + 20; + double newHeight = root.getPrefHeight() + 20; + // 窗口位置 + double newX = stage.getX() - (newWidth - oldWidth) / 2; + double newY = stage.getY() - (newHeight - oldHeight) / 2; + scene.setRoot(root); + stage.setX(newX < 0 ? 0 : newX); + stage.setY(newY < 0 ? 0 : newY); + stage.setWidth(newWidth); + stage.setHeight(newHeight); + + controller = fxmlLoader.getController(); + controller.setApplication(getApplication()); } @Override @@ -86,21 +102,29 @@ public abstract class BaseController

implements Initializable { initViewAction(); } - public void showLoading(){ + public void showLoading() { showLoading(null); } - public void showLoading(String message){ - if(loading == null) loading = new Loading((Stage) getRootPanel().getScene().getWindow()); - if(!StringUtils.isEmpty(message))loading.showMessage(message); + public void showLoading(String message) { + if (loading == null) loading = new Loading((Stage) getRootPanel().getScene().getWindow()); + if (!StringUtils.isEmpty(message)) loading.showMessage(message); loading.show(); } - public boolean isLoadShowing(){ + public void setApplication(Application application) { + this.application = application; + } + + public Application getApplication() { + return application; + } + + public boolean isLoadShowing() { return loading != null && loading.showing(); } - public void stopLoading(){ + public void stopLoading() { loading.closeStage(); } @@ -159,11 +183,16 @@ public abstract class BaseController

implements Initializable { /** * 关闭窗口 */ - public void onDestroy(){ + public void onDestroy() { Stage stage = (Stage) getRootPanel().getScene().getWindow(); stage.hide(); stage.close(); - Platform.exit(); - System.exit(0); + try { + Thread.sleep(2000); + Platform.exit(); + System.exit(0); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } diff --git a/src/main/java/top/octopusyan/common/test.java b/src/main/java/top/octopusyan/common/test.java deleted file mode 100644 index 98369f9..0000000 --- a/src/main/java/top/octopusyan/common/test.java +++ /dev/null @@ -1,10 +0,0 @@ -package top.octopusyan.common; - -/** - * @author : octopus yan - * @email : octopus_yan@foxmail.com - * @description : test - * @create : 2022-4-1 23:36 - */ -public class test { -} diff --git a/src/main/java/top/octopusyan/config/ProxyConfig.java b/src/main/java/top/octopusyan/config/ProxyConfig.java index a078d12..c7b34f9 100644 --- a/src/main/java/top/octopusyan/config/ProxyConfig.java +++ b/src/main/java/top/octopusyan/config/ProxyConfig.java @@ -1,5 +1,9 @@ package top.octopusyan.config; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -10,14 +14,19 @@ import java.util.Map; * @create : 2022-4-5 18:14 */ public class ProxyConfig { - private static Map serverPath = new HashMap<>(); - private static Map typePort = new HashMap<>(); + private static final Map serverPath = new HashMap<>(); + private static final Map serverIp = new HashMap<>(); + private static final Map typePort = new HashMap<>(); static { serverPath.put("香港", "xg.frp.octopusyan.top"); serverPath.put("北京", "bj.frp.octopusyan.top"); serverPath.put("上海", "frp.octopusyan.top"); + serverIp.put("香港", "101.32.202.135"); + serverIp.put("北京", "112.125.120.135"); + serverIp.put("上海", "81.68.214.67"); + typePort.put("http", 80); typePort.put("https", 80); typePort.put("ssh", 22); @@ -25,8 +34,25 @@ public class ProxyConfig { } + @Getter public enum ProxyServer { - 香港, 北京, 上海 + xg("香港", 1), + bj("北京", 2), + sh("上海", 3); + private final String serverName; + private final int value; + + ProxyServer(String serverName, int value) { + this.serverName = serverName; + this.value = value; + } + + public static ProxyServer valueOf(int node) { + for (ProxyServer value : values()) { + if (value.value == node) return value; + } + return null; + } } public enum ProxyType { @@ -36,7 +62,7 @@ public class ProxyConfig { TCP("tcp"), ; - private String type; + private final String type; public String getType() { return type; @@ -51,7 +77,49 @@ public class ProxyConfig { return serverPath.get(serverName); } + public static int getServerNode(String serverName) { + for (ProxyServer server : Arrays.asList(ProxyServer.values())) { + if(server.serverName.equals(serverName)) return server.value; + } + + return 3; + } + + /** + * 获取服务名称 + * @param node 服务器标签 + */ + public static String getServerName(int node) { + ProxyServer proxyServer = ProxyServer.valueOf(node); + assert proxyServer != null; + return proxyServer.getServerName(); + } + + /** + * 获取服务器IP地址 + * @param serverName 服务器名称 + */ + public static String getServerIP(String serverName) { + return serverIp.get(serverName); + } + + /** + * 获取服务器IP地址 + * @param node 服务器标签 + */ + public static String getServerIP(int node) { + return serverIp.get(getServerName(node)); + } + + /** + * 获取代理类型默认端口 + * @param type 类型名称 + */ public static Integer getTypePort(String type) { return typePort.get(type); } + + public static Integer getTypeIndex(String type) { + return Arrays.asList(ProxyType.values()).indexOf(ProxyType.valueOf(StringUtils.upperCase(type))); + } } diff --git a/src/main/java/top/octopusyan/config/TextValidate.java b/src/main/java/top/octopusyan/config/TextValidate.java new file mode 100644 index 0000000..d5655f6 --- /dev/null +++ b/src/main/java/top/octopusyan/config/TextValidate.java @@ -0,0 +1,155 @@ +package top.octopusyan.config; + +import com.jfoenix.validation.RegexValidator; +import com.jfoenix.validation.RequiredFieldValidator; +import com.jfoenix.validation.StringLengthValidator; +import com.jfoenix.validation.base.ValidatorBase; +import top.octopusyan.config.ProxyConfig; +import top.octopusyan.model.ProxySetupModel; +import top.octopusyan.utils.DomainUtil; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * @author : octopus yan + * @email : octopus_yan@foxmail.com + * @description : JFX文本校验 + * @create : 2022-4-2 17:03 + */ +public class TextValidate { +// /** +// * 账号格式错误 +// */ +// public static ValidatorBase AccountFail = new ValidatorBase() { +// @Override +// protected void eval() { +// setMessage("账号格式错误"); +// hasErrors.set(true); +// } +// }; + + /** + * 账号不能为空 + */ + public static RequiredFieldValidator AccoountRequired = new RequiredFieldValidator("账号不能为空!"); + /** + * 账号格式 + */ + public static RegexValidator AccoountValidator = new RegexValidator("账号格式有误!"); + /** + * 密码不能为空 + */ + public static RequiredFieldValidator PasswordRequired = new RequiredFieldValidator("密码不能为空!"); + /** + * 邮箱地址格式验证 + */ + public static RegexValidator EmailFormat = new RegexValidator("邮箱地址格式错误"); + /** + * 域名为空检查 + */ + public static RequiredFieldValidator DomainRequired = new RequiredFieldValidator("域名不能为空!"); + /** + * 端口为空检查 + */ + public static RequiredFieldValidator PortRequired = new RequiredFieldValidator("端口不能为空"); + /** + * 端口格式检查 + */ + public static final RegexValidator PortFormat = new RegexValidator("端口格式错误"); + /** + * IP为空检查 + */ + public static RequiredFieldValidator IpRequired = new RequiredFieldValidator("本地IP不能为空"); + /** + * IP格式检查 + */ + public static final RegexValidator IpFormat = new RegexValidator("本地IP格式错误"); + + static { + EmailFormat.setRegexPattern("^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$"); + AccoountValidator.setRegexPattern("^[a-zA-Z0-9_-]*$"); + PortFormat.setRegexPattern("^([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{4}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$"); + IpFormat.setRegexPattern("^(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])$"); + } + + /** + * 文本长度校验 + *

message > name + "长度有误" + */ + public static StringLengthValidator getLengthValidator(String name, int length) { + return new StringLengthValidator(name + "长度有误", length); + } + + /** + * 文本长度校验 + *

message > name + "长度有误,应在" + min + "到" + max + "之间" + */ + public static RegexValidator getLengthValidator(int min, int max, String name) { + String message = name + "长度有误,应在" + min + "到" + max + "之间"; + + RegexValidator validator = new RegexValidator(message); + + validator.setRegexPattern("[a-zA-Z0-9_-]{" + min + "," + max + "}$"); + + return validator; + } + + /** + * 域名格式检查 + */ + public static ValidatorBase domainFormatValidator(ProxySetupModel model) { + + return new ValidatorBase("域名格式错误") { + @Override + protected void eval() { + if (!DomainUtil.isCustomize(model.get())) + hasErrors.set(false); + else { + boolean matches = Pattern.compile("^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$") + .matcher(model.getDomain() + model.getDomainSuffix()).matches(); + hasErrors.set(!matches); + } + } + }; + } + + /** + * 域名格式检查 + */ + public static ValidatorBase domainLengthValidator(ProxySetupModel model) { + + return new RegexValidator("请输入子域名,长度不小于3个字符") { + @Override + protected void eval() { + setRegexPattern("^[a-zA-Z0-9_-]{3,18}$"); + if (DomainUtil.isHttp(model) && !DomainUtil.isCustomize(model.get())) { + super.eval(); + } else { + hasErrors.set(false); + } + } + }; + } + + /** + * 自定义域名解析检查 + */ + public static ValidatorBase domainAddressValidator(ProxySetupModel model) { + + return new ValidatorBase("请输入您的域名,并解析至: " + ProxyConfig.getServerIP(model.getServer())) { + @Override + protected void eval() { + if (!DomainUtil.isCustomize(model.get())) + hasErrors.set(false); + else + hasErrors.set( + !Objects.equals( + ProxyConfig.getServerIP(model.getServer()), + DomainUtil.getDomainAddress(model.getDomain()) + ) + ); + } + }; + } +} diff --git a/src/main/java/top/octopusyan/controller/LoginController.java b/src/main/java/top/octopusyan/controller/LoginController.java index ce2546a..931bda8 100644 --- a/src/main/java/top/octopusyan/controller/LoginController.java +++ b/src/main/java/top/octopusyan/controller/LoginController.java @@ -5,39 +5,28 @@ import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXPasswordField; import com.jfoenix.controls.JFXTextField; import javafx.application.Platform; -import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; import org.kordamp.ikonli.javafx.FontIcon; import top.octopusyan.base.BaseController; -import top.octopusyan.common.http.EasyHttp; -import top.octopusyan.common.http.api.NotParamApi; -import top.octopusyan.common.http.config.HttpConstant; -import top.octopusyan.common.http.listener.OnHttpListener; +import top.octopusyan.manager.http.EasyHttp; +import top.octopusyan.manager.http.api.NotParamApi; +import top.octopusyan.manager.http.config.HttpConstant; +import top.octopusyan.manager.http.listener.OnHttpListener; +import top.octopusyan.config.TextValidate; import top.octopusyan.http.Api; import top.octopusyan.http.request.LoginParam; -import top.octopusyan.http.request.ProxySetup; -import top.octopusyan.utils.AlertUtil; -import top.octopusyan.utils.ApplicatonStore; -import top.octopusyan.utils.ProxyUtil; -import top.octopusyan.utils.TextValidate; +import top.octopusyan.utils.*; -import java.net.URL; -import java.util.List; -import java.util.ResourceBundle; -import java.util.stream.Collectors; +import java.io.IOException; /** * @author : octopus yan @@ -119,8 +108,8 @@ public class LoginController extends BaseController implements Initia String account = ApplicatonStore.getAccount(); String password = ApplicatonStore.getPassword(); - if(!StringUtils.isEmpty(account)) accountTextField.setText(account); - if(!StringUtils.isEmpty(password)) { + if (!StringUtils.isEmpty(account)) accountTextField.setText(account); + if (!StringUtils.isEmpty(password)) { passwordTextField.setText(password); seePwdTextField.setText(password); } @@ -172,7 +161,13 @@ public class LoginController extends BaseController implements Initia public void initViewAction() { // 注册 - registerBtn.setOnMouseClicked(event -> jumpTo(new RegisterController())); + registerBtn.setOnMouseClicked(event -> { + try { + jumpTo(new RegisterController()); + } catch (IOException e) { + e.printStackTrace(); + } + }); // 查看密码 seePwdIconBtn.setOnMouseClicked(event -> { @@ -187,22 +182,23 @@ public class LoginController extends BaseController implements Initia // 登录 loginBtn.setOnMouseClicked(event -> login()); accountTextField.setOnKeyPressed(event -> { - if(event.getCode() == KeyCode.ENTER) login(); + if (event.getCode() == KeyCode.ENTER) login(); }); passwordTextField.setOnKeyPressed(event -> { - if(event.getCode() == KeyCode.ENTER) login(); + if (event.getCode() == KeyCode.ENTER) login(); }); // 自动登录 - if((ApplicatonStore.isAutoLogin() || ApplicatonStore.isRegisterSuccess()) && + if ((ApplicatonStore.isAutoLogin() || ApplicatonStore.isRegisterSuccess()) && !StringUtils.isEmpty(ApplicatonStore.getAccount()) && !StringUtils.isEmpty(ApplicatonStore.getPassword()) - ){ + ) { login(); } } - private void login(){ + private void login() { + // 获取文本校验结果 boolean pwdValidate = pwdParent.getChildren().contains(passwordTextField) ? passwordTextField.validate() : seePwdTextField.validate(); @@ -215,15 +211,20 @@ public class LoginController extends BaseController implements Initia .request(new OnHttpListener() { @Override public void onSucceed(String result) { - Document html = Jsoup.parse(result); // 登录出错 - if (result.contains("alert-danger")) { - Platform.runLater(() -> AlertUtil.error(getHtmlErrorMessage(html)).show()); + if (!JsoupUtil.isAlertSuccess(result)) { + Platform.runLater(() -> AlertUtil.error(JsoupUtil.getErrorMessage(result)).show()); return; } // TODO 登录成功 setCsrf(); - jumpTo(new MainController()); + Platform.runLater(() -> { + try { + jumpTo(new MainController()); + } catch (IOException e) { + e.printStackTrace(); + } + }); } }); } @@ -242,10 +243,6 @@ public class LoginController extends BaseController implements Initia }); } - private String getHtmlErrorMessage(Document html) { - return html.body().getElementsByClass("alert alert-danger alert-dismissable").text().substring(1); - } - @Override public void onDestroy() { EasyHttp.cancel(); diff --git a/src/main/java/top/octopusyan/controller/MainController.java b/src/main/java/top/octopusyan/controller/MainController.java index 41b57c2..dcdc43b 100644 --- a/src/main/java/top/octopusyan/controller/MainController.java +++ b/src/main/java/top/octopusyan/controller/MainController.java @@ -1,14 +1,22 @@ package top.octopusyan.controller; import com.jfoenix.controls.*; +import com.jfoenix.validation.IntegerValidator; +import com.jfoenix.validation.RequiredFieldValidator; +import javafx.application.HostServices; import javafx.application.Platform; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; +import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; @@ -19,13 +27,18 @@ import top.octopusyan.config.ProxyConfig; import top.octopusyan.config.ProxyConfig.ProxyServer; import top.octopusyan.config.ProxyConfig.ProxyType; import top.octopusyan.http.request.ProxySetup; +import top.octopusyan.model.ProxySetupModel; import top.octopusyan.utils.AlertUtil; -import top.octopusyan.utils.ApplicatonStore; +import top.octopusyan.utils.DomainUtil; +import top.octopusyan.utils.FrpUtil; import top.octopusyan.utils.ProxyUtil; +import top.octopusyan.config.TextValidate; +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.*; import java.util.List; /** @@ -40,6 +53,9 @@ public class MainController extends BaseController implements Initial public static final String PROXY_LIST_ITEM_STOP_CLASS = "proxyListItem-stop"; public static final String PROXY_LIST_ITEM_RUN_CLASS = "proxyListItem-run"; public static final String PROXY_LIST_ITEM_CLOSE_CLASS = "proxyListItem-close"; + public static final String PROXY_LIST_ITEM_SELECT_CLASS = "proxyListItem-select"; + public static final String INPUT_LEFT_CLASS = "inputText-left"; + public static final String INPUT_CLASS = "inputText"; @FXML public StackPane root; @@ -48,11 +64,9 @@ public class MainController extends BaseController implements Initial public JFXButton closeBtn, minimizeBtn; @FXML - public JFXButton startProxyBtn; + public JFXButton startProxyBtn, addProxyBtn; @FXML - public JFXButton addProxyBtn; - @FXML - public JFXComboBox

author : octopus yan + *

email : octopus_yan@foxmail.com + *

description : + *

create : 2022-4-9 01:45 * + */ +public class FxmlUtil { + + public static FXMLLoader init(String path) { + FXMLLoader fxmlLoader = new FXMLLoader(); + fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory()); + fxmlLoader.setLocation(FxmlUtil.class.getResource(path)); + return fxmlLoader; + } +} diff --git a/src/main/java/top/octopusyan/utils/JsoupUtil.java b/src/main/java/top/octopusyan/utils/JsoupUtil.java new file mode 100644 index 0000000..5436df9 --- /dev/null +++ b/src/main/java/top/octopusyan/utils/JsoupUtil.java @@ -0,0 +1,43 @@ +package top.octopusyan.utils; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; + +/** + * @author : octopus yan + * @email : octopus_yan@foxmail.com + * @description : html解析工具类 + * @create : 2022-4-8 11:04 + */ +public class JsoupUtil { + + public static Document getDocument(String htmlStr) { + return Jsoup.parse(htmlStr); + } + + public static boolean isAlertSuccess(String htmlStr) { + return !htmlStr.contains("alert-danger"); + } + + public static String getErrorMessage(String htmlStr) { + return getDocument(htmlStr).body().getElementsByClass("alert alert-danger alert-dismissable").text().substring(1); + } + + public static String getHtmlMessage(String htmlStr) { + return getDocument(htmlStr).getElementsByClass("alert alert-success alert-dismissable").text().substring(1); + } + + public static String getServerConfiguration(String htmlStr) { + Document document = getDocument(htmlStr); + String configuration = document.getElementsByTag("pre").get(0).text(); + int end = configuration.indexOf("\n\n"); + // 服务器配置获取失败 + if (end == -1 && !configuration.contains("[common]")) return null; + // 没有 + if (end == -1) { + configuration += "\n\n"; + return configuration; + } + return configuration.substring(0, end + 2); + } +} diff --git a/src/main/java/top/octopusyan/utils/ProxyUtil.java b/src/main/java/top/octopusyan/utils/ProxyUtil.java index ebf6c61..5015d33 100644 --- a/src/main/java/top/octopusyan/utils/ProxyUtil.java +++ b/src/main/java/top/octopusyan/utils/ProxyUtil.java @@ -1,17 +1,24 @@ package top.octopusyan.utils; +import javafx.application.Platform; +import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; -import top.octopusyan.common.http.EasyHttp; -import top.octopusyan.common.http.listener.OnHttpListener; -import top.octopusyan.common.http.model.ResponseClass; -import top.octopusyan.config.ProxyConfig.*; +import top.octopusyan.manager.http.EasyHttp; +import top.octopusyan.manager.http.listener.OnHttpListener; +import top.octopusyan.manager.http.model.ResponseClass; +import top.octopusyan.config.ProxyConfig; +import top.octopusyan.config.ProxyConfig.ProxyServer; +import top.octopusyan.config.ProxyConfig.ProxyType; import top.octopusyan.http.Api; import top.octopusyan.http.request.ProxySetup; +import top.octopusyan.model.ProxySetupModel; -import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -22,32 +29,38 @@ import java.util.stream.Collectors; */ public class ProxyUtil { private static String csrf; + private static Map serverConfigraution = new HashMap<>(3); /** * 初始化隧道设置 */ - public static ProxySetup initProxy() { + public static ProxySetup initProxy(Integer maxsort) { ProxySetup setup = new ProxySetup(); setup.setNode(3);// 上海服务器 setup.setLocal_ip("127.0.0.1"); setup.setLocal_port(80); setup.setProxy_name("默认连接"); - setup.setProxy_type(ProxyType.HTTP.name()); + setup.setProxy_type(StringUtils.lowerCase(ProxyType.HTTP.name())); + setup.setDomain(".frp.octopusyan.top"); + setup.setSort(1); + if (maxsort != null) setup.setSort(maxsort + 1); setup.setUse_compression(true); setup.setUse_encryption(true); + setup.setStatus(true); + setup.setRuning(false); return setup; } public static void delete(Integer id) { EasyHttp.builder() .api(Api.DeleteProxy()) - .pathParam(id, csrf) - .request(result -> { }); + .pathParam(String.valueOf(id), csrf) + .request(result -> { + }); } /** * 获取隧道详情信息 - * @return */ public static ProxySetup info(ProxySetup setup) { @@ -55,7 +68,7 @@ public class ProxyUtil { try { html = EasyHttp.builder() .api(Api.ProxyInfo()) - .pathParam(setup.getId()) + .pathParam(String.valueOf(setup.getId())) .execute(new ResponseClass() { }); } catch (Exception e) { @@ -75,14 +88,14 @@ public class ProxyUtil { String domain = select.get(8).text(); String status = select.get(13).text(); - Arrays.stream(ProxyServer.values()).forEach(server -> { - if (serverStr.contains(server.name())) - setup.setNode(Arrays.asList(ProxyServer.values()).indexOf(server)); - }); - + for (ProxyServer value : ProxyServer.values()) { + String name = value.getServerName(); + if (serverStr.contains(name)) + setup.setNode(ProxyConfig.getServerNode(name)); + } setup.setLocal_ip(localIp); setup.setLocal_port(Integer.parseInt(localPort)); - setup.setRemote_port("无".equals(webPort) ? null : Integer.parseInt(webPort)); + setup.setRemote_port("无".equals(webPort) ? "" : webPort); setup.setDomain(domain); setup.setUse_compression(useCom.equals("启用")); setup.setUse_encryption(useEnc.equals("启用")); @@ -91,14 +104,15 @@ public class ProxyUtil { return setup; } - public static int randomPort(){ + public static int randomPort() { try { - return EasyHttp.builder() + return Integer.parseInt(EasyHttp.builder() .api(Api.RandomPort()) - .execute(new ResponseClass() { }); + .execute(new ResponseClass() { + })); } catch (Exception e) { e.printStackTrace(); - return (int) (Math.random() * 31000); + return (int) (Math.random() * 1000 + 30000); } } @@ -121,33 +135,35 @@ public class ProxyUtil { proxySetup.setProxy_name(tds.get(1).text()); proxySetup.setProxy_type(tds.get(2).text()); proxySetup.setSort(Integer.parseInt(tds.get(5).text())); + + ProxySetup info = info(proxySetup); + if (info != null) return info; return proxySetup; }).collect(Collectors.toList()); - listener.onSucceed(proxySetupList); + Platform.runLater(() -> listener.onSucceed(proxySetupList)); + } + + @Override + public void onFail(Exception e) { + AlertUtil.exception(e).show(); } }); } - public static boolean add(OnHttpListener listener, ProxySetup setup) { - String result = null; - try { - result = EasyHttp.builder() - .api(Api.AddProxy()) - .param(setup) - .execute(new ResponseClass() { - }); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - - if(result.equals("隧道创建成功")){ - getList(result1 -> listener.onSucceed(result1.get(result1.size()-1))); - } else { - AlertUtil.error(result).show(); - } - return result.equals("隧道创建成功"); + public static void add(OnHttpListener listener, ProxySetup setup) { + EasyHttp.builder() + .api(Api.AddProxy()) + .param(setup) + .request(result -> getList(result1 -> { + if (result1 != null && result1.size() > 0) { + for (ProxySetup proxySetup : result1) { + if (Objects.equals(proxySetup.getSort(), proxySetup.getSort())) { + listener.onSucceed(proxySetup); + } + } + } + })); } public static String getCsrf() { @@ -159,4 +175,53 @@ public class ProxyUtil { int i1 = htmlStr.indexOf("\"", i + 18); ProxyUtil.csrf = htmlStr.substring(i + 18, i1); } + + public static String getUserServerConfig(int node) { + String config = serverConfigraution.get(node); + if (StringUtils.isNotEmpty(config)) return config; + try { + String result = EasyHttp.builder() + .api(Api.ServerConfiguration) + .pathParam(String.valueOf(node)) + .execute(new ResponseClass() { + }); + config = JsoupUtil.getServerConfiguration(result); + if (StringUtils.isNotEmpty(config)) serverConfigraution.put(node, config); + return config; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String getProxyConfig(ProxySetupModel setup) { + String n = "\n"; + // 基础配置 + StringBuffer stringBuffer = new StringBuffer("[" + setup.getProxyName() + "]\n"); + stringBuffer.append("privilege_mode = true\n") + .append("type = ").append(setup.getProxyType()).append(n) + .append("local_ip = ").append(setup.getLocalIp()).append(n) + .append("local_port = ").append(setup.getLocalPort()).append(n).append(n); + + if ("http".equals(setup.getProxyType()) || "https".equals(setup.getProxyType())) { + // HTTP / HTTPS + stringBuffer.append("custom_domains = ").append(setup.getDomain()).append(setup.getDomainSuffix()).append(n); + if (!StringUtils.isEmpty(setup.getLocations())) + stringBuffer.append("locations = ").append(setup.getLocations()).append(n); + if (!StringUtils.isEmpty(setup.getLocations())) + stringBuffer.append("host_header_rewrite = ").append(setup.getHostHeaderRewrite()).append(n); + if (!StringUtils.isEmpty(setup.getLocations())) + stringBuffer.append("header_X-From-Where = ").append(setup.getHeader_X_From_Where()).append(n); + } else { + // TCP / UDP / XTCP / STCP + stringBuffer.append("remote_port = ").append(setup.getRemotePort()).append(n); + if (!StringUtils.isEmpty(setup.getSk())) + stringBuffer.append("sk = ").append(setup.getSk()).append(n); + } + // 压缩和加密 + stringBuffer.append("use_encryption = ").append(setup.isUseEncryption()).append(n) + .append("use_compression = ").append(setup.isUseCompression()).append(n).append(n); + + return stringBuffer.toString(); + } } diff --git a/src/main/java/top/octopusyan/utils/TextValidate.java b/src/main/java/top/octopusyan/utils/TextValidate.java deleted file mode 100644 index 8b4e8c2..0000000 --- a/src/main/java/top/octopusyan/utils/TextValidate.java +++ /dev/null @@ -1,64 +0,0 @@ -package top.octopusyan.utils; - -import com.jfoenix.validation.RegexValidator; -import com.jfoenix.validation.RequiredFieldValidator; -import com.jfoenix.validation.StringLengthValidator; -import com.jfoenix.validation.base.ValidatorBase; -import org.apache.commons.lang3.StringUtils; - -/** - * @author : octopus yan - * @email : octopus_yan@foxmail.com - * @description : JFX文本校验 - * @create : 2022-4-2 17:03 - */ -public class TextValidate { -// /** -// * 账号格式错误 -// */ -// public static ValidatorBase AccountFail = new ValidatorBase() { -// @Override -// protected void eval() { -// setMessage("账号格式错误"); -// hasErrors.set(true); -// } -// }; - - /** 账号不能为空 */ - public static RequiredFieldValidator AccoountRequired = new RequiredFieldValidator("账号不能为空!"); - - /** 账号格式 */ - public static RegexValidator AccoountValidator = new RegexValidator("账号格式有误!"); - /** 密码不能为空 */ - public static RequiredFieldValidator PasswordRequired = new RequiredFieldValidator("密码不能为空!"); - - /** 邮箱地址格式验证 */ - public static RegexValidator EmailFormat = new RegexValidator("邮箱地址格式错误"); - - static { - EmailFormat.setRegexPattern("^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$"); - AccoountValidator.setRegexPattern("^[a-zA-Z0-9_-]*$"); - } - - /** - * 文本长度校验 - *

message > name + "长度有误" - */ - public static StringLengthValidator getLengthValidator(String name, int length){ - return new StringLengthValidator(name+"长度有误", length); - } - - /** - * 文本长度校验 - *

message > name + "长度有误,应在" + min + "到" + max + "之间" - */ - public static RegexValidator getLengthValidator(int min, int max, String name){ - String message = name + "长度有误,应在" + min + "到" + max + "之间"; - - RegexValidator validator = new RegexValidator(message); - - validator.setRegexPattern("[a-zA-Z0-9_-]{" + min + "," + max + "}$"); - - return validator; - } -} diff --git a/src/main/resources/css/main.css b/src/main/resources/css/main.css index 9c0cea1..b69945e 100644 --- a/src/main/resources/css/main.css +++ b/src/main/resources/css/main.css @@ -16,7 +16,7 @@ -fx-opacity: 0.4; } /* 布局边框 */ -.mainPane, #addProxyBtn , .jfx-list-view{ +.mainPane, #addProxyBtn, .jfx-list-view{ -fx-border-color: linear-gradient(#57b4f2, #9198e5); } @@ -38,16 +38,23 @@ -fx-border-radius: 0 5 5 0; } /* 服务线路 */ -#proxyServerCB { +#proxyServerComboBox { -fx-font-size: 16; -fx-text-fill: #757575; } /* TODO 隧道列表 */ #proxyListView .list-cell:selected { - -fx-text-fill: white; -fx-background-color: linear-gradient(#9198e5, #57b4f2); } +#proxyListView .list-cell:hover { + -fx-opacity: 0.7; + -fx-background-color: linear-gradient(#9198e5, #57b4f2); +} +#proxyListView .list-cell:hover lable, #proxyListView .list-cell:selected label, .proxyListItem-select{ + -fx-text-fill: white; +} + .proxyListItem-run FontIcon { -fx-icon-color: linear-gradient(#95f257, #91e5ac); } @@ -73,18 +80,20 @@ -fx-font-family: "Microsoft YaHei"; } /* 隧道类型 */ -#proxyProtocolCombox { +#proxyProtocolComboBox { -fx-font-size: 14px; -fx-font-family: "Microsoft YaHei"; } /* 自定义域名 */ -#customizeDomainBtn { +#customizeDomainBtn, #resetProxyBtn, #copyDomainBtn { -fx-text-fill: linear-gradient(#57b4f2, #9198e5); } /* 添加隧道 */ #addProxyBtn { -fx-border-width: 0 1 1; -fx-font-size: 14px; + -fx-text-fill: white; + -fx-background-color: linear-gradient(rgba(87, 180, 242, 0.7), rgba(145, 152, 229, 0.71)); -fx-font-family: "Microsoft YaHei"; } /* 启动 */ @@ -102,7 +111,12 @@ } /* 修改Tab的背景颜色 */ - .jfx-tab-pane .tab-header-background { +.jfx-tab-pane .tab-header-background { -fx-background-color: linear-gradient(#57b4f2, #9198e5); } - +/* 日志 */ +.text-area { + -fx-font-size: 15; + -fx-font-family: monospace; + -fx-background-color: white; +} diff --git a/src/main/resources/css/root.css b/src/main/resources/css/root.css index 7efddac..b8bcd17 100644 --- a/src/main/resources/css/root.css +++ b/src/main/resources/css/root.css @@ -1,8 +1,8 @@ -.root, #root{ +.root, #root { -fx-background-radius: 14; } -#titleLable, #closeBtn, #minimizeBtn{ +#titleLable, #closeBtn, #minimizeBtn { -fx-font-family: "Microsoft YaHei"; -fx-text-fill: white; } @@ -28,8 +28,13 @@ #closeBtn:hover { -fx-background-color: rgba(255, 0, 0, 0.70); + -fx-text-fill: white; } #minimizeBtn:hover { -fx-background-color: rgba(243, 242, 242, 0.20); +} + +.button:hover, .jfx-button:hover { + -fx-opacity: 0.8; } \ No newline at end of file diff --git a/src/main/resources/fxml/main.fxml b/src/main/resources/fxml/main.fxml index 3f3df92..2fa04ef 100644 --- a/src/main/resources/fxml/main.fxml +++ b/src/main/resources/fxml/main.fxml @@ -5,8 +5,10 @@ + + @@ -20,177 +22,228 @@ - + - + - - - - + + + + + + + + - - + + - - + + - - - - - - - - - - - - - + + + + - + + + + + + + + + - + + + + + - + + + + + + + + + - - - - - - - - - - - - + - + + + + - - + + - - - @@ -200,16 +253,23 @@ - + - + + + + - - - + + + + + diff --git a/src/main/resources/static/frpc.exe b/src/main/resources/static/frpc.exe new file mode 100644 index 0000000..3d48b9a Binary files /dev/null and b/src/main/resources/static/frpc.exe differ