应用数据本地加密存储

添加记住密码、自动登录、找回密码
修复点击隧道列表时隧道设置显示bug
This commit is contained in:
octopusYan 2022-04-10 23:57:07 +08:00
parent 269c1c1299
commit 2c327bbd69
25 changed files with 804 additions and 462 deletions

View File

@ -6,7 +6,7 @@
<groupId>top.octopusyan</groupId> <groupId>top.octopusyan</groupId>
<artifactId>YanFrp</artifactId> <artifactId>YanFrp</artifactId>
<version>1.0-SNAPSHOT</version> <version>0.1.2-SNAPSHOT</version>
<name>YanFrp</name> <name>YanFrp</name>
<properties> <properties>

View File

@ -3,7 +3,6 @@ package top.octopusyan;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
@ -12,13 +11,14 @@ import javafx.stage.StageStyle;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import top.octopusyan.base.BaseController; 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.HttpConfig;
import top.octopusyan.manager.http.config.LogStrategy; import top.octopusyan.manager.http.config.LogStrategy;
import top.octopusyan.manager.http.request.RequestHandler; 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.AlertUtil;
import top.octopusyan.utils.ApplicatonStore; import top.octopusyan.utils.EncryptionUtil;
import top.octopusyan.utils.FrpUtil;
import top.octopusyan.utils.FxmlUtil; import top.octopusyan.utils.FxmlUtil;
/** /**
@ -35,8 +35,14 @@ public class YanFrpApplication extends Application {
super.init(); super.init();
logger.info("init..."); logger.info("init...");
// 初始化加密工具
EncryptionUtil.init();
// 初始化应用数据
ApplicatonStore.init();
// 初始化frp客户端临时文件 // 初始化frp客户端临时文件
FrpUtil.initFrpc(); FrpManager.initFrpc();
// 网络请求设置 // 网络请求设置
HttpConfig.with(OkHttpClientConfig.httpClient()) HttpConfig.with(OkHttpClientConfig.httpClient())
@ -64,8 +70,6 @@ public class YanFrpApplication extends Application {
// 初始化弹窗 // 初始化弹窗
AlertUtil.initOwner(stage); AlertUtil.initOwner(stage);
// 初始化应用数据
ApplicatonStore.setRegisterSuccess(false);
try { try {
// 静态写法无法获取controler // 静态写法无法获取controler
@ -102,7 +106,7 @@ public class YanFrpApplication extends Application {
@Override @Override
public void stop() throws Exception { public void stop() throws Exception {
super.stop(); super.stop();
FrpUtil.clearTmp(); FrpManager.clearTmp();
logger.info("stop..."); logger.info("stop...");
} }

View File

@ -1,24 +1,9 @@
package top.octopusyan; package top.octopusyan;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform; import top.octopusyan.manager.FrpManager;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene; import java.io.IOException;
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 * @author : octopus yan
@ -28,6 +13,12 @@ import top.octopusyan.utils.FxmlUtil;
*/ */
public class YanFrpLuncher { public class YanFrpLuncher {
public static void main(String[] args) { 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); Application.launch(YanFrpApplication.class, args);
} }
} }

View File

@ -12,6 +12,9 @@ import javafx.scene.layout.Pane;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull; 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.FxmlUtil;
import top.octopusyan.utils.Loading; import top.octopusyan.utils.Loading;
@ -184,11 +187,18 @@ public abstract class BaseController<P extends Pane> implements Initializable {
* 关闭窗口 * 关闭窗口
*/ */
public void onDestroy() { public void onDestroy() {
// 取消所有请求
EasyHttp.cancel();
// 停止所有代理隧道
FrpManager.frpcList.forEach(FrpManager::stop);
// 保存应用数据
ApplicatonStore.save();
Stage stage = (Stage) getRootPanel().getScene().getWindow(); Stage stage = (Stage) getRootPanel().getScene().getWindow();
stage.hide(); stage.hide();
stage.close(); stage.close();
try { try {
Thread.sleep(2000); Thread.sleep(1000);
Platform.exit(); Platform.exit();
System.exit(0); System.exit(0);
} catch (InterruptedException e) { } catch (InterruptedException e) {

View File

@ -31,6 +31,7 @@ public class ProxyConfig {
typePort.put("https", 80); typePort.put("https", 80);
typePort.put("ssh", 22); typePort.put("ssh", 22);
typePort.put("tcp", 0); typePort.put("tcp", 0);
typePort.put("udp", 0);
} }
@ -60,6 +61,7 @@ public class ProxyConfig {
HTTPS("https"), HTTPS("https"),
SSH("tcp"), SSH("tcp"),
TCP("tcp"), TCP("tcp"),
UDP("udp"),
; ;
private final String type; private final String type;

View File

@ -4,7 +4,6 @@ import com.jfoenix.validation.RegexValidator;
import com.jfoenix.validation.RequiredFieldValidator; import com.jfoenix.validation.RequiredFieldValidator;
import com.jfoenix.validation.StringLengthValidator; import com.jfoenix.validation.StringLengthValidator;
import com.jfoenix.validation.base.ValidatorBase; import com.jfoenix.validation.base.ValidatorBase;
import top.octopusyan.config.ProxyConfig;
import top.octopusyan.model.ProxySetupModel; import top.octopusyan.model.ProxySetupModel;
import top.octopusyan.utils.DomainUtil; import top.octopusyan.utils.DomainUtil;
@ -100,12 +99,19 @@ public class TextValidate {
*/ */
public static ValidatorBase domainFormatValidator(ProxySetupModel model) { public static ValidatorBase domainFormatValidator(ProxySetupModel model) {
return new ValidatorBase("域名格式错误") { return new ValidatorBase("域名格式错误,支持数字字母下划线") {
@Override @Override
protected void eval() { protected void eval() {
if (!DomainUtil.isCustomize(model.get())) 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); hasErrors.set(false);
else { }
} 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})+$") 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(); .matcher(model.getDomain() + model.getDomainSuffix()).matches();
hasErrors.set(!matches); hasErrors.set(!matches);
@ -119,7 +125,7 @@ public class TextValidate {
*/ */
public static ValidatorBase domainLengthValidator(ProxySetupModel model) { public static ValidatorBase domainLengthValidator(ProxySetupModel model) {
return new RegexValidator("请输入子域名长度不小于3个字符") { return new RegexValidator("子域名长度不小于3个字符") {
@Override @Override
protected void eval() { protected void eval() {
setRegexPattern("^[a-zA-Z0-9_-]{3,18}$"); setRegexPattern("^[a-zA-Z0-9_-]{3,18}$");

View File

@ -5,6 +5,7 @@ import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXPasswordField; import com.jfoenix.controls.JFXPasswordField;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@ -17,14 +18,18 @@ import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
import top.octopusyan.base.BaseController; 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.EasyHttp;
import top.octopusyan.manager.http.api.NotParamApi; import top.octopusyan.manager.http.api.NotParamApi;
import top.octopusyan.manager.http.config.HttpConstant; import top.octopusyan.manager.http.config.HttpConstant;
import top.octopusyan.manager.http.listener.OnHttpListener; import top.octopusyan.manager.http.listener.OnHttpListener;
import top.octopusyan.config.TextValidate; import top.octopusyan.model.ApplicatonStore;
import top.octopusyan.http.Api; import top.octopusyan.utils.AlertUtil;
import top.octopusyan.http.request.LoginParam; import top.octopusyan.utils.JsoupUtil;
import top.octopusyan.utils.*;
import java.io.IOException; import java.io.IOException;
@ -69,8 +74,10 @@ public class LoginController extends BaseController<StackPane> implements Initia
public JFXCheckBox autoLoginCBox; public JFXCheckBox autoLoginCBox;
@FXML @FXML
public JFXCheckBox rememberCBox; public JFXCheckBox rememberCBox;
@FXML
public JFXButton findpassBtn;
private boolean autoLogin; private SimpleStringProperty tmppwd = new SimpleStringProperty();
@Override @Override
public boolean dragWindow() { public boolean dragWindow() {
@ -105,19 +112,27 @@ public class LoginController extends BaseController<StackPane> implements Initia
@Override @Override
public void initData() { 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 AnchorPane pwdParent;
private AlertUtil.Builder findpassAlert;
@Override @Override
public void initViewStyle() { public void initViewStyle() {
@ -136,11 +151,6 @@ public class LoginController extends BaseController<StackPane> implements Initia
pwdParent = (AnchorPane) passwordTextField.getParent(); pwdParent = (AnchorPane) passwordTextField.getParent();
pwdParent.getChildren().remove(seePwdTextField); 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.AccoountRequired);
accountTextField.getValidators().add(TextValidate.AccoountValidator); accountTextField.getValidators().add(TextValidate.AccoountValidator);
@ -148,12 +158,9 @@ public class LoginController extends BaseController<StackPane> implements Initia
passwordTextField.getValidators().add(TextValidate.PasswordRequired); passwordTextField.getValidators().add(TextValidate.PasswordRequired);
seePwdTextField.getValidators().add(TextValidate.PasswordRequired); seePwdTextField.getValidators().add(TextValidate.PasswordRequired);
// 记住密码
rememberCBox.selectedProperty().addListener((observable, oldValue, newValue) -> ApplicatonStore.setRememberMe(newValue));
// 自动登录 // 自动登录
autoLoginCBox.selectedProperty().addListener((observable, oldValue, newValue) -> { autoLoginCBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
ApplicatonStore.setAutoLogin(newValue); if (newValue) ApplicatonStore.setRememberMe(true);
if (newValue && !ApplicatonStore.isRememberMe()) rememberCBox.selectedProperty().set(true);
}); });
} }
@ -162,6 +169,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
// 注册 // 注册
registerBtn.setOnMouseClicked(event -> { registerBtn.setOnMouseClicked(event -> {
ApplicatonStore.setPassword(tmppwd.get());
try { try {
jumpTo(new RegisterController()); jumpTo(new RegisterController());
} catch (IOException e) { } catch (IOException e) {
@ -175,7 +183,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
pwdParent.getChildren().remove(isHide ? passwordTextField : seePwdTextField); pwdParent.getChildren().remove(isHide ? passwordTextField : seePwdTextField);
pwdParent.getChildren().add(1, isHide ? seePwdTextField : passwordTextField); 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); seePwdIcon.setIconColor(isHide ? Color.BLUE : Color.BLACK);
}); });
@ -188,6 +196,14 @@ public class LoginController extends BaseController<StackPane> implements Initia
if (event.getCode() == KeyCode.ENTER) login(); if (event.getCode() == KeyCode.ENTER) login();
}); });
// 找回密码
findpassBtn.setOnMouseClicked(event -> {
findpassAlert = AlertUtil.input("请输入您的账号或邮箱")
.title("找回密码")
.header(null);
findpass(findpassAlert.getInput());
});
// 自动登录 // 自动登录
if ((ApplicatonStore.isAutoLogin() || ApplicatonStore.isRegisterSuccess()) && if ((ApplicatonStore.isAutoLogin() || ApplicatonStore.isRegisterSuccess()) &&
!StringUtils.isEmpty(ApplicatonStore.getAccount()) && !StringUtils.isEmpty(ApplicatonStore.getAccount()) &&
@ -197,6 +213,33 @@ public class LoginController extends BaseController<StackPane> implements Initia
} }
} }
/**
* 找回密码
*/
private void findpass(String input) {
if (StringUtils.isNotEmpty(input))
EasyHttp.builder()
.api(Api.findpass)
.param(new FindPassParam(input))
.request(new OnHttpListener<String>() {
@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() { private void login() {
// 获取文本校验结果 // 获取文本校验结果
@ -207,7 +250,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
if (pwdValidate && accountValidate) if (pwdValidate && accountValidate)
EasyHttp.builder() EasyHttp.builder()
.api(Api.Login) .api(Api.Login)
.param(new LoginParam(accountTextField.getText(), ApplicatonStore.getPassword())) .param(new LoginParam(accountTextField.getText(), tmppwd.get()))
.request(new OnHttpListener<String>() { .request(new OnHttpListener<String>() {
@Override @Override
public void onSucceed(String result) { public void onSucceed(String result) {
@ -216,8 +259,11 @@ public class LoginController extends BaseController<StackPane> implements Initia
Platform.runLater(() -> AlertUtil.error(JsoupUtil.getErrorMessage(result)).show()); Platform.runLater(() -> AlertUtil.error(JsoupUtil.getErrorMessage(result)).show());
return; return;
} }
// TODO 登录成功 // 登录成功
setCsrf(); setCsrf();
// 记住我
ApplicatonStore.rememberMe(tmppwd.get());
// 跳转
Platform.runLater(() -> { Platform.runLater(() -> {
try { try {
jumpTo(new MainController()); jumpTo(new MainController());
@ -243,7 +289,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
.request(new OnHttpListener<String>() { .request(new OnHttpListener<String>() {
@Override @Override
public void onSucceed(String result) { public void onSucceed(String result) {
ProxyUtil.setCsrf(result); ProxyManager.setCsrf(result);
} }
@Override @Override
@ -255,7 +301,6 @@ public class LoginController extends BaseController<StackPane> implements Initia
@Override @Override
public void onDestroy() { public void onDestroy() {
EasyHttp.cancel();
super.onDestroy(); super.onDestroy();
} }
} }

View File

@ -1,23 +1,17 @@
package top.octopusyan.controller; package top.octopusyan.controller;
import com.jfoenix.controls.*; import com.jfoenix.controls.*;
import com.jfoenix.validation.IntegerValidator;
import com.jfoenix.validation.RequiredFieldValidator;
import javafx.application.HostServices;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; 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.HBox;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.apache.commons.lang3.StringUtils; 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;
import top.octopusyan.config.ProxyConfig.ProxyServer; import top.octopusyan.config.ProxyConfig.ProxyServer;
import top.octopusyan.config.ProxyConfig.ProxyType; import top.octopusyan.config.ProxyConfig.ProxyType;
import top.octopusyan.config.TextValidate;
import top.octopusyan.http.request.ProxySetup; 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.manager.http.listener.OnHttpListener;
import top.octopusyan.model.ApplicatonStore;
import top.octopusyan.model.ProxySetupModel; import top.octopusyan.model.ProxySetupModel;
import top.octopusyan.utils.AlertUtil; import top.octopusyan.utils.AlertUtil;
import top.octopusyan.utils.DomainUtil; 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.*;
import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.io.IOException; import java.io.IOException;
import java.util.*;
import java.util.List; import java.util.List;
import java.util.*;
import static top.octopusyan.model.ApplicatonStore.*;
/** /**
* @author : octopus yan * @author : octopus yan
@ -55,7 +52,6 @@ public class MainController extends BaseController<StackPane> implements Initial
public static final String PROXY_LIST_ITEM_RUN_CLASS = "proxyListItem-run"; 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_CLOSE_CLASS = "proxyListItem-close";
public static final String PROXY_LIST_ITEM_SELECT_CLASS = "proxyListItem-select"; 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"; public static final String INPUT_CLASS = "inputText";
@FXML @FXML
@ -94,15 +90,15 @@ public class MainController extends BaseController<StackPane> implements Initial
public Hyperlink domainLink; public Hyperlink domainLink;
@FXML @FXML
public HBox proxyStatusPane; public HBox proxyStatusPane;
@FXML
public JFXButton logoutBtn;
private ToggleGroup openProxyGroup = new ToggleGroup(); private ToggleGroup openProxyGroup = new ToggleGroup();
private SimpleBooleanProperty customizeDomain = new SimpleBooleanProperty(false); private SimpleBooleanProperty customizeDomain = new SimpleBooleanProperty(false);
private List<ProxySetup> proxyList = new ArrayList<>(); private Map<String, FrpManager> frpUtilMap = new HashMap<>();
private Map<String, FrpUtil> frpUtilMap = new HashMap<>();
private SimpleIntegerProperty selectProxy = new SimpleIntegerProperty(0);
private ProxySetupModel proxySetupModel; private ProxySetupModel proxySetupModel;
private ProxySetup proxySetup; private ProxySetup proxySetup;
private SimpleBooleanProperty setting = new SimpleBooleanProperty(false); private SimpleBooleanProperty setup = new SimpleBooleanProperty(false);
@Override @Override
public boolean dragWindow() { public boolean dragWindow() {
@ -138,8 +134,12 @@ public class MainController extends BaseController<StackPane> implements Initial
@Override @Override
public void initData() { public void initData() {
// 初始化视图模型 // 初始化视图模型
proxySetup = ProxyUtil.initProxy(null); proxySetup = ProxyManager.initProxy(null);
proxySetupModel = new ProxySetupModel(proxySetup); proxySetupModel = new ProxySetupModel(proxySetup);
// 初始化用户隧道列表
setProxyList(Collections.singletonList(proxySetup));
// 重置隧道列表视图
initProxyListView();
// 隧道类型 // 隧道类型
Arrays.asList(ProxyType.values()).forEach((type) -> proxyProtocolComboBox.getItems().add(type.name())); Arrays.asList(ProxyType.values()).forEach((type) -> proxyProtocolComboBox.getItems().add(type.name()));
@ -148,24 +148,26 @@ public class MainController extends BaseController<StackPane> implements Initial
Arrays.asList(ProxyServer.values()).forEach((server) -> proxyServerComboBox.getItems().add(server.getServerName())); Arrays.asList(ProxyServer.values()).forEach((server) -> proxyServerComboBox.getItems().add(server.getServerName()));
// 获取用户隧道列表 // 获取用户隧道列表
ProxyUtil.getList(new OnHttpListener<List<ProxySetup>>() { ProxyManager.getList(new OnHttpListener<List<ProxySetup>>() {
@Override @Override
public void onSucceed(List<ProxySetup> result) { public void onSucceed(List<ProxySetup> result) {
if (result != null) Platform.runLater(() -> {
proxyList = result;
// 如果用户隧道列表不为空 // 如果用户隧道列表不为空
if (proxyList != null && proxyList.size() > 0) { if (result != null && result.size() > 0) {
// 显示隧道列表 proxySetup = result.get(selectProxy());
initProxyList(proxyList); proxySetupModel.set(result.get(selectProxy()));
// 默认选中第一个(配置隧道设置模型) // 初始化用户隧道列表
proxyListView.getSelectionModel().select(0); setProxyList(result);
} else { // 重置隧道列表视图
// 配置隧道设置模型 initProxyListView();
proxyList = new ArrayList<>();
proxyList.add(proxySetup);
} }
// else {
// }
//
// bindDataView();
});
} }
@Override @Override
@ -199,6 +201,8 @@ public class MainController extends BaseController<StackPane> implements Initial
@Override @Override
public void initViewStyle() { public void initViewStyle() {
// 设置列表
proxyListView.getSelectionModel().select(0);
// 启用链接 // 启用链接
openProxyRBtn.setToggleGroup(openProxyGroup); openProxyRBtn.setToggleGroup(openProxyGroup);
closeProxyRBtn.setToggleGroup(openProxyGroup); closeProxyRBtn.setToggleGroup(openProxyGroup);
@ -232,7 +236,8 @@ public class MainController extends BaseController<StackPane> implements Initial
// 隧道名称 // 隧道名称
proxySetupModel.proxyNameProperty().addListener((observable, oldValue, newValue) -> { 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<StackPane> implements Initial
startProxyBtn.getStyleClass().add(newValue ? stopClass : startClass); startProxyBtn.getStyleClass().add(newValue ? stopClass : startClass);
startProxyBtn.setText(newValue ? "停止" : "启动"); startProxyBtn.setText(newValue ? "停止" : "启动");
// 列表显示 // 列表显示
ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy.get()).getStyleClass(); ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass();
styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS); styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS);
styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS); styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS);
styleClass.add(newValue ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS); styleClass.add(newValue ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
// 外网访问连接 setDomainLink();
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());
}
}); });
// 自定义外网访问地址按钮 // 自定义外网访问地址按钮
customizeDomain.addListener((observable, oldValue, newValue) -> { customizeDomain.addListener((observable, oldValue, newValue) -> {
HBox parent = (HBox) domainTextField.getParent(); AnchorPane parent = (AnchorPane) domainTextField.getParent();
ObservableList<String> styleClass = domainTextField.getStyleClass();
styleClass.remove(INPUT_LEFT_CLASS);
styleClass.remove(INPUT_CLASS);
// 是否切换为自定义域名 // 是否切换为自定义域名
if (newValue) { if (newValue) {
// 隐藏系统域名 // 隐藏系统域名
if (parent.getChildren().contains(domainSuffixTextField)) if (parent.getChildren().contains(domainSuffixTextField))
parent.getChildren().remove(domainSuffixTextField); parent.getChildren().remove(domainSuffixTextField);
// 用户域名是否自定义 // 用户域名是否自定义
if (!DomainUtil.isCustomize(proxySetup)) { if (!DomainUtil.isCustomize(proxySetupModel.get())) {
proxySetupModel.setDomain(""); proxySetupModel.setDomain("");
} else { } else {
proxySetupModel.setDomain(proxySetup.getDomain()); proxySetupModel.setDomain(proxySetupModel.getDomain());
} }
proxySetupModel.setDomainSuffix(""); proxySetupModel.setDomainSuffix("");
styleClass.add(INPUT_CLASS);
domainTextField.promptTextProperty().set("自定义域名"); domainTextField.promptTextProperty().set("自定义域名");
customizeDomainBtn.setText("系统分配"); customizeDomainBtn.setText("系统分配");
domainHtinTextField.setText("请输入您的域名,并解析至: " + ProxyConfig.getServerIP(proxySetupModel.getServer())); domainHtinTextField.setText("请输入您的域名,并解析至: " + ProxyConfig.getServerIP(proxySetupModel.getServer()));
@ -289,14 +281,13 @@ public class MainController extends BaseController<StackPane> implements Initial
if (!parent.getChildren().contains(domainSuffixTextField)) if (!parent.getChildren().contains(domainSuffixTextField))
parent.getChildren().add(domainSuffixTextField); parent.getChildren().add(domainSuffixTextField);
// 用户域名是否自定义 // 用户域名是否自定义
if (DomainUtil.isCustomize(proxySetup)) { if (DomainUtil.isCustomize(proxySetupModel.get())) {
proxySetupModel.setDomain(""); proxySetupModel.setDomain("");
proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(proxySetupModel.getServer())); proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(proxySetupModel.getServer()));
} else { } else {
proxySetupModel.setDomain(DomainUtil.getCustomize(proxySetup)); proxySetupModel.setDomain(DomainUtil.getCustomize(proxySetupModel.get()));
proxySetupModel.setDomainSuffix(DomainUtil.getSuffix(proxySetup)); proxySetupModel.setDomainSuffix(DomainUtil.getSuffix(proxySetup));
} }
styleClass.add(INPUT_LEFT_CLASS);
domainTextField.promptTextProperty().set("自定义子域名 大于3位"); domainTextField.promptTextProperty().set("自定义子域名 大于3位");
customizeDomainBtn.setText("自定义"); customizeDomainBtn.setText("自定义");
domainHtinTextField.setText("请输入子域名长度不小于3个字符"); domainHtinTextField.setText("请输入子域名长度不小于3个字符");
@ -307,10 +298,27 @@ public class MainController extends BaseController<StackPane> implements Initial
tabPane.getSelectionModel().select(1); 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 @Override
public void initViewAction() { public void initViewAction() {
// 重置 // 重置
resetProxyBtn.setOnMouseClicked(event -> proxySetupModel.set(proxySetup)); resetProxyBtn.setOnMouseClicked(event -> {
proxySetup.setRuning(false);
proxySetupModel.set(proxySetup);
domainTextField.resetValidation();
});
// 日志清理 // 日志清理
clearLogBtn.setOnMouseClicked(event -> { clearLogBtn.setOnMouseClicked(event -> {
@ -360,7 +368,7 @@ public class MainController extends BaseController<StackPane> implements Initial
}); });
// 域名检查 // 域名检查
setting.addListener((observable, oldValue, newValue) -> { setup.addListener((observable, oldValue, newValue) -> {
if (newValue) domainTextField.validate(); if (newValue) domainTextField.validate();
}); });
domainTextField.textProperty().addListener((observable, oldValue, newValue) -> { domainTextField.textProperty().addListener((observable, oldValue, newValue) -> {
@ -376,6 +384,9 @@ public class MainController extends BaseController<StackPane> implements Initial
// 点击隧道列表 // 点击隧道列表
proxyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { proxyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
ObservableList<Label> items = proxyListView.getItems(); ObservableList<Label> items = proxyListView.getItems();
if (items.size() == 0) return;
for (Label item : items) { for (Label item : items) {
item.getStyleClass().remove(PROXY_LIST_ITEM_SELECT_CLASS); item.getStyleClass().remove(PROXY_LIST_ITEM_SELECT_CLASS);
} }
@ -383,30 +394,31 @@ public class MainController extends BaseController<StackPane> implements Initial
newValue.getStyleClass().add(PROXY_LIST_ITEM_SELECT_CLASS); newValue.getStyleClass().add(PROXY_LIST_ITEM_SELECT_CLASS);
int oldIndex = items.indexOf(oldValue); int oldIndex = items.indexOf(oldValue);
int newIndex = items.indexOf(newValue); int newIndex = items.indexOf(newValue);
if (oldIndex != -1) proxyList.set(oldIndex, proxySetupModel.get()); // 保存隧道状态
ObservableList<ProxySetup> proxyList = proxyList();
if (oldIndex != -1 && StringUtils.isNotEmpty(proxyList.get(oldIndex).getProxy_name()))
proxyList.set(oldIndex, proxySetupModel.get());
proxySetup = proxyList.get(newIndex); proxySetup = proxyList.get(newIndex);
selectProxy.set(newIndex); selectProxy(newIndex);
Platform.runLater(() -> { Platform.runLater(() -> {
setting.set(false); setup.set(false);
proxySetupModel.set(proxySetup); proxySetupModel.set(proxySetup);
FrpUtil frpUtil = frpUtilMap.get(proxySetup.getProxy_name()); FrpManager frpManager = frpUtilMap.get(proxySetup.getProxy_name());
proxyLogPane.contentProperty().set(frpUtil == null ? null : frpUtil.getConsole()); proxyLogPane.contentProperty().set(frpManager == null ? null : frpManager.getConsole());
setting.set(true); setup.set(true);
setDomainLink();
}); });
} }
}); });
// 添加隧道 // 添加隧道
addProxyBtn.setOnMouseClicked(event -> { addProxyBtn.setOnMouseClicked(event -> {
// 保存运行状态 // 获取默认隧道设置加入列表
proxyList.get(selectProxy.get()).setRuning(proxySetupModel.isRunning()); ObservableList<ProxySetup> proxyList = proxyList();
// 获取默认隧道设置 addProxyList(ProxyManager.initProxy(proxyList.get(proxyList.size() - 1).getSort()));
proxySetup = ProxyUtil.initProxy(proxyList.get(proxyList.size() - 1).getSort()); // 重置隧道列表视图
// 加入列表 initProxyListView();
proxyList.add(proxySetup); proxyListView.getSelectionModel().select(proxyListView.getItems().size() - 1);
initProxyList(proxyList);
// 展示
proxyListView.getSelectionModel().select(proxyList.size() - 1);
}); });
// 启动 // 启动
@ -433,27 +445,28 @@ public class MainController extends BaseController<StackPane> implements Initial
// 复制成功提示 // 复制成功提示
AlertUtil.info("复制成功,快去分享给小伙伴吧!").show(); AlertUtil.info("复制成功,快去分享给小伙伴吧!").show();
}); });
// 退出登录
logoutBtn.setOnMouseClicked(event -> {
for (String key : frpUtilMap.keySet()) {
frpUtilMap.get(key).stop();
} }
ApplicatonStore.logout();
/**
* 显示隧道列表
*/
private void initProxyList(List<ProxySetup> proxyList) {
// 清空列表
proxyListView.getItems().clear();
for (ProxySetup proxy : proxyList) {
try { try {
Label label = FXMLLoader.load(getClass().getResource("/fxml/proxyItem.fxml")); jumpTo(new LoginController());
label.textProperty().set(proxy.getProxy_name());
ObservableList<String> styleClass = label.getStyleClass();
styleClass.addAll(PROXY_LIST_ITEM_CLASS, proxy.isRuning() ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
proxyListView.getItems().add(label);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
});
} }
} //
// private void initEmptyProxyListView() {
// // 初始化视图模型
// proxySetup = ProxyManager.initProxy(null);
// proxySetupModel = new ProxySetupModel(proxySetup);
// setProxyList(Collections.singletonList(proxySetup));
// initProxyListView();
// }
/** /**
* 是否启用当前隧道 * 是否启用当前隧道
@ -468,7 +481,7 @@ public class MainController extends BaseController<StackPane> implements Initial
domainTextField.setDisable(close); // 外网子域名 domainTextField.setDisable(close); // 外网子域名
startProxyBtn.setDisable(close); // 启动按钮 startProxyBtn.setDisable(close); // 启动按钮
ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy.get()).getStyleClass(); ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass();
styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS); styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS);
styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS); styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS);
styleClass.remove(PROXY_LIST_ITEM_CLOSE_CLASS); styleClass.remove(PROXY_LIST_ITEM_CLOSE_CLASS);
@ -479,25 +492,73 @@ public class MainController extends BaseController<StackPane> implements Initial
} }
} }
/** private void initProxyListView() {
* TODO 启动代理 ObservableList<ProxySetup> proxyList = proxyList();
*/ for (ProxySetup proxy : proxyList.subList(proxyListView.getItems().size(), proxyList.size())) {
private void startProxy() { setProxyListView(proxyList.indexOf(proxy), proxy);
// http 隧道系统随机分配端口 }
if (!DomainUtil.isHttp(proxySetupModel)) { // 设置选中
proxySetupModel.setRemotePort(String.valueOf(ProxyUtil.randomPort())); proxyListView.getSelectionModel().select(selectProxy());
} }
private void setProxyListView(int index, ProxySetup setup) {
ObservableList<Label> items = proxyListView.getItems();
try {
Label label = FXMLLoader.load(getClass().getResource("/fxml/proxyItem.fxml"));
label.textProperty().set(setup.getProxy_name());
ObservableList<String> styleClass = label.getStyleClass();
styleClass.addAll(PROXY_LIST_ITEM_CLASS, setup.isRuning() ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
if (index < items.size()) {
items.set(index, label);
} else {
items.add(label);
}
} catch (IOException e) {
e.printStackTrace();
}
proxyListView.getSelectionModel().select(items.size() - 1);
}
/**
* 启动代理
*/
private void startProxy() {
// 验证
if(!domainTextField.validate() || !localHostTextField.validate() || !localPortTextField.validate())
return;
// http 隧道系统随机分配端口
if (!DomainUtil.isHttp(proxySetupModel)) {
proxySetupModel.setRemotePort(String.valueOf(ProxyManager.randomPort()));
}
// 是用户隧道
if (proxySetupModel.getId() != null && !proxySetupModel.get().equals(proxySetup)) { if (proxySetupModel.getId() != null && !proxySetupModel.get().equals(proxySetup)) {
// 删除旧隧道 // 删除旧隧道
ProxyUtil.delete(proxySetupModel.getId()); ProxyManager.delete(Integer.parseInt(proxySetupModel.getId()));
// 添加隧道 // 添加隧道
ProxyUtil.add(new OnHttpListener<ProxySetup>() { ProxyManager.add(new OnHttpListener<ProxySetup>() {
@Override @Override
public void onSucceed(ProxySetup result) { public void onSucceed(ProxySetup result) {
proxySetup = result; result.setRuning(frpUtilMap.containsKey(result.getProxy_name()));
proxySetupModel.set(proxySetup); proxySetupModel.set(result);
}
@Override
public void onFail(Exception e) {
}
}, proxySetupModel.get());
} else if(proxySetupModel.getId() == null) {
// 添加隧道
ProxyManager.add(new OnHttpListener<ProxySetup>() {
@Override
public void onSucceed(ProxySetup result) {
result.setRuning(frpUtilMap.containsKey(result.getProxy_name()));
proxySetupModel.set(result);
} }
@Override @Override
@ -508,23 +569,33 @@ public class MainController extends BaseController<StackPane> implements Initial
} }
// 初始化frputil // 初始化frputil
FrpUtil frpUtil; FrpManager frpManager;
if ((frpUtil = frpUtilMap.get(proxySetupModel.getProxyName())) == null) if ((frpManager = frpUtilMap.get(proxySetupModel.getProxyName())) == null)
frpUtilMap.put(proxySetupModel.getProxyName(), frpUtil = FrpUtil.init(proxySetupModel)); frpUtilMap.put(proxySetupModel.getProxyName(), frpManager = FrpManager.init(proxySetupModel));
// 设置文本域对象 // 设置文本域对象
if (proxyLogPane.getContent() == null) proxyLogPane.contentProperty().set(frpUtil.getConsole()); if (proxyLogPane.getContent() == null) proxyLogPane.contentProperty().set(frpManager.getConsole());
// 开始 // 开始
frpUtil.start(); frpManager.start();
// 显示日志
tabPane.getSelectionModel().select(0);
} }
/** /**
* TODO 暂停代理 * 暂停代理
*/ */
private void stopProxy() { private void stopProxy() {
// 关闭CMD // 关闭CMD
FrpUtil frpUtil = frpUtilMap.get(proxySetupModel.getProxyName()); FrpManager frpManager = frpUtilMap.get(proxySetupModel.getProxyName());
if (frpUtil != null) frpUtil.stop(); if (frpManager != null) frpManager.stop();
}
@Override
public void onDestroy() {
super.onDestroy();
} }
} }

View File

@ -10,15 +10,15 @@ import javafx.scene.layout.StackPane;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import top.octopusyan.base.BaseController; import top.octopusyan.base.BaseController;
import top.octopusyan.manager.http.EasyHttp; import top.octopusyan.config.TextValidate;
import top.octopusyan.manager.http.listener.OnHttpListener;
import top.octopusyan.http.Api; import top.octopusyan.http.Api;
import top.octopusyan.http.request.RegisterParam; import top.octopusyan.http.request.RegisterParam;
import top.octopusyan.http.request.SendEmailCheckParam; import top.octopusyan.http.request.SendEmailCheckParam;
import top.octopusyan.manager.http.EasyHttp;
import top.octopusyan.manager.http.listener.OnHttpListener;
import top.octopusyan.model.ApplicatonStore;
import top.octopusyan.utils.AlertUtil; import top.octopusyan.utils.AlertUtil;
import top.octopusyan.utils.ApplicatonStore;
import top.octopusyan.utils.JsoupUtil; import top.octopusyan.utils.JsoupUtil;
import top.octopusyan.config.TextValidate;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
@ -180,7 +180,7 @@ public class RegisterController extends BaseController<StackPane> implements Ser
if (JsoupUtil.isAlertSuccess(result)) { if (JsoupUtil.isAlertSuccess(result)) {
// 注册成功 // 注册成功
Platform.runLater(() -> Platform.runLater(() ->
AlertUtil.info(JsoupUtil.getHtmlMessage(result)).header(null).show() AlertUtil.info(JsoupUtil.getSuccessMessage(result)).header(null).show()
); );
ApplicatonStore.setRegisterSuccess(true); ApplicatonStore.setRegisterSuccess(true);
Platform.runLater(() -> { Platform.runLater(() -> {

View File

@ -1,15 +1,12 @@
package top.octopusyan.http; package top.octopusyan.http;
import top.octopusyan.http.request.*;
import top.octopusyan.manager.http.api.NotParamApi; import top.octopusyan.manager.http.api.NotParamApi;
import top.octopusyan.manager.http.api.ParamApi; import top.octopusyan.manager.http.api.ParamApi;
import top.octopusyan.manager.http.api.PathParamApi; import top.octopusyan.manager.http.api.PathParamApi;
import top.octopusyan.manager.http.config.BodyType; import top.octopusyan.manager.http.config.BodyType;
import top.octopusyan.manager.http.config.HttpConstant; import top.octopusyan.manager.http.config.HttpConstant;
import top.octopusyan.http.request.ProxySetup; import top.octopusyan.manager.ProxyManager;
import top.octopusyan.http.request.RegisterParam;
import top.octopusyan.http.request.SendEmailCheckParam;
import top.octopusyan.http.request.LoginParam;
import top.octopusyan.utils.ProxyUtil;
/** /**
* @author : octopus yan * @author : octopus yan
@ -39,11 +36,20 @@ public class Api {
BodyType.FORM BodyType.FORM
); );
/** 找回密码 */
public static ParamApi<FindPassParam, String> findpass = new ParamApi<>(
"/?action=findpass&page=findpass",
HttpConstant.Method.POST,
BodyType.FORM
);
/** 获取服务器配置 */ /** 获取服务器配置 */
public static final PathParamApi<String> ServerConfiguration = new PathParamApi<>( public static PathParamApi<String> getServerConfiguration(int node){
"/?page=panel&module=configuration&server={0}", return new PathParamApi<>(
"/?page=panel&module=configuration&server="+node,
HttpConstant.Method.GET HttpConstant.Method.GET
); );
}
/** 隧道列表 */ /** 隧道列表 */
public static final NotParamApi<String> ProxyList = new NotParamApi<>( public static final NotParamApi<String> ProxyList = new NotParamApi<>(
@ -54,7 +60,7 @@ public class Api {
/** 隧道详情 */ /** 隧道详情 */
public static PathParamApi<String> ProxyInfo() { public static PathParamApi<String> ProxyInfo() {
return new PathParamApi<>( return new PathParamApi<>(
"/?page=panel&module=proxies&getproxyinfo={0}&csrf=" + ProxyUtil.getCsrf(), "/?page=panel&module=proxies&getproxyinfo={0}&csrf=" + ProxyManager.getCsrf(),
HttpConstant.Method.GET HttpConstant.Method.GET
); );
} }
@ -62,7 +68,7 @@ public class Api {
/** 添加隧道 */ /** 添加隧道 */
public static ParamApi<ProxySetup, String> AddProxy() { public static ParamApi<ProxySetup, String> AddProxy() {
return new ParamApi<>( return new ParamApi<>(
"/?page=panel&module=addproxy&action=addproxy&csrf=" + ProxyUtil.getCsrf(), "/?page=panel&module=addproxy&action=addproxy&csrf=" + ProxyManager.getCsrf(),
HttpConstant.Method.POST, HttpConstant.Method.POST,
BodyType.FORM BodyType.FORM
); );
@ -71,7 +77,7 @@ public class Api {
/** 删除隧道 */ /** 删除隧道 */
public static PathParamApi<Void> DeleteProxy() { public static PathParamApi<Void> DeleteProxy() {
return new PathParamApi<>( return new PathParamApi<>(
"/?page=panel&module=proxies&delete={0}&csrf=" + ProxyUtil.getCsrf(), "/?page=panel&module=proxies&delete={0}&csrf=" + ProxyManager.getCsrf(),
HttpConstant.Method.GET HttpConstant.Method.GET
); );
} }
@ -79,7 +85,7 @@ public class Api {
/** 随机端口 */ /** 随机端口 */
public static NotParamApi<String> RandomPort() { public static NotParamApi<String> RandomPort() {
return new NotParamApi<>( return new NotParamApi<>(
"/?page=panel&module=addproxy&randomport&csrf=" + ProxyUtil.getCsrf(), "/?page=panel&module=addproxy&randomport&csrf=" + ProxyManager.getCsrf(),
HttpConstant.Method.GET HttpConstant.Method.GET
); );
} }

View File

@ -0,0 +1,16 @@
package top.octopusyan.http.request;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* <p> author : octopus yan
* <p> email : octopus_yan@foxmail.com
* <p> description : 找回密码请求参数
* <p> create : 2022-4-10 20:20 *
*/
@Data
@AllArgsConstructor
public class FindPassParam {
private String username;
}

View File

@ -28,8 +28,10 @@ public class ProxySetup {
private String host_header_rewrite; private String host_header_rewrite;
private String header_X_From_Where; private String header_X_From_Where;
private String sk; private String sk;
private Integer sort;
// 非保留数据
/** 是否启用 */ /** 是否启用 */
private Boolean status; private Boolean status;
private Integer sort;
private boolean runing; private boolean runing;
} }

View File

@ -1,91 +1,59 @@
package top.octopusyan.utils; package top.octopusyan.manager;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import lombok.Data;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import top.octopusyan.manager.ThreadPoolManager; import top.octopusyan.model.ApplicatonStore;
import top.octopusyan.model.ProxySetupModel; import top.octopusyan.model.ProxySetupModel;
import java.io.*; import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.util.ArrayList;
import java.nio.file.Paths; import java.util.List;
/** /**
* @author : octopus yan * @author : octopus yan
* @email : octopus_yan@foxmail.com * @email : octopus_yan@foxmail.com
* @description : TODO frp 工具 * @description : frp 客户端 工具
* @create : 2022-4-7 23:19 * @create : 2022-4-7 23:19
*/ */
public class FrpUtil { public class FrpManager {
public static List<FrpManager> frpcList = new ArrayList<>();
private static final String YAN_FRP_TEMP_DIR_NAME = "frpclienttmp"; 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_PREFIX_NAME = "proxy_";
private final String FRPC_CONF_SUFFIX_NAME = ".ini"; 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 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 static final File frpc = new File(FRPC_CLIENT_FILE_PATH);
public File frpcConfigFile; public File frpcConfigFile;
private final ProxySetupModel model; private final ProxySetupModel model;
private final TextArea console; private final TextArea console;
private Thread thread; private ProxyThread thread;
private Process exec; private Process exec;
private FrpUtil(TextArea console, ProxySetupModel model) { private FrpManager(TextArea console, ProxySetupModel model) {
this.console = console; this.console = console;
this.model = model; this.model = model;
} }
public static FrpUtil init(ProxySetupModel model) { public static FrpManager init(ProxySetupModel model) {
TextArea console = new TextArea(); TextArea console = new TextArea();
console.setPrefHeight(180); console.setPrefHeight(180);
console.setMinWidth(200); console.setMinWidth(200);
return new FrpUtil(console, model); FrpManager frpManager = new FrpManager(console, model);
} frpcList.add(frpManager);
return frpManager;
/**
* 初始化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();
}
}
} }
public TextArea getConsole() { public TextArea getConsole() {
@ -96,43 +64,7 @@ public class FrpUtil {
* 开始 * 开始
*/ */
public void start() { public void start() {
thread = new ProxyThread();
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.start(); thread.start();
} }
@ -145,7 +77,7 @@ public class FrpUtil {
do { do {
if (exec != null && exec.isAlive()) if (exec != null && exec.isAlive())
exec.destroy(); exec.destroy();
thread.setExit(true);
thread.interrupt(); thread.interrupt();
} while (!thread.isInterrupted()); } while (!thread.isInterrupted());
@ -182,18 +114,62 @@ public class FrpUtil {
private void initProxyConfigFile() throws IOException { private void initProxyConfigFile() throws IOException {
// 创建配置文件 // 创建配置文件
frpcConfigFile = new File(getConfigFilePath()); frpcConfigFile = new File(getConfigFilePath());
if (frpcConfigFile.exists()) { if (!frpcConfigFile.exists()) {
frpcConfigFile.delete(); FileUtils.createParentDirectories(frpcConfigFile);
}
frpcConfigFile.createNewFile(); frpcConfigFile.createNewFile();
}
frpcConfigFile.deleteOnExit(); 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) private static void copyFileUsingFileStreams(InputStream input, File dest)
throws IOException { throws IOException {
@ -210,4 +186,46 @@ public class FrpUtil {
output.close(); 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"));
}
}
}
} }

View File

@ -1,19 +1,22 @@
package top.octopusyan.utils; package top.octopusyan.manager;
import javafx.application.Platform; import javafx.application.Platform;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.select.Elements; 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;
import top.octopusyan.config.ProxyConfig.ProxyServer; import top.octopusyan.config.ProxyConfig.ProxyServer;
import top.octopusyan.config.ProxyConfig.ProxyType; import top.octopusyan.config.ProxyConfig.ProxyType;
import top.octopusyan.http.Api; import top.octopusyan.http.Api;
import top.octopusyan.http.request.ProxySetup; 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.model.ProxySetupModel;
import top.octopusyan.utils.AlertUtil;
import top.octopusyan.utils.JsoupUtil;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -27,7 +30,7 @@ import java.util.stream.Collectors;
* @description : 代理设置工具 * @description : 代理设置工具
* @create : 2022-4-5 09:56 * @create : 2022-4-5 09:56
*/ */
public class ProxyUtil { public class ProxyManager {
private static String csrf; private static String csrf;
private static Map<Integer, String> serverConfigraution = new HashMap<>(3); private static Map<Integer, String> serverConfigraution = new HashMap<>(3);
@ -173,8 +176,9 @@ public class ProxyUtil {
if (result != null && result.size() > 0) { if (result != null && result.size() > 0) {
for (ProxySetup proxySetup : result) { for (ProxySetup proxySetup : result) {
if (Objects.equals(proxySetup.getSort(), proxySetup.getSort())) { if (Objects.equals(proxySetup.getSort(), setup.getSort())) {
listener.onSucceed(proxySetup); listener.onSucceed(proxySetup);
return;
} }
} }
} }
@ -201,7 +205,7 @@ public class ProxyUtil {
public static void setCsrf(String htmlStr) { public static void setCsrf(String htmlStr) {
int i = htmlStr.indexOf("var csrf_token = \""); int i = htmlStr.indexOf("var csrf_token = \"");
int i1 = htmlStr.indexOf("\"", i + 18); 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) { public static String getUserServerConfig(int node) {
@ -209,7 +213,7 @@ public class ProxyUtil {
if (StringUtils.isNotEmpty(config)) return config; if (StringUtils.isNotEmpty(config)) return config;
try { try {
String result = EasyHttp.builder() String result = EasyHttp.builder()
.api(Api.ServerConfiguration) .api(Api.getServerConfiguration(node))
.pathParam(String.valueOf(node)) .pathParam(String.valueOf(node))
.execute(new ResponseClass<String>() { .execute(new ResponseClass<String>() {
}); });
@ -225,31 +229,31 @@ public class ProxyUtil {
public static String getProxyConfig(ProxySetupModel setup) { public static String getProxyConfig(ProxySetupModel setup) {
String n = "\n"; String n = "\n";
// 基础配置 // 基础配置
StringBuffer stringBuffer = new StringBuffer("[" + setup.getProxyName() + "]\n"); StringBuilder stringBuilder = new StringBuilder("[" + ApplicatonStore.getAccount() + "_" + setup.getProxyName() + "]\n");
stringBuffer.append("privilege_mode = true\n") stringBuilder.append("privilege_mode = true\n")
.append("type = ").append(setup.getProxyType()).append(n) .append("type = ").append(setup.getProxyType().contains("http") ? "http" : setup.getProxyType()).append(n)
.append("local_ip = ").append(setup.getLocalIp()).append(n) .append("local_ip = ").append(setup.getLocalIp()).append(n)
.append("local_port = ").append(setup.getLocalPort()).append(n).append(n); .append("local_port = ").append(setup.getLocalPort()).append(n).append(n);
if ("http".equals(setup.getProxyType()) || "https".equals(setup.getProxyType())) { if ("http".equals(setup.getProxyType()) || "https".equals(setup.getProxyType())) {
// HTTP / HTTPS // 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())) 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())) 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())) 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 { } else {
// TCP / UDP / XTCP / STCP // 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())) 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); .append("use_compression = ").append(setup.isUseCompression()).append(n).append(n);
return stringBuffer.toString(); return stringBuilder.toString();
} }
} }

View File

@ -100,7 +100,7 @@ public interface ILogStrategy {
* 创建对应数量的制表符 * 创建对应数量的制表符
*/ */
static String getSpaceOrTab(int tabNum) { static String getSpaceOrTab(int tabNum) {
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < tabNum; i++) { for (int i = 0; i < tabNum; i++) {
sb.append('\t'); sb.append('\t');
} }

View File

@ -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<ProxySetup> 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<ProxySetup> proxyList() {
return proxyList;
}
public static void setProxyList(List<ProxySetup> 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();
}
}
}
}

View File

@ -15,7 +15,7 @@ import top.octopusyan.utils.DomainUtil;
* @create : 2022-4-6 16:45 * @create : 2022-4-6 16:45
*/ */
public class ProxySetupModel { public class ProxySetupModel {
private final SimpleIntegerProperty id = new SimpleIntegerProperty(); private final SimpleStringProperty id = new SimpleStringProperty();
private final SimpleStringProperty server = new SimpleStringProperty(); private final SimpleStringProperty server = new SimpleStringProperty();
private final SimpleStringProperty proxyName = new SimpleStringProperty(); private final SimpleStringProperty proxyName = new SimpleStringProperty();
private final SimpleStringProperty proxyType = new SimpleStringProperty(); private final SimpleStringProperty proxyType = new SimpleStringProperty();
@ -36,6 +36,8 @@ public class ProxySetupModel {
private final SimpleBooleanProperty isCustomize = new SimpleBooleanProperty(); private final SimpleBooleanProperty isCustomize = new SimpleBooleanProperty();
public ProxySetupModel(ProxySetup setup) { public ProxySetupModel(ProxySetup setup) {
if(setup == null) return;
String domainStr = setup.getDomain(); String domainStr = setup.getDomain();
String suffix = ""; String suffix = "";
isCustomize.set(DomainUtil.isCustomize(setup)); isCustomize.set(DomainUtil.isCustomize(setup));
@ -57,13 +59,13 @@ public class ProxySetupModel {
setUseCompression(setup.getUse_compression()); setUseCompression(setup.getUse_compression());
setHeader_X_From_Where(setup.getHost_header_rewrite()); setHeader_X_From_Where(setup.getHost_header_rewrite());
setHostHeaderRewrite(setup.getHost_header_rewrite()); setHostHeaderRewrite(setup.getHost_header_rewrite());
setStatus(setup.getStatus()); setStatus(setup.getStatus() == null || setup.getStatus());
setSk(setup.getSk()); setSk(setup.getSk());
setSort(setup.getSort()); setSort(setup.getSort());
setRunning(setup.isRuning()); setRunning(setup.isRuning());
} }
public Integer getId() { public String getId() {
return id.get(); return id.get();
} }
@ -136,7 +138,7 @@ public class ProxySetupModel {
} }
public void setId(Integer id) { 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) { public void setProxyName(String proxyName) {
@ -196,7 +198,7 @@ public class ProxySetupModel {
} }
public void setSort(Integer sort) { public void setSort(Integer sort) {
if(sort != null) this.sort.set(sort); if (sort != null) this.sort.set(sort);
} }
public void setStatus(boolean status) { public void setStatus(boolean status) {
@ -235,7 +237,7 @@ public class ProxySetupModel {
return useEncryption; return useEncryption;
} }
public SimpleIntegerProperty idProperty() { public SimpleStringProperty idProperty() {
return id; return id;
} }
@ -292,7 +294,6 @@ public class ProxySetupModel {
suffix = DomainUtil.getSuffix(setup); suffix = DomainUtil.getSuffix(setup);
} }
setId(setup.getId()); setId(setup.getId());
setServer(ProxyConfig.getServerName(setup.getNode()));
setProxyName(setup.getProxy_name()); setProxyName(setup.getProxy_name());
setProxyType(setup.getProxy_type()); setProxyType(setup.getProxy_type());
setLocalIp(setup.getLocal_ip()); setLocalIp(setup.getLocal_ip());
@ -305,15 +306,17 @@ public class ProxySetupModel {
setUseCompression(setup.getUse_compression()); setUseCompression(setup.getUse_compression());
setHeader_X_From_Where(setup.getHost_header_rewrite()); setHeader_X_From_Where(setup.getHost_header_rewrite());
setHostHeaderRewrite(setup.getHost_header_rewrite()); setHostHeaderRewrite(setup.getHost_header_rewrite());
setStatus(setup.getStatus()); setStatus(setup.getStatus() == null || setup.getStatus());
setSk(setup.getSk()); setSk(setup.getSk());
setSort(setup.getSort()); setSort(setup.getSort());
setRunning(setup.isRuning()); setRunning(setup.isRuning());
// 数据绑定原因 放在最后
setServer(ProxyConfig.getServerName(setup.getNode()));
} }
public ProxySetup get() { public ProxySetup get() {
return new ProxySetup( return new ProxySetup(
getId(), getId() == null ? null : Integer.parseInt(getId()),
ProxyConfig.getServerNode(server.get()), ProxyConfig.getServerNode(server.get()),
getProxyName(), getProxyName(),
StringUtils.lowerCase(proxyType.getValue()), StringUtils.lowerCase(proxyType.getValue()),
@ -327,8 +330,8 @@ public class ProxySetupModel {
getHostHeaderRewrite(), getHostHeaderRewrite(),
getHeader_X_From_Where(), getHeader_X_From_Where(),
getSk(), getSk(),
getStatus(),
getSort(), getSort(),
getStatus(),
isRunning() isRunning()
); );
} }

View File

@ -65,23 +65,32 @@ public class AlertUtil {
alert.showAndWait(); alert.showAndWait();
} }
private void show(OnClickListener listener) { /**
* AlertUtil.confirm
* @param listener
*/
public void show(OnClickListener listener) {
Optional<ButtonType> result = alert.showAndWait(); Optional<ButtonType> result = alert.showAndWait();
listener.onClicked(result.get().getText()); listener.onClicked(result.get().getText());
} }
private void show(OnChoseListener listener) { /**
* AlertUtil.confirm
* @param listener
*/
public void show(OnChoseListener listener) {
Optional<ButtonType> result = alert.showAndWait(); Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) { if (result.get() == ButtonType.OK) {
listener.confirm(); listener.confirm();
} else { } else {
listener.cancelOrClose(); listener.cancelOrClose(result.get());
} }
} }
/** /**
* AlertUtil.input
* 如果用户点击了取消按钮,将会返回null * 如果用户点击了取消按钮,将会返回null
*/ */
public String getInput() { public String getInput() {
@ -92,6 +101,12 @@ public class AlertUtil {
return null; return null;
} }
/**
* AlertUtil.choices
* @param choices
* @param <R>
* @return
*/
public <R> R getChoice(R... choices) { public <R> R getChoice(R... choices) {
Optional<R> result = alert.showAndWait(); Optional<R> result = alert.showAndWait();
return result.get(); return result.get();
@ -155,6 +170,9 @@ public class AlertUtil {
return alert; return alert;
} }
/**
* 确认对话框
*/
public static Builder confirm() { public static Builder confirm() {
Alert alert = new Alert(AlertType.CONFIRMATION); Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("确认对话框"); alert.setTitle("确认对话框");
@ -196,7 +214,7 @@ public class AlertUtil {
interface OnChoseListener { interface OnChoseListener {
void confirm(); void confirm();
void cancelOrClose(); void cancelOrClose(ButtonType buttonType);
} }
interface OnClickListener { interface OnClickListener {

View File

@ -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<ProxySetup> 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<ProxySetup> getProxyList() {
return proxyList.get();
}
public static void initProxyList(List<ProxySetup> 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();
}
}

View File

@ -1,13 +1,11 @@
package top.octopusyan.utils; package top.octopusyan.utils;
import cn.hutool.core.util.ObjectUtil;
import top.octopusyan.config.ProxyConfig; import top.octopusyan.config.ProxyConfig;
import top.octopusyan.http.request.ProxySetup; import top.octopusyan.http.request.ProxySetup;
import top.octopusyan.model.ProxySetupModel; import top.octopusyan.model.ProxySetupModel;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Objects;
/** /**
* @author : octopus yan * @author : octopus yan
@ -34,10 +32,6 @@ public class DomainUtil {
return ProxyConfig.getTypeIndex(model.getProxyType()) < 2; return ProxyConfig.getTypeIndex(model.getProxyType()) < 2;
} }
public static boolean isHttps(ProxySetupModel model){
return ProxyConfig.getTypeIndex(model.getProxyType()) == 1;
}
/** /**
* 是否自定义隧道 * 是否自定义隧道
*/ */

View File

@ -2,12 +2,11 @@ package top.octopusyan.utils;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.MD5;
import sun.misc.BASE64Encoder;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException; import javax.crypto.NoSuchPaddingException;
@ -30,7 +29,7 @@ import java.util.Arrays;
* @create : 2021-03-18 19:03 * @create : 2021-03-18 19:03
*/ */
public class EncryptionUtil { public class EncryptionUtil {
private static EncryptionUtil util; private static final String defaultPassword = "yanfrp";
private static Logger logger = LoggerFactory.getLogger(EncryptionUtil.class); private static Logger logger = LoggerFactory.getLogger(EncryptionUtil.class);
private static BASE64Encoder BASE64Encoder; private static BASE64Encoder BASE64Encoder;
@ -51,7 +50,8 @@ public class EncryptionUtil {
* 加解密算法/工作模式/填充方式 * 加解密算法/工作模式/填充方式
*/ */
private static final String ECB_MOB = "DES/ECB/PKCS5Padding"; 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; private static final Charset UTF_8 = StandardCharsets.UTF_8;
@ -60,8 +60,8 @@ public class EncryptionUtil {
private static final char hexDigitsLower[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', private static final char hexDigitsLower[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
'd', 'e', 'f'}; 'd', 'e', 'f'};
public static synchronized EncryptionUtil getInstance() { public static void init() {
if (util == null) {
BASE64Encoder = new BASE64Encoder(); BASE64Encoder = new BASE64Encoder();
try { try {
MessageDigest_SHA1 = MessageDigest.getInstance("SHA-1"); MessageDigest_SHA1 = MessageDigest.getInstance("SHA-1");
@ -72,8 +72,29 @@ public class EncryptionUtil {
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) { } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace(); 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) { public static String getSha1(String str) {

View File

@ -23,7 +23,7 @@ public class JsoupUtil {
return getDocument(htmlStr).body().getElementsByClass("alert alert-danger alert-dismissable").text().substring(1); 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); return getDocument(htmlStr).getElementsByClass("alert alert-success alert-dismissable").text().substring(1);
} }

View File

@ -43,7 +43,7 @@
-fx-text-fill: #757575; -fx-text-fill: #757575;
} }
/* TODO 隧道列表 */ /* 隧道列表 */
#proxyListView .list-cell:selected { #proxyListView .list-cell:selected {
-fx-background-color: linear-gradient(#9198e5, #57b4f2); -fx-background-color: linear-gradient(#9198e5, #57b4f2);
} }
@ -93,7 +93,7 @@
-fx-border-width: 0 1 1; -fx-border-width: 0 1 1;
-fx-font-size: 14px; -fx-font-size: 14px;
-fx-text-fill: white; -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"; -fx-font-family: "Microsoft YaHei";
} }
/* 启动 */ /* 启动 */

View File

@ -95,8 +95,8 @@
<Font size="11.5"/> <Font size="11.5"/>
</font> </font>
</JFXCheckBox> </JFXCheckBox>
<JFXButton alignment="TOP_CENTER" contentDisplay="RIGHT" prefHeight="23.0" prefWidth="64.0" <JFXButton fx:id="findpassBtn" alignment="TOP_CENTER" contentDisplay="RIGHT" prefHeight="23.0" prefWidth="64.0"
text="找回密码" textAlignment="RIGHT" textFill="#00000078"> text="忘记密码" textAlignment="RIGHT" textFill="#00000078">
<font> <font>
<Font size="11.0"/> <Font size="11.0"/>
</font> </font>

View File

@ -5,7 +5,6 @@
<?import com.jfoenix.controls.JFXListView?> <?import com.jfoenix.controls.JFXListView?>
<?import com.jfoenix.controls.JFXRadioButton?> <?import com.jfoenix.controls.JFXRadioButton?>
<?import com.jfoenix.controls.JFXTabPane?> <?import com.jfoenix.controls.JFXTabPane?>
<?import com.jfoenix.controls.JFXTextArea?>
<?import com.jfoenix.controls.JFXTextField?> <?import com.jfoenix.controls.JFXTextField?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.Cursor?> <?import javafx.scene.Cursor?>
@ -166,21 +165,24 @@
<Insets right="20.0"/> <Insets right="20.0"/>
</HBox.margin> </HBox.margin>
</Label> </Label>
<HBox prefWidth="320.0"> <AnchorPane maxHeight="30.0" prefHeight="30" prefWidth="320.0">
<JFXTextField fx:id="domainTextField" alignment="CENTER" prefHeight="30.0" prefWidth="320.0" <JFXTextField fx:id="domainTextField" prefHeight="31.0" prefWidth="320.0"
promptText="自定义子域名大于3位" styleClass="inputText-left"> promptText="自定义子域名大于3位" styleClass="inputText">
<font> <font>
<Font size="14.0"/> <Font size="14.0"/>
</font> </font>
<padding>
<Insets left="15.0"/>
</padding>
</JFXTextField> </JFXTextField>
<JFXTextField fx:id="domainSuffixTextField" alignment="CENTER" disable="true" <JFXTextField fx:id="domainSuffixTextField" alignment="CENTER" disable="true"
minWidth="155.0" prefHeight="30.0" styleClass="inputText-right" layoutX="165.0" prefHeight="30.0" prefWidth="155.0"
text=".frp.octopusyan.top"> styleClass="inputText-right" text=".frp.octopusyan.top">
<font> <font>
<Font size="14.0"/> <Font size="14.0"/>
</font> </font>
</JFXTextField> </JFXTextField>
</HBox> </AnchorPane>
<JFXButton fx:id="customizeDomainBtn" prefHeight="Infinity" text="自定义"> <JFXButton fx:id="customizeDomainBtn" prefHeight="Infinity" text="自定义">
<HBox.margin> <HBox.margin>
<Insets left="5.0"/> <Insets left="5.0"/>
@ -227,15 +229,13 @@
<AnchorPane prefHeight="Infinity" prefWidth="Infinity" GridPane.rowIndex="1"> <AnchorPane prefHeight="Infinity" prefWidth="Infinity" GridPane.rowIndex="1">
<JFXTabPane fx:id="tabPane" prefHeight="230" prefWidth="520.0" styleClass="mainPane"> <JFXTabPane fx:id="tabPane" prefHeight="230" prefWidth="520.0" styleClass="mainPane">
<tabs> <tabs>
<Tab fx:id="proxyLogPane" text=" 日志 "> <Tab fx:id="proxyLogPane" text=" 日志 "/>
<JFXTextArea prefWidth="Infinity"/>
</Tab>
<Tab text="常见问题"> <Tab text="常见问题">
<VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> <VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<padding> <padding>
<Insets left="20.0" top="20.0"/> <Insets left="20.0" top="20.0"/>
</padding> </padding>
<HBox prefHeight="30.0" prefWidth="Infinity"> <HBox prefHeight="30.0" prefWidth="Infinity" visible="false">
<Label prefHeight="Infinity" text="* 若启动后无法通过外网访问,请点击"/> <Label prefHeight="Infinity" text="* 若启动后无法通过外网访问,请点击"/>
<Hyperlink prefHeight="Infinity" text="此处"/> <Hyperlink prefHeight="Infinity" text="此处"/>
<Label prefHeight="Infinity" text="排查问题"/> <Label prefHeight="Infinity" text="排查问题"/>
@ -246,11 +246,12 @@
<Label prefHeight="30" text="* 请勿将非法、暴力、色情等信息映射到外网上去,一经发现立即封号"/> <Label prefHeight="30" text="* 请勿将非法、暴力、色情等信息映射到外网上去,一经发现立即封号"/>
</VBox> </VBox>
</Tab> </Tab>
<Tab text="使用场景"> <!-- TODO 使用场景 -->
<VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> <!-- <Tab text="使用场景">-->
<!-- <VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">-->
</VBox> <!-- </VBox>-->
</Tab> <!-- </Tab>-->
</tabs> </tabs>
</JFXTabPane> </JFXTabPane>
<JFXButton fx:id="clearLogBtn" layoutX="480.0" layoutY="-1.0" prefHeight="35.0" prefWidth="26.0" <JFXButton fx:id="clearLogBtn" layoutX="480.0" layoutY="-1.0" prefHeight="35.0" prefWidth="26.0"
@ -263,13 +264,22 @@
</cursor> </cursor>
</JFXButton> </JFXButton>
</AnchorPane> </AnchorPane>
<VBox prefHeight="200.0" prefWidth="100.0" GridPane.columnIndex="1" GridPane.rowIndex="1"> <VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" GridPane.columnIndex="1"
GridPane.rowIndex="1">
<JFXButton fx:id="startProxyBtn" buttonType="RAISED" prefHeight="44.0" prefWidth="202.0" <JFXButton fx:id="startProxyBtn" buttonType="RAISED" prefHeight="44.0" prefWidth="202.0"
styleClass="startProxyBtn" text="启动"> styleClass="startProxyBtn" text="启动">
<cursor> <cursor>
<Cursor fx:constant="HAND"/> <Cursor fx:constant="HAND"/>
</cursor> </cursor>
</JFXButton> </JFXButton>
<JFXButton fx:id="logoutBtn" text="退出登录">
<VBox.margin>
<Insets top="20.0"/>
</VBox.margin>
<cursor>
<Cursor fx:constant="HAND"/>
</cursor>
</JFXButton>
</VBox> </VBox>
</children> </children>
</GridPane> </GridPane>