diff --git a/pom.xml b/pom.xml index cd929eb..1bbac05 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ top.octopusyan YanFrp - 1.0-SNAPSHOT + 0.1.2-SNAPSHOT YanFrp diff --git a/src/main/java/top/octopusyan/YanFrpApplication.java b/src/main/java/top/octopusyan/YanFrpApplication.java index 0912428..45612b7 100644 --- a/src/main/java/top/octopusyan/YanFrpApplication.java +++ b/src/main/java/top/octopusyan/YanFrpApplication.java @@ -3,7 +3,6 @@ package top.octopusyan; 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; @@ -12,13 +11,14 @@ 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.FrpManager; 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.model.ApplicatonStore; import top.octopusyan.utils.AlertUtil; -import top.octopusyan.utils.ApplicatonStore; -import top.octopusyan.utils.FrpUtil; +import top.octopusyan.utils.EncryptionUtil; import top.octopusyan.utils.FxmlUtil; /** @@ -35,8 +35,14 @@ public class YanFrpApplication extends Application { super.init(); logger.info("init..."); + // 初始化加密工具 + EncryptionUtil.init(); + + // 初始化应用数据 + ApplicatonStore.init(); + // 初始化frp客户端临时文件 - FrpUtil.initFrpc(); + FrpManager.initFrpc(); // 网络请求设置 HttpConfig.with(OkHttpClientConfig.httpClient()) @@ -64,8 +70,6 @@ public class YanFrpApplication extends Application { // 初始化弹窗 AlertUtil.initOwner(stage); - // 初始化应用数据 - ApplicatonStore.setRegisterSuccess(false); try { // 静态写法无法获取controler @@ -102,7 +106,7 @@ public class YanFrpApplication extends Application { @Override public void stop() throws Exception { super.stop(); - FrpUtil.clearTmp(); + FrpManager.clearTmp(); logger.info("stop..."); } diff --git a/src/main/java/top/octopusyan/YanFrpLuncher.java b/src/main/java/top/octopusyan/YanFrpLuncher.java index 76db4fe..32db0e4 100644 --- a/src/main/java/top/octopusyan/YanFrpLuncher.java +++ b/src/main/java/top/octopusyan/YanFrpLuncher.java @@ -1,24 +1,9 @@ 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; +import top.octopusyan.manager.FrpManager; + +import java.io.IOException; /** * @author : octopus yan @@ -28,6 +13,12 @@ import top.octopusyan.utils.FxmlUtil; */ public class YanFrpLuncher { public static void main(String[] args) { + try { + Runtime.getRuntime().exec("Taskkill /IM " + FrpManager.FRPC_CLIENT_FILE_NAME + " /f"); + } catch (IOException e) { + e.printStackTrace(); + } + 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 174ac65..f6423fb 100644 --- a/src/main/java/top/octopusyan/base/BaseController.java +++ b/src/main/java/top/octopusyan/base/BaseController.java @@ -12,6 +12,9 @@ import javafx.scene.layout.Pane; import javafx.stage.Stage; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; +import top.octopusyan.manager.FrpManager; +import top.octopusyan.manager.http.EasyHttp; +import top.octopusyan.model.ApplicatonStore; import top.octopusyan.utils.FxmlUtil; import top.octopusyan.utils.Loading; @@ -184,11 +187,18 @@ public abstract class BaseController

implements Initializable { * 关闭窗口 */ public void onDestroy() { + // 取消所有请求 + EasyHttp.cancel(); + // 停止所有代理隧道 + FrpManager.frpcList.forEach(FrpManager::stop); + // 保存应用数据 + ApplicatonStore.save(); + Stage stage = (Stage) getRootPanel().getScene().getWindow(); stage.hide(); stage.close(); try { - Thread.sleep(2000); + Thread.sleep(1000); Platform.exit(); System.exit(0); } catch (InterruptedException e) { diff --git a/src/main/java/top/octopusyan/config/ProxyConfig.java b/src/main/java/top/octopusyan/config/ProxyConfig.java index c7b34f9..9fff32d 100644 --- a/src/main/java/top/octopusyan/config/ProxyConfig.java +++ b/src/main/java/top/octopusyan/config/ProxyConfig.java @@ -31,6 +31,7 @@ public class ProxyConfig { typePort.put("https", 80); typePort.put("ssh", 22); typePort.put("tcp", 0); + typePort.put("udp", 0); } @@ -60,6 +61,7 @@ public class ProxyConfig { HTTPS("https"), SSH("tcp"), TCP("tcp"), + UDP("udp"), ; private final String type; diff --git a/src/main/java/top/octopusyan/config/TextValidate.java b/src/main/java/top/octopusyan/config/TextValidate.java index d5655f6..4d24786 100644 --- a/src/main/java/top/octopusyan/config/TextValidate.java +++ b/src/main/java/top/octopusyan/config/TextValidate.java @@ -4,7 +4,6 @@ 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; @@ -100,12 +99,19 @@ public class TextValidate { */ public static ValidatorBase domainFormatValidator(ProxySetupModel model) { - return new ValidatorBase("域名格式错误") { + return new ValidatorBase("域名格式错误,支持数字字母下划线") { @Override protected void eval() { - if (!DomainUtil.isCustomize(model.get())) - hasErrors.set(false); - else { + if (!DomainUtil.isCustomize(model.get())) { + if (DomainUtil.isHttp(model)) { + // http / https + boolean matches = Pattern.compile("^[a-zA-Z0-9_-]{3,18}$").matcher(model.getDomain()).matches(); + hasErrors.set(!matches); + } else { + // tcp / udp + 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); @@ -119,7 +125,7 @@ public class TextValidate { */ public static ValidatorBase domainLengthValidator(ProxySetupModel model) { - return new RegexValidator("请输入子域名,长度不小于3个字符") { + return new RegexValidator("子域名长度不小于3个字符") { @Override protected void eval() { setRegexPattern("^[a-zA-Z0-9_-]{3,18}$"); diff --git a/src/main/java/top/octopusyan/controller/LoginController.java b/src/main/java/top/octopusyan/controller/LoginController.java index dca73da..0384272 100644 --- a/src/main/java/top/octopusyan/controller/LoginController.java +++ b/src/main/java/top/octopusyan/controller/LoginController.java @@ -5,6 +5,7 @@ import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXPasswordField; import com.jfoenix.controls.JFXTextField; import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; @@ -17,14 +18,18 @@ import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.kordamp.ikonli.javafx.FontIcon; import top.octopusyan.base.BaseController; +import top.octopusyan.config.TextValidate; +import top.octopusyan.http.Api; +import top.octopusyan.http.request.FindPassParam; +import top.octopusyan.http.request.LoginParam; +import top.octopusyan.manager.ProxyManager; 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.utils.*; +import top.octopusyan.model.ApplicatonStore; +import top.octopusyan.utils.AlertUtil; +import top.octopusyan.utils.JsoupUtil; import java.io.IOException; @@ -69,8 +74,10 @@ public class LoginController extends BaseController implements Initia public JFXCheckBox autoLoginCBox; @FXML public JFXCheckBox rememberCBox; + @FXML + public JFXButton findpassBtn; - private boolean autoLogin; + private SimpleStringProperty tmppwd = new SimpleStringProperty(); @Override public boolean dragWindow() { @@ -105,19 +112,27 @@ public class LoginController extends BaseController implements Initia @Override public void initData() { - String account = ApplicatonStore.getAccount(); - String password = ApplicatonStore.getPassword(); + // 数据绑定 + // 账号 + accountTextField.textProperty().bindBidirectional(ApplicatonStore.accountProperty()); + // 密码 + if (ApplicatonStore.isRememberMe()) tmppwd.set(ApplicatonStore.getPassword()); + // 自动登录 + autoLoginCBox.selectedProperty().bindBidirectional(ApplicatonStore.autoLoginProperty()); + // 记住我 + rememberCBox.selectedProperty().bindBidirectional(ApplicatonStore.rememberMeProperty()); + + + passwordTextField.textProperty().bindBidirectional(tmppwd); + seePwdTextField.textProperty().bindBidirectional(tmppwd); - if (!StringUtils.isEmpty(account)) accountTextField.setText(account); - if (!StringUtils.isEmpty(password)) { - passwordTextField.setText(password); - seePwdTextField.setText(password); - } } // 隐藏展示密码框 private AnchorPane pwdParent; + private AlertUtil.Builder findpassAlert; + @Override public void initViewStyle() { @@ -136,11 +151,6 @@ public class LoginController extends BaseController implements Initia pwdParent = (AnchorPane) passwordTextField.getParent(); pwdParent.getChildren().remove(seePwdTextField); - // 同步用户数据 - accountTextField.textProperty().addListener((observable, oldValue, newValue) -> ApplicatonStore.setAccount(newValue)); - passwordTextField.textProperty().addListener((observable, oldValue, newValue) -> ApplicatonStore.setPassword(newValue)); - seePwdTextField.textProperty().addListener((observable, oldValue, newValue) -> ApplicatonStore.setPassword(newValue)); - // 添加文本校验 accountTextField.getValidators().add(TextValidate.AccoountRequired); accountTextField.getValidators().add(TextValidate.AccoountValidator); @@ -148,12 +158,9 @@ public class LoginController extends BaseController implements Initia passwordTextField.getValidators().add(TextValidate.PasswordRequired); seePwdTextField.getValidators().add(TextValidate.PasswordRequired); - // 记住密码 - rememberCBox.selectedProperty().addListener((observable, oldValue, newValue) -> ApplicatonStore.setRememberMe(newValue)); // 自动登录 autoLoginCBox.selectedProperty().addListener((observable, oldValue, newValue) -> { - ApplicatonStore.setAutoLogin(newValue); - if (newValue && !ApplicatonStore.isRememberMe()) rememberCBox.selectedProperty().set(true); + if (newValue) ApplicatonStore.setRememberMe(true); }); } @@ -162,6 +169,7 @@ public class LoginController extends BaseController implements Initia // 注册 registerBtn.setOnMouseClicked(event -> { + ApplicatonStore.setPassword(tmppwd.get()); try { jumpTo(new RegisterController()); } catch (IOException e) { @@ -175,7 +183,7 @@ public class LoginController extends BaseController implements Initia pwdParent.getChildren().remove(isHide ? passwordTextField : seePwdTextField); pwdParent.getChildren().add(1, isHide ? seePwdTextField : passwordTextField); - (isHide ? seePwdTextField : passwordTextField).setText(ApplicatonStore.getPassword()); + (isHide ? seePwdTextField : passwordTextField).setText(tmppwd.get()); seePwdIcon.setIconColor(isHide ? Color.BLUE : Color.BLACK); }); @@ -188,6 +196,14 @@ public class LoginController extends BaseController implements Initia if (event.getCode() == KeyCode.ENTER) login(); }); + // 找回密码 + findpassBtn.setOnMouseClicked(event -> { + findpassAlert = AlertUtil.input("请输入您的账号或邮箱") + .title("找回密码") + .header(null); + findpass(findpassAlert.getInput()); + }); + // 自动登录 if ((ApplicatonStore.isAutoLogin() || ApplicatonStore.isRegisterSuccess()) && !StringUtils.isEmpty(ApplicatonStore.getAccount()) && @@ -197,6 +213,33 @@ public class LoginController extends BaseController implements Initia } } + /** + * 找回密码 + */ + private void findpass(String input) { + if (StringUtils.isNotEmpty(input)) + EasyHttp.builder() + .api(Api.findpass) + .param(new FindPassParam(input)) + .request(new OnHttpListener() { + @Override + public void onSucceed(String result) { + Platform.runLater(() -> { + if (JsoupUtil.isAlertSuccess(result)) { + AlertUtil.info(JsoupUtil.getSuccessMessage(result)).show(); + } else { + AlertUtil.error(JsoupUtil.getErrorMessage(result)).show(); + } + }); + } + + @Override + public void onFail(Exception e) { + Platform.runLater(() -> AlertUtil.exceptionAlert(e).show()); + } + }); + } + private void login() { // 获取文本校验结果 @@ -207,7 +250,7 @@ public class LoginController extends BaseController implements Initia if (pwdValidate && accountValidate) EasyHttp.builder() .api(Api.Login) - .param(new LoginParam(accountTextField.getText(), ApplicatonStore.getPassword())) + .param(new LoginParam(accountTextField.getText(), tmppwd.get())) .request(new OnHttpListener() { @Override public void onSucceed(String result) { @@ -216,8 +259,11 @@ public class LoginController extends BaseController implements Initia Platform.runLater(() -> AlertUtil.error(JsoupUtil.getErrorMessage(result)).show()); return; } - // TODO 登录成功 + // 登录成功 setCsrf(); + // 记住我 + ApplicatonStore.rememberMe(tmppwd.get()); + // 跳转 Platform.runLater(() -> { try { jumpTo(new MainController()); @@ -243,7 +289,7 @@ public class LoginController extends BaseController implements Initia .request(new OnHttpListener() { @Override public void onSucceed(String result) { - ProxyUtil.setCsrf(result); + ProxyManager.setCsrf(result); } @Override @@ -255,7 +301,6 @@ public class LoginController extends BaseController implements Initia @Override public void onDestroy() { - EasyHttp.cancel(); super.onDestroy(); } } diff --git a/src/main/java/top/octopusyan/controller/MainController.java b/src/main/java/top/octopusyan/controller/MainController.java index d1c3e87..33af128 100644 --- a/src/main/java/top/octopusyan/controller/MainController.java +++ b/src/main/java/top/octopusyan/controller/MainController.java @@ -1,23 +1,17 @@ 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.control.*; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import org.apache.commons.lang3.StringUtils; @@ -26,21 +20,24 @@ import top.octopusyan.base.BaseController; import top.octopusyan.config.ProxyConfig; import top.octopusyan.config.ProxyConfig.ProxyServer; import top.octopusyan.config.ProxyConfig.ProxyType; +import top.octopusyan.config.TextValidate; import top.octopusyan.http.request.ProxySetup; +import top.octopusyan.manager.FrpManager; +import top.octopusyan.manager.ProxyManager; import top.octopusyan.manager.http.listener.OnHttpListener; +import top.octopusyan.model.ApplicatonStore; import top.octopusyan.model.ProxySetupModel; import top.octopusyan.utils.AlertUtil; 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.*; import java.util.List; +import java.util.*; + +import static top.octopusyan.model.ApplicatonStore.*; /** * @author : octopus yan @@ -55,7 +52,6 @@ public class MainController extends BaseController implements Initial 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 @@ -94,15 +90,15 @@ public class MainController extends BaseController implements Initial public Hyperlink domainLink; @FXML public HBox proxyStatusPane; + @FXML + public JFXButton logoutBtn; private ToggleGroup openProxyGroup = new ToggleGroup(); private SimpleBooleanProperty customizeDomain = new SimpleBooleanProperty(false); - private List proxyList = new ArrayList<>(); - private Map frpUtilMap = new HashMap<>(); - private SimpleIntegerProperty selectProxy = new SimpleIntegerProperty(0); + private Map frpUtilMap = new HashMap<>(); private ProxySetupModel proxySetupModel; private ProxySetup proxySetup; - private SimpleBooleanProperty setting = new SimpleBooleanProperty(false); + private SimpleBooleanProperty setup = new SimpleBooleanProperty(false); @Override public boolean dragWindow() { @@ -138,8 +134,12 @@ public class MainController extends BaseController implements Initial @Override public void initData() { // 初始化视图模型 - proxySetup = ProxyUtil.initProxy(null); + proxySetup = ProxyManager.initProxy(null); proxySetupModel = new ProxySetupModel(proxySetup); + // 初始化用户隧道列表 + setProxyList(Collections.singletonList(proxySetup)); + // 重置隧道列表视图 + initProxyListView(); // 隧道类型 Arrays.asList(ProxyType.values()).forEach((type) -> proxyProtocolComboBox.getItems().add(type.name())); @@ -148,24 +148,26 @@ public class MainController extends BaseController implements Initial Arrays.asList(ProxyServer.values()).forEach((server) -> proxyServerComboBox.getItems().add(server.getServerName())); // 获取用户隧道列表 - ProxyUtil.getList(new OnHttpListener>() { + ProxyManager.getList(new OnHttpListener>() { @Override public void onSucceed(List result) { - if (result != null) - proxyList = result; + Platform.runLater(() -> { - // 如果用户隧道列表不为空 - if (proxyList != null && proxyList.size() > 0) { - // 显示隧道列表 - initProxyList(proxyList); - // 默认选中第一个(配置隧道设置模型) - proxyListView.getSelectionModel().select(0); - } else { - // 配置隧道设置模型 - proxyList = new ArrayList<>(); - proxyList.add(proxySetup); - } + // 如果用户隧道列表不为空 + if (result != null && result.size() > 0) { + proxySetup = result.get(selectProxy()); + proxySetupModel.set(result.get(selectProxy())); + // 初始化用户隧道列表 + setProxyList(result); + // 重置隧道列表视图 + initProxyListView(); + } +// else { +// } +// +// bindDataView(); + }); } @Override @@ -199,6 +201,8 @@ public class MainController extends BaseController implements Initial @Override public void initViewStyle() { + // 设置列表 + proxyListView.getSelectionModel().select(0); // 启用链接 openProxyRBtn.setToggleGroup(openProxyGroup); closeProxyRBtn.setToggleGroup(openProxyGroup); @@ -232,7 +236,8 @@ public class MainController extends BaseController implements Initial // 隧道名称 proxySetupModel.proxyNameProperty().addListener((observable, oldValue, newValue) -> { - proxyListView.getItems().get(selectProxy.get()).setText(newValue); + if (proxyListView.getItems().size() > 0) + proxyListView.getItems().get(selectProxy()).setText(newValue); }); // 运行状态监听 @@ -245,42 +250,29 @@ public class MainController extends BaseController implements Initial startProxyBtn.getStyleClass().add(newValue ? stopClass : startClass); startProxyBtn.setText(newValue ? "停止" : "启动"); // 列表显示 - ObservableList styleClass = proxyListView.getItems().get(selectProxy.get()).getStyleClass(); + ObservableList styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass(); styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS); styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS); styleClass.add(newValue ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS); - // 外网访问连接 - if (DomainUtil.isHttp(proxySetupModel)) { - // http / TODO https -// String prefix = DomainUtil.isHttps(proxySetupModel) ? "https://" : "http://"; -// domainLink.textProperty().set(prefix + proxySetupModel.getDomain() + proxySetupModel.getDomainSuffix()); - domainLink.textProperty().set(proxySetupModel.getDomain() + proxySetupModel.getDomainSuffix()); - } else { - // ssh / tcp - domainLink.textProperty().set(ProxyConfig.getServerIP(ProxyConfig.getServerNode(proxySetupModel.getServer())) + ":" + proxySetupModel.getRemotePort()); - } + setDomainLink(); }); // 自定义外网访问地址按钮 customizeDomain.addListener((observable, oldValue, newValue) -> { - HBox parent = (HBox) domainTextField.getParent(); - ObservableList styleClass = domainTextField.getStyleClass(); - styleClass.remove(INPUT_LEFT_CLASS); - styleClass.remove(INPUT_CLASS); + AnchorPane parent = (AnchorPane) domainTextField.getParent(); // 是否切换为自定义域名 if (newValue) { // 隐藏系统域名 if (parent.getChildren().contains(domainSuffixTextField)) parent.getChildren().remove(domainSuffixTextField); // 用户域名是否自定义 - if (!DomainUtil.isCustomize(proxySetup)) { + if (!DomainUtil.isCustomize(proxySetupModel.get())) { proxySetupModel.setDomain(""); } else { - proxySetupModel.setDomain(proxySetup.getDomain()); + proxySetupModel.setDomain(proxySetupModel.getDomain()); } proxySetupModel.setDomainSuffix(""); - styleClass.add(INPUT_CLASS); domainTextField.promptTextProperty().set("自定义域名"); customizeDomainBtn.setText("系统分配"); domainHtinTextField.setText("请输入您的域名,并解析至: " + ProxyConfig.getServerIP(proxySetupModel.getServer())); @@ -289,14 +281,13 @@ public class MainController extends BaseController implements Initial if (!parent.getChildren().contains(domainSuffixTextField)) parent.getChildren().add(domainSuffixTextField); // 用户域名是否自定义 - if (DomainUtil.isCustomize(proxySetup)) { + if (DomainUtil.isCustomize(proxySetupModel.get())) { proxySetupModel.setDomain(""); proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(proxySetupModel.getServer())); } else { - proxySetupModel.setDomain(DomainUtil.getCustomize(proxySetup)); + proxySetupModel.setDomain(DomainUtil.getCustomize(proxySetupModel.get())); proxySetupModel.setDomainSuffix(DomainUtil.getSuffix(proxySetup)); } - styleClass.add(INPUT_LEFT_CLASS); domainTextField.promptTextProperty().set("自定义子域名 大于3位"); customizeDomainBtn.setText("自定义"); domainHtinTextField.setText("请输入子域名,长度不小于3个字符"); @@ -307,10 +298,27 @@ public class MainController extends BaseController implements Initial tabPane.getSelectionModel().select(1); } + /** 设置访问链接 */ + private void setDomainLink() { + // 外网访问连接 + if (DomainUtil.isHttp(proxySetupModel)) { + // http / https + String prefix = proxySetupModel.getProxyType() + "://"; + domainLink.textProperty().set(prefix + proxySetupModel.getDomain() + proxySetupModel.getDomainSuffix()); + } else { + // ssh / tcp + domainLink.textProperty().set(ProxyConfig.getServerIP(ProxyConfig.getServerNode(proxySetupModel.getServer())) + ":" + proxySetupModel.getRemotePort()); + } + } + @Override public void initViewAction() { // 重置 - resetProxyBtn.setOnMouseClicked(event -> proxySetupModel.set(proxySetup)); + resetProxyBtn.setOnMouseClicked(event -> { + proxySetup.setRuning(false); + proxySetupModel.set(proxySetup); + domainTextField.resetValidation(); + }); // 日志清理 clearLogBtn.setOnMouseClicked(event -> { @@ -360,7 +368,7 @@ public class MainController extends BaseController implements Initial }); // 域名检查 - setting.addListener((observable, oldValue, newValue) -> { + setup.addListener((observable, oldValue, newValue) -> { if (newValue) domainTextField.validate(); }); domainTextField.textProperty().addListener((observable, oldValue, newValue) -> { @@ -376,6 +384,9 @@ public class MainController extends BaseController implements Initial // 点击隧道列表 proxyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { ObservableList

author : octopus yan + *

email : octopus_yan@foxmail.com + *

description : 找回密码请求参数 + *

create : 2022-4-10 20:20 * + */ +@Data +@AllArgsConstructor +public class FindPassParam { + private String username; +} diff --git a/src/main/java/top/octopusyan/http/request/ProxySetup.java b/src/main/java/top/octopusyan/http/request/ProxySetup.java index 9fbf196..0c3c79c 100644 --- a/src/main/java/top/octopusyan/http/request/ProxySetup.java +++ b/src/main/java/top/octopusyan/http/request/ProxySetup.java @@ -28,8 +28,10 @@ public class ProxySetup { private String host_header_rewrite; private String header_X_From_Where; private String sk; + private Integer sort; + + // 非保留数据 /** 是否启用 */ private Boolean status; - private Integer sort; private boolean runing; } diff --git a/src/main/java/top/octopusyan/utils/FrpUtil.java b/src/main/java/top/octopusyan/manager/FrpManager.java similarity index 66% rename from src/main/java/top/octopusyan/utils/FrpUtil.java rename to src/main/java/top/octopusyan/manager/FrpManager.java index 4a5ec8c..5a40094 100644 --- a/src/main/java/top/octopusyan/utils/FrpUtil.java +++ b/src/main/java/top/octopusyan/manager/FrpManager.java @@ -1,91 +1,59 @@ -package top.octopusyan.utils; +package top.octopusyan.manager; import javafx.application.Platform; import javafx.scene.control.TextArea; +import lombok.Data; import org.apache.commons.io.FileUtils; -import top.octopusyan.manager.ThreadPoolManager; +import top.octopusyan.model.ApplicatonStore; import top.octopusyan.model.ProxySetupModel; import java.io.*; -import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; /** * @author : octopus yan * @email : octopus_yan@foxmail.com - * @description : TODO frp 工具 + * @description : frp 客户端 工具 * @create : 2022-4-7 23:19 */ -public class FrpUtil { +public class FrpManager { + public static List frpcList = new ArrayList<>(); private static final String YAN_FRP_TEMP_DIR_NAME = "frpclienttmp"; - private static final String FRPC_CLIENT_FILE_NAME = "frpclienttmpfile.exe"; + public static final String FRPC_CLIENT_FILE_NAME = "frpclienttmpfile.exe"; private final String FRPC_CONF_PREFIX_NAME = "proxy_"; private final String FRPC_CONF_SUFFIX_NAME = ".ini"; + /** + * 应用临时目录 地址 + */ private static final String YAN_FRP_TEMP_DIR_PATH = System.getProperty("java.io.tmpdir") + System.currentTimeMillis() + YAN_FRP_TEMP_DIR_NAME; - private static final String FRPC_CLIENT_FILE_PATH = YAN_FRP_TEMP_DIR_PATH + File.separator + FRPC_CLIENT_FILE_NAME; - private static final File frpcDir = new File(YAN_FRP_TEMP_DIR_PATH); + /** + * frpc 地址 + */ + private static final String FRPC_CLIENT_FILE_PATH = ApplicatonStore.appDataDirPath + File.separator + FRPC_CLIENT_FILE_NAME; + private static final File frpconfigDir = new File(YAN_FRP_TEMP_DIR_PATH); public static final File frpc = new File(FRPC_CLIENT_FILE_PATH); public File frpcConfigFile; private final ProxySetupModel model; private final TextArea console; - private Thread thread; + private ProxyThread thread; private Process exec; - private FrpUtil(TextArea console, ProxySetupModel model) { + private FrpManager(TextArea console, ProxySetupModel model) { this.console = console; this.model = model; } - public static FrpUtil init(ProxySetupModel model) { + public static FrpManager init(ProxySetupModel model) { TextArea console = new TextArea(); console.setPrefHeight(180); console.setMinWidth(200); - return new FrpUtil(console, model); - } - - /** - * 初始化frpc客户端文件 - */ - public static void initFrpc() throws IOException { - - // 存在就删除 - if (frpcDir.exists()) FrpUtil.clearTmp(); - - // 重新创建 - frpcDir.mkdir(); - frpc.createNewFile(); - - // 发送删除请求 - frpcDir.deleteOnExit(); - frpc.deleteOnExit(); - - // 复制 frpc - String fprcFilePath = FrpUtil.class.getResource("/static/frpc.exe").getFile(); - System.out.println(fprcFilePath); - copyFileUsingFileStreams(FrpUtil.class.getResourceAsStream("/static/frpc.exe"), frpc); -// FileUtils.copyFile(new File(fprcFilePath), frpc); - } - - /** - * 清理缓存 - */ - public static void clearTmp() { - try { - FileUtils.deleteDirectory(frpcDir); - } catch (IOException e) { - e.printStackTrace(); - try { - Runtime.getRuntime().exec("taskkill /im " + FRPC_CLIENT_FILE_NAME + " /f"); - Thread.sleep(2000); - FileUtils.deleteDirectory(frpcDir); - } catch (InterruptedException | IOException ex) { - ex.printStackTrace(); - } - } + FrpManager frpManager = new FrpManager(console, model); + frpcList.add(frpManager); + return frpManager; } public TextArea getConsole() { @@ -96,43 +64,7 @@ public class FrpUtil { * 开始 */ public void start() { - - thread = new Thread(() -> { - - Platform.runLater(() -> console.appendText("yan-frp-info:正在启动\n")); - try { - // 检查客户端文件 - if (!frpc.exists()) initFrpc(); - - // 初始化配置文件 - initProxyConfigFile(); - - // 执行命令 - String command = FRPC_CLIENT_FILE_PATH + " -c " + getConfigFilePath(); - System.out.println(command); - exec = Runtime.getRuntime().exec(command, null, frpcDir); - - // 设置运行状态 - Platform.runLater(() -> model.setRunning(true)); - - // 获取标准输出 - BufferedReader readStdout = new BufferedReader(new InputStreamReader(exec.getInputStream())); - - // 逐行读取 - String line; - while ((line = readStdout.readLine()) != null) { - String finalLine = line; - Platform.runLater(() -> console.appendText("yan-frp-info:" + finalLine + "\n")); - } - - } catch (IOException e) { - e.printStackTrace(); - // - Platform.runLater(() -> console.appendText("yan-frp-error:" + e.getMessage() + "\n")); - Platform.runLater(() -> console.appendText("yan-frp-error:启动失败\n")); - } - }); - + thread = new ProxyThread(); thread.start(); } @@ -145,7 +77,7 @@ public class FrpUtil { do { if (exec != null && exec.isAlive()) exec.destroy(); - + thread.setExit(true); thread.interrupt(); } while (!thread.isInterrupted()); @@ -182,18 +114,62 @@ public class FrpUtil { private void initProxyConfigFile() throws IOException { // 创建配置文件 frpcConfigFile = new File(getConfigFilePath()); - if (frpcConfigFile.exists()) { - frpcConfigFile.delete(); + if (!frpcConfigFile.exists()) { + FileUtils.createParentDirectories(frpcConfigFile); + frpcConfigFile.createNewFile(); } - frpcConfigFile.createNewFile(); frpcConfigFile.deleteOnExit(); // 写入服务配置 - FileUtils.write(frpcConfigFile, ProxyUtil.getUserServerConfig(model.get().getNode()), StandardCharsets.UTF_8); + FileUtils.write(frpcConfigFile, ProxyManager.getUserServerConfig(model.get().getNode()), StandardCharsets.UTF_8); // 写入隧道配置 - FileUtils.write(frpcConfigFile, ProxyUtil.getProxyConfig(model), StandardCharsets.UTF_8, true); + FileUtils.write(frpcConfigFile, ProxyManager.getProxyConfig(model), StandardCharsets.UTF_8, true); } + /** + * 初始化frpc客户端文件 + */ + public static void initFrpc() throws IOException { + // frpc 文件 + if (!frpc.exists()) { + frpc.createNewFile(); + + // 复制 frpc + copyFileUsingFileStreams(FrpManager.class.getResourceAsStream("/static/frpc.exe"), frpc); + } + + // frpc 临时配置文件 + // 存在就删除 + if (frpconfigDir.exists()) FrpManager.clearTmp(); + // 重新创建 + frpconfigDir.mkdir(); + // 发送删除请求 + frpconfigDir.deleteOnExit(); + } + + /** + * 清理缓存 + */ + public static void clearTmp() { + try { + FileUtils.deleteDirectory(frpconfigDir); + + for (File file : FileUtils.getTempDirectory().listFiles()) { + if (file.getPath().contains(YAN_FRP_TEMP_DIR_NAME) && file.canWrite() && file.isDirectory()) { + FileUtils.deleteDirectory(file); + } + } + } catch (IOException e) { + e.printStackTrace(); + try { + Runtime.getRuntime().exec("taskkill /im " + FRPC_CLIENT_FILE_NAME + " /f"); + Thread.sleep(1000); + FileUtils.deleteDirectory(frpconfigDir); + } catch (InterruptedException | IOException ex) { + ex.printStackTrace(); + } + } + } private static void copyFileUsingFileStreams(InputStream input, File dest) throws IOException { @@ -210,4 +186,46 @@ public class FrpUtil { output.close(); } } + + @Data + class ProxyThread extends Thread{ + private volatile boolean exit = false; + + @Override + public void run() { + Platform.runLater(() -> console.appendText("yan-frp-info:正在启动\n")); + try { + // 检查客户端文件 + if (!frpc.exists()) initFrpc(); + + // 初始化配置文件 + initProxyConfigFile(); + + // 执行命令 + String command = FRPC_CLIENT_FILE_PATH + " -c " + getConfigFilePath(); + System.out.println(command); + exec = Runtime.getRuntime().exec(command, null, frpconfigDir); + + // 设置运行状态 + Platform.runLater(() -> model.setRunning(true)); + + // 获取标准输出 + BufferedReader readStdout = new BufferedReader(new InputStreamReader(exec.getInputStream())); + + // 逐行读取 + String line; + while ((line = readStdout.readLine()) != null && !exit) { + String finalLine = line; + System.out.println("yan-frp-info:" + line); + Platform.runLater(() -> console.appendText("yan-frp-info:" + finalLine + "\n")); + } + + } catch (IOException e) { + e.printStackTrace(); + // + Platform.runLater(() -> console.appendText("yan-frp-error:" + e.getMessage() + "\n")); + Platform.runLater(() -> console.appendText("yan-frp-error:启动失败\n")); + } + } + } } diff --git a/src/main/java/top/octopusyan/utils/ProxyUtil.java b/src/main/java/top/octopusyan/manager/ProxyManager.java similarity index 85% rename from src/main/java/top/octopusyan/utils/ProxyUtil.java rename to src/main/java/top/octopusyan/manager/ProxyManager.java index 433a95f..8b2dcd9 100644 --- a/src/main/java/top/octopusyan/utils/ProxyUtil.java +++ b/src/main/java/top/octopusyan/manager/ProxyManager.java @@ -1,19 +1,22 @@ -package top.octopusyan.utils; +package top.octopusyan.manager; 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.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.manager.http.EasyHttp; +import top.octopusyan.manager.http.listener.OnHttpListener; +import top.octopusyan.manager.http.model.ResponseClass; +import top.octopusyan.model.ApplicatonStore; import top.octopusyan.model.ProxySetupModel; +import top.octopusyan.utils.AlertUtil; +import top.octopusyan.utils.JsoupUtil; import java.util.HashMap; import java.util.List; @@ -27,7 +30,7 @@ import java.util.stream.Collectors; * @description : 代理设置工具 * @create : 2022-4-5 09:56 */ -public class ProxyUtil { +public class ProxyManager { private static String csrf; private static Map serverConfigraution = new HashMap<>(3); @@ -173,8 +176,9 @@ public class ProxyUtil { if (result != null && result.size() > 0) { for (ProxySetup proxySetup : result) { - if (Objects.equals(proxySetup.getSort(), proxySetup.getSort())) { + if (Objects.equals(proxySetup.getSort(), setup.getSort())) { listener.onSucceed(proxySetup); + return; } } } @@ -201,7 +205,7 @@ public class ProxyUtil { public static void setCsrf(String htmlStr) { int i = htmlStr.indexOf("var csrf_token = \""); int i1 = htmlStr.indexOf("\"", i + 18); - ProxyUtil.csrf = htmlStr.substring(i + 18, i1); + ProxyManager.csrf = htmlStr.substring(i + 18, i1); } public static String getUserServerConfig(int node) { @@ -209,7 +213,7 @@ public class ProxyUtil { if (StringUtils.isNotEmpty(config)) return config; try { String result = EasyHttp.builder() - .api(Api.ServerConfiguration) + .api(Api.getServerConfiguration(node)) .pathParam(String.valueOf(node)) .execute(new ResponseClass() { }); @@ -225,31 +229,31 @@ public class ProxyUtil { 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) + StringBuilder stringBuilder = new StringBuilder("[" + ApplicatonStore.getAccount() + "_" + setup.getProxyName() + "]\n"); + stringBuilder.append("privilege_mode = true\n") + .append("type = ").append(setup.getProxyType().contains("http") ? "http" : 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); + stringBuilder.append("custom_domains = ").append(setup.getDomain()).append(setup.getDomainSuffix()).append(n); if (!StringUtils.isEmpty(setup.getLocations())) - stringBuffer.append("locations = ").append(setup.getLocations()).append(n); + stringBuilder.append("locations = ").append(setup.getLocations()).append(n); if (!StringUtils.isEmpty(setup.getLocations())) - stringBuffer.append("host_header_rewrite = ").append(setup.getHostHeaderRewrite()).append(n); + stringBuilder.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); + stringBuilder.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); + stringBuilder.append("remote_port = ").append(setup.getRemotePort()).append(n); if (!StringUtils.isEmpty(setup.getSk())) - stringBuffer.append("sk = ").append(setup.getSk()).append(n); + stringBuilder.append("sk = ").append(setup.getSk()).append(n); } // 压缩和加密 - stringBuffer.append("use_encryption = ").append(setup.isUseEncryption()).append(n) + stringBuilder.append("use_encryption = ").append(setup.isUseEncryption()).append(n) .append("use_compression = ").append(setup.isUseCompression()).append(n).append(n); - return stringBuffer.toString(); + return stringBuilder.toString(); } } diff --git a/src/main/java/top/octopusyan/manager/http/config/ILogStrategy.java b/src/main/java/top/octopusyan/manager/http/config/ILogStrategy.java index 49d7da1..2a46f4c 100644 --- a/src/main/java/top/octopusyan/manager/http/config/ILogStrategy.java +++ b/src/main/java/top/octopusyan/manager/http/config/ILogStrategy.java @@ -100,7 +100,7 @@ public interface ILogStrategy { * 创建对应数量的制表符 */ static String getSpaceOrTab(int tabNum) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < tabNum; i++) { sb.append('\t'); } diff --git a/src/main/java/top/octopusyan/model/ApplicatonStore.java b/src/main/java/top/octopusyan/model/ApplicatonStore.java new file mode 100644 index 0000000..f6a03bb --- /dev/null +++ b/src/main/java/top/octopusyan/model/ApplicatonStore.java @@ -0,0 +1,222 @@ +package top.octopusyan.model; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import org.apache.commons.io.FileUtils; +import top.octopusyan.http.request.ProxySetup; +import top.octopusyan.utils.EncryptionUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * @author : octopus yan + * @email : octopus_yan@foxmail.com + * @description : 应用信息 + * @create : 2022-4-4 17:22 + */ +public class ApplicatonStore { + public static final String appDataDirPath = System.getProperty("user.home") + File.separator + "AppData" + File.separator + "Local" + File.separator + "yanfrp"; + public static final String appDataFilePath = appDataDirPath + File.separator + "yanfrp"; + private static final File appDataDir = new File(appDataDirPath); + private static final File appDataFile = new File(appDataFilePath); + private static final String ACCOUNT_KEY = "YANFRP_ACCOUNT"; + private static final String PASSWORD_KEY = "YANFRP_PASSWORD"; + private static final String AUTO_LOGIN_KEY = "YANFRP_AUTO_LOGIN"; + private static final String REMEMBER_ME_KEY = "YANFRP_REMEMBER_ME"; + private static final String SELECT_PROXY_NAME_KEY = "YANFRP_SELECT_PROXY_NAME"; + private static final SimpleStringProperty account = new SimpleStringProperty(); + private static final SimpleStringProperty password = new SimpleStringProperty(); + private static final SimpleStringProperty email = new SimpleStringProperty(); + private static final SimpleBooleanProperty autoLogin = new SimpleBooleanProperty(); + private static final SimpleBooleanProperty rememberMe = new SimpleBooleanProperty(); + private static final SimpleBooleanProperty registerSuccess = new SimpleBooleanProperty(); + private static final ObservableList proxyList = FXCollections.observableList(new ArrayList<>()); + private static final SimpleIntegerProperty selectProxy = new SimpleIntegerProperty(0); + + public static void setAccount(String account) { + ApplicatonStore.account.set(account); + } + + public static void setEmail(String email) { + ApplicatonStore.email.set(email); + } + + public static void setPassword(String password) { + ApplicatonStore.password.set(password); + } + + public static String getAccount() { + return account.get(); + } + + public static SimpleStringProperty accountProperty() { + return account; + } + + public static String getEmail() { + return email.get(); + } + + public static String getPassword() { + return password.get(); + } + + public static SimpleStringProperty passwordProperty() { + return password; + } + + public static void setAutoLogin(boolean autoLogin) { + ApplicatonStore.autoLogin.set(autoLogin); + } + + public static void setRememberMe(boolean rememberMe) { + ApplicatonStore.rememberMe.set(rememberMe); + } + + public static boolean isAutoLogin() { + return autoLogin.get(); + } + + public static SimpleBooleanProperty autoLoginProperty() { + return autoLogin; + } + + public static boolean isRememberMe() { + return rememberMe.get(); + } + + public static SimpleBooleanProperty rememberMeProperty() { + return rememberMe; + } + + public static void setRegisterSuccess(boolean registerSuccess) { + ApplicatonStore.registerSuccess.set(registerSuccess); + } + + public static boolean isRegisterSuccess() { + return registerSuccess.get(); + } + + public static ObservableList proxyList() { + return proxyList; + } + + public static void setProxyList(List proxySetupList) { + proxyList.setAll(proxySetupList); + } + + public static void addProxyList(ProxySetup proxySetup) { + proxyList.add(proxySetup); + } + + public static void selectProxy(int selectProxy) { + ApplicatonStore.selectProxy.set(selectProxy); + } + + public static int selectProxy() { + return selectProxy.get(); + } + + /** + * 初始化应用数据 + */ + public static void init() { + // 检查应用目录 + checkAppData(); + // 注册状态 + setRegisterSuccess(false); + // 应用数据 + JSONObject appDataJson = new JSONObject(); + try { + String str = FileUtils.readFileToString(appDataFile, StandardCharsets.UTF_8); + appDataJson = JSON.parseObject(EncryptionUtil.decodeAppData(str)); + } catch (IOException e) { + e.printStackTrace(); + } + + if (appDataJson == null) return; + + // 账号 + if (appDataJson.containsKey(ACCOUNT_KEY)) account.set(appDataJson.getString(ACCOUNT_KEY)); + // 密码 + if (appDataJson.containsKey(PASSWORD_KEY)) password.set(appDataJson.getString(PASSWORD_KEY)); + // 自动登录 + if (appDataJson.containsKey(AUTO_LOGIN_KEY)) autoLogin.set(appDataJson.getBoolean(AUTO_LOGIN_KEY)); + // 记住 + if (appDataJson.containsKey(REMEMBER_ME_KEY)) rememberMe.set(appDataJson.getBoolean(REMEMBER_ME_KEY)); + // TODO 已选择隧道 + if (appDataJson.containsKey(SELECT_PROXY_NAME_KEY)) { +// String proxtName = appDataJson.getString(SELECT_PROXY_NAME_KEY); +// proxyListProperty().addListener(new ); + } + } + + /** + * 序列化应用信息到本地 + */ + public static void save() { + // 检查应用目录 + checkAppData(); + // 保存数据 + JSONObject appDataJson = new JSONObject(); + // 账号 + if (rememberMe.get() && account.get() != null) appDataJson.put(ACCOUNT_KEY, account.get()); + // 密码 + if (rememberMe.get() && password.get() != null) appDataJson.put(PASSWORD_KEY, password.get()); + // 选择隧道 + if (selectProxy.getValue() != null && proxyList.size() > 0 && proxyList.size() > selectProxy.get()) + appDataJson.put(SELECT_PROXY_NAME_KEY, proxyList.get(selectProxy.getValue())); + // 自动登录 + appDataJson.put(AUTO_LOGIN_KEY, autoLogin.get()); + // 记住 + appDataJson.put(REMEMBER_ME_KEY, rememberMe.get()); + + try { + String appDataStr = EncryptionUtil.encodeAppData(appDataJson.toJSONString()); + FileUtils.write(appDataFile, appDataStr, StandardCharsets.UTF_8); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void rememberMe(String password) { + // 密码 + if (isRememberMe()) { + setPassword(password); + } + } + + public static void logout(){ + setRegisterSuccess(false); + setAutoLogin(false); + setRememberMe(false); + setPassword(""); + } + + /** + * 检查应用数据目录 + */ + private static void checkAppData() { + + // 应用目录 + if (!appDataDir.exists()) appDataDir.mkdirs(); + + // 应用数据文件 + if (!appDataFile.exists()) { + try { + appDataFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/top/octopusyan/model/ProxySetupModel.java b/src/main/java/top/octopusyan/model/ProxySetupModel.java index d4e692b..4329579 100644 --- a/src/main/java/top/octopusyan/model/ProxySetupModel.java +++ b/src/main/java/top/octopusyan/model/ProxySetupModel.java @@ -15,7 +15,7 @@ import top.octopusyan.utils.DomainUtil; * @create : 2022-4-6 16:45 */ public class ProxySetupModel { - private final SimpleIntegerProperty id = new SimpleIntegerProperty(); + private final SimpleStringProperty id = new SimpleStringProperty(); private final SimpleStringProperty server = new SimpleStringProperty(); private final SimpleStringProperty proxyName = new SimpleStringProperty(); private final SimpleStringProperty proxyType = new SimpleStringProperty(); @@ -36,6 +36,8 @@ public class ProxySetupModel { private final SimpleBooleanProperty isCustomize = new SimpleBooleanProperty(); public ProxySetupModel(ProxySetup setup) { + if(setup == null) return; + String domainStr = setup.getDomain(); String suffix = ""; isCustomize.set(DomainUtil.isCustomize(setup)); @@ -57,13 +59,13 @@ public class ProxySetupModel { setUseCompression(setup.getUse_compression()); setHeader_X_From_Where(setup.getHost_header_rewrite()); setHostHeaderRewrite(setup.getHost_header_rewrite()); - setStatus(setup.getStatus()); + setStatus(setup.getStatus() == null || setup.getStatus()); setSk(setup.getSk()); setSort(setup.getSort()); setRunning(setup.isRuning()); } - public Integer getId() { + public String getId() { return id.get(); } @@ -136,7 +138,7 @@ public class ProxySetupModel { } public void setId(Integer id) { - if(id != null) this.id.set(id); + this.id.set(id == null ? null : id.toString()); } public void setProxyName(String proxyName) { @@ -196,7 +198,7 @@ public class ProxySetupModel { } public void setSort(Integer sort) { - if(sort != null) this.sort.set(sort); + if (sort != null) this.sort.set(sort); } public void setStatus(boolean status) { @@ -235,7 +237,7 @@ public class ProxySetupModel { return useEncryption; } - public SimpleIntegerProperty idProperty() { + public SimpleStringProperty idProperty() { return id; } @@ -292,7 +294,6 @@ public class ProxySetupModel { suffix = DomainUtil.getSuffix(setup); } setId(setup.getId()); - setServer(ProxyConfig.getServerName(setup.getNode())); setProxyName(setup.getProxy_name()); setProxyType(setup.getProxy_type()); setLocalIp(setup.getLocal_ip()); @@ -305,15 +306,17 @@ public class ProxySetupModel { setUseCompression(setup.getUse_compression()); setHeader_X_From_Where(setup.getHost_header_rewrite()); setHostHeaderRewrite(setup.getHost_header_rewrite()); - setStatus(setup.getStatus()); + setStatus(setup.getStatus() == null || setup.getStatus()); setSk(setup.getSk()); setSort(setup.getSort()); setRunning(setup.isRuning()); + // 数据绑定原因 放在最后 + setServer(ProxyConfig.getServerName(setup.getNode())); } public ProxySetup get() { return new ProxySetup( - getId(), + getId() == null ? null : Integer.parseInt(getId()), ProxyConfig.getServerNode(server.get()), getProxyName(), StringUtils.lowerCase(proxyType.getValue()), @@ -327,8 +330,8 @@ public class ProxySetupModel { getHostHeaderRewrite(), getHeader_X_From_Where(), getSk(), - getStatus(), getSort(), + getStatus(), isRunning() ); } diff --git a/src/main/java/top/octopusyan/utils/AlertUtil.java b/src/main/java/top/octopusyan/utils/AlertUtil.java index b0943aa..2571653 100644 --- a/src/main/java/top/octopusyan/utils/AlertUtil.java +++ b/src/main/java/top/octopusyan/utils/AlertUtil.java @@ -65,23 +65,32 @@ public class AlertUtil { alert.showAndWait(); } - private void show(OnClickListener listener) { + /** + * AlertUtil.confirm + * @param listener + */ + public void show(OnClickListener listener) { Optional result = alert.showAndWait(); listener.onClicked(result.get().getText()); } - private void show(OnChoseListener listener) { + /** + * AlertUtil.confirm + * @param listener + */ + public void show(OnChoseListener listener) { Optional result = alert.showAndWait(); if (result.get() == ButtonType.OK) { listener.confirm(); } else { - listener.cancelOrClose(); + listener.cancelOrClose(result.get()); } } /** + * AlertUtil.input * 如果用户点击了取消按钮,将会返回null */ public String getInput() { @@ -92,6 +101,12 @@ public class AlertUtil { return null; } + /** + * AlertUtil.choices + * @param choices + * @param + * @return + */ public R getChoice(R... choices) { Optional result = alert.showAndWait(); return result.get(); @@ -155,6 +170,9 @@ public class AlertUtil { return alert; } + /** + * 确认对话框 + */ public static Builder confirm() { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("确认对话框"); @@ -196,7 +214,7 @@ public class AlertUtil { interface OnChoseListener { void confirm(); - void cancelOrClose(); + void cancelOrClose(ButtonType buttonType); } interface OnClickListener { diff --git a/src/main/java/top/octopusyan/utils/ApplicatonStore.java b/src/main/java/top/octopusyan/utils/ApplicatonStore.java deleted file mode 100644 index 6cc6cc3..0000000 --- a/src/main/java/top/octopusyan/utils/ApplicatonStore.java +++ /dev/null @@ -1,101 +0,0 @@ -package top.octopusyan.utils; - -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleListProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import top.octopusyan.http.request.ProxySetup; - -import java.util.List; - -/** - * @author : octopus yan - * @email : octopus_yan@foxmail.com - * @description : 应用信息 - * @create : 2022-4-4 17:22 - */ -public class ApplicatonStore { - private static SimpleStringProperty account = new SimpleStringProperty(); - private static SimpleStringProperty password = new SimpleStringProperty(); - private static SimpleStringProperty email = new SimpleStringProperty(); - private static SimpleStringProperty sendMailCode = new SimpleStringProperty(); - private static SimpleBooleanProperty autoLogin = new SimpleBooleanProperty(); - private static SimpleBooleanProperty rememberMe = new SimpleBooleanProperty(); - private static SimpleBooleanProperty registerSuccess = new SimpleBooleanProperty(); - private static SimpleListProperty proxyList = new SimpleListProperty<>(); - private static SimpleIntegerProperty selectProxy = new SimpleIntegerProperty(0); - - public static void setAccount(String account) { - ApplicatonStore.account.set(account); - } - - public static void setEmail(String email) { - ApplicatonStore.email.set(email); - } - - public static void setPassword(String password) { - ApplicatonStore.password.set(password); - } - - public static void setSendMailCode(String sendMailCode) { - ApplicatonStore.sendMailCode.set(sendMailCode); - } - - public static String getAccount() { - return account.get(); - } - - public static String getEmail() { - return email.get(); - } - - public static String getPassword() { - return password.get(); - } - - public static String getSendMailCode() { - return sendMailCode.get(); - } - - public static void setAutoLogin(boolean autoLogin) { - ApplicatonStore.autoLogin.set(autoLogin); - } - - public static void setRememberMe(boolean rememberMe) { - ApplicatonStore.rememberMe.set(rememberMe); - } - - public static boolean isAutoLogin() { - return autoLogin.get(); - } - - public static boolean isRememberMe() { - return rememberMe.get(); - } - - public static void setRegisterSuccess(boolean registerSuccess) { - ApplicatonStore.registerSuccess.set(registerSuccess); - } - - public static boolean isRegisterSuccess() { - return registerSuccess.get(); - } - - public static ObservableList getProxyList() { - return proxyList.get(); - } - - public static void initProxyList(List proxySetupList) { - ApplicatonStore.proxyList = new SimpleListProperty<>(FXCollections.observableList(proxySetupList)); - } - - public static void setSelectProxy(int selectProxy) { - ApplicatonStore.selectProxy.set(selectProxy); - } - - public static int getSelectProxy() { - return selectProxy.get(); - } -} diff --git a/src/main/java/top/octopusyan/utils/DomainUtil.java b/src/main/java/top/octopusyan/utils/DomainUtil.java index a92e9ba..48dc24a 100644 --- a/src/main/java/top/octopusyan/utils/DomainUtil.java +++ b/src/main/java/top/octopusyan/utils/DomainUtil.java @@ -1,13 +1,11 @@ package top.octopusyan.utils; -import cn.hutool.core.util.ObjectUtil; import top.octopusyan.config.ProxyConfig; import top.octopusyan.http.request.ProxySetup; import top.octopusyan.model.ProxySetupModel; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.Objects; /** * @author : octopus yan @@ -34,10 +32,6 @@ public class DomainUtil { return ProxyConfig.getTypeIndex(model.getProxyType()) < 2; } - public static boolean isHttps(ProxySetupModel model){ - return ProxyConfig.getTypeIndex(model.getProxyType()) == 1; - } - /** * 是否自定义隧道 */ diff --git a/src/main/java/top/octopusyan/utils/EncryptionUtil.java b/src/main/java/top/octopusyan/utils/EncryptionUtil.java index 26e5841..e62477a 100644 --- a/src/main/java/top/octopusyan/utils/EncryptionUtil.java +++ b/src/main/java/top/octopusyan/utils/EncryptionUtil.java @@ -2,12 +2,11 @@ package top.octopusyan.utils; import cn.hutool.core.codec.Base64; import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.digest.MD5; -import sun.misc.BASE64Encoder; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sun.misc.BASE64Encoder; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; @@ -30,7 +29,7 @@ import java.util.Arrays; * @create : 2021-03-18 19:03 */ public class EncryptionUtil { - private static EncryptionUtil util; + private static final String defaultPassword = "yanfrp"; private static Logger logger = LoggerFactory.getLogger(EncryptionUtil.class); private static BASE64Encoder BASE64Encoder; @@ -51,7 +50,8 @@ public class EncryptionUtil { * 加解密算法/工作模式/填充方式 */ private static final String ECB_MOB = "DES/ECB/PKCS5Padding"; - private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding"; +// private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding"; + private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding"; private static final Charset UTF_8 = StandardCharsets.UTF_8; @@ -60,20 +60,41 @@ public class EncryptionUtil { private static final char hexDigitsLower[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - public static synchronized EncryptionUtil getInstance() { - if (util == null) { - BASE64Encoder = new BASE64Encoder(); - try { - MessageDigest_SHA1 = MessageDigest.getInstance("SHA-1"); - MessageDigest_MD5 = MessageDigest.getInstance("MD5"); - Cipher_ECB_MOB = Cipher.getInstance(ECB_MOB); - Cipher_ALGORITHM_MODE_PADDING = Cipher.getInstance(ALGORITHM_MODE_PADDING); - Signature_SHA256_RSA = Signature.getInstance("SHA256WithRSA"); - } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - e.printStackTrace(); - } + public static void init() { + + BASE64Encoder = new BASE64Encoder(); + try { + MessageDigest_SHA1 = MessageDigest.getInstance("SHA-1"); + MessageDigest_MD5 = MessageDigest.getInstance("MD5"); + Cipher_ECB_MOB = Cipher.getInstance(ECB_MOB); + Cipher_ALGORITHM_MODE_PADDING = Cipher.getInstance(ALGORITHM_MODE_PADDING); + Signature_SHA256_RSA = Signature.getInstance("SHA256WithRSA"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + e.printStackTrace(); + } + + } + + public static String getDefaultPassword() { + return getSHA(defaultPassword + getMessageDigestString("frp." + defaultPassword, hexDigits, MessageDigest_SHA1)); + } + + public static String decodeAppData(String str){ + try { + return DESdecode(str, getDefaultPassword()); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static String encodeAppData(String str){ + try { + return DESencode(str, getDefaultPassword()); + } catch (Exception e) { + e.printStackTrace(); + return null; } - return util; } public static String getSha1(String str) { diff --git a/src/main/java/top/octopusyan/utils/JsoupUtil.java b/src/main/java/top/octopusyan/utils/JsoupUtil.java index 5436df9..c438ea9 100644 --- a/src/main/java/top/octopusyan/utils/JsoupUtil.java +++ b/src/main/java/top/octopusyan/utils/JsoupUtil.java @@ -23,7 +23,7 @@ public class JsoupUtil { return getDocument(htmlStr).body().getElementsByClass("alert alert-danger alert-dismissable").text().substring(1); } - public static String getHtmlMessage(String htmlStr) { + public static String getSuccessMessage(String htmlStr) { return getDocument(htmlStr).getElementsByClass("alert alert-success alert-dismissable").text().substring(1); } diff --git a/src/main/resources/css/main.css b/src/main/resources/css/main.css index b69945e..80c9d1f 100644 --- a/src/main/resources/css/main.css +++ b/src/main/resources/css/main.css @@ -43,7 +43,7 @@ -fx-text-fill: #757575; } -/* TODO 隧道列表 */ +/* 隧道列表 */ #proxyListView .list-cell:selected { -fx-background-color: linear-gradient(#9198e5, #57b4f2); } @@ -93,7 +93,7 @@ -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-background-color: linear-gradient(rgb(87, 180, 242), rgb(145, 152, 229)); -fx-font-family: "Microsoft YaHei"; } /* 启动 */ diff --git a/src/main/resources/fxml/login.fxml b/src/main/resources/fxml/login.fxml index 3552c2b..55effce 100644 --- a/src/main/resources/fxml/login.fxml +++ b/src/main/resources/fxml/login.fxml @@ -95,8 +95,8 @@ - + diff --git a/src/main/resources/fxml/main.fxml b/src/main/resources/fxml/main.fxml index 2fa04ef..f94f321 100644 --- a/src/main/resources/fxml/main.fxml +++ b/src/main/resources/fxml/main.fxml @@ -5,7 +5,6 @@ - @@ -166,21 +165,24 @@ - - + + + + + + layoutX="165.0" prefHeight="30.0" prefWidth="155.0" + styleClass="inputText-right" text=".frp.octopusyan.top"> - + @@ -227,15 +229,13 @@ - - - + - + - - + + + - - + + - + + + + + + + + +