应用数据本地加密存储

添加记住密码、自动登录、找回密码
修复点击隧道列表时隧道设置显示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>
<artifactId>YanFrp</artifactId>
<version>1.0-SNAPSHOT</version>
<version>0.1.2-SNAPSHOT</version>
<name>YanFrp</name>
<properties>

View File

@ -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...");
}

View File

@ -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);
}
}

View File

@ -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<P extends Pane> 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) {

View File

@ -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;

View File

@ -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}$");

View File

@ -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<StackPane> 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<StackPane> 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<StackPane> 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<StackPane> 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<StackPane> 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<StackPane> 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<StackPane> 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<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() {
// 获取文本校验结果
@ -207,7 +250,7 @@ public class LoginController extends BaseController<StackPane> 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<String>() {
@Override
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());
return;
}
// TODO 登录成功
// 登录成功
setCsrf();
// 记住我
ApplicatonStore.rememberMe(tmppwd.get());
// 跳转
Platform.runLater(() -> {
try {
jumpTo(new MainController());
@ -243,7 +289,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
.request(new OnHttpListener<String>() {
@Override
public void onSucceed(String result) {
ProxyUtil.setCsrf(result);
ProxyManager.setCsrf(result);
}
@Override
@ -255,7 +301,6 @@ public class LoginController extends BaseController<StackPane> implements Initia
@Override
public void onDestroy() {
EasyHttp.cancel();
super.onDestroy();
}
}

View File

@ -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<StackPane> 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<StackPane> 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<ProxySetup> proxyList = new ArrayList<>();
private Map<String, FrpUtil> frpUtilMap = new HashMap<>();
private SimpleIntegerProperty selectProxy = new SimpleIntegerProperty(0);
private Map<String, FrpManager> 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<StackPane> 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<StackPane> implements Initial
Arrays.asList(ProxyServer.values()).forEach((server) -> proxyServerComboBox.getItems().add(server.getServerName()));
// 获取用户隧道列表
ProxyUtil.getList(new OnHttpListener<List<ProxySetup>>() {
ProxyManager.getList(new OnHttpListener<List<ProxySetup>>() {
@Override
public void onSucceed(List<ProxySetup> 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<StackPane> 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<StackPane> 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<StackPane> implements Initial
startProxyBtn.getStyleClass().add(newValue ? stopClass : startClass);
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_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<String> 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<StackPane> 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<StackPane> 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<StackPane> 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<StackPane> implements Initial
// 点击隧道列表
proxyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
ObservableList<Label> items = proxyListView.getItems();
if (items.size() == 0) return;
for (Label item : items) {
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);
int oldIndex = items.indexOf(oldValue);
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);
selectProxy.set(newIndex);
selectProxy(newIndex);
Platform.runLater(() -> {
setting.set(false);
setup.set(false);
proxySetupModel.set(proxySetup);
FrpUtil frpUtil = frpUtilMap.get(proxySetup.getProxy_name());
proxyLogPane.contentProperty().set(frpUtil == null ? null : frpUtil.getConsole());
setting.set(true);
FrpManager frpManager = frpUtilMap.get(proxySetup.getProxy_name());
proxyLogPane.contentProperty().set(frpManager == null ? null : frpManager.getConsole());
setup.set(true);
setDomainLink();
});
}
});
// 添加隧道
addProxyBtn.setOnMouseClicked(event -> {
// 保存运行状态
proxyList.get(selectProxy.get()).setRuning(proxySetupModel.isRunning());
// 获取默认隧道设置
proxySetup = ProxyUtil.initProxy(proxyList.get(proxyList.size() - 1).getSort());
// 加入列表
proxyList.add(proxySetup);
initProxyList(proxyList);
// 展示
proxyListView.getSelectionModel().select(proxyList.size() - 1);
// 获取默认隧道设置加入列表
ObservableList<ProxySetup> proxyList = proxyList();
addProxyList(ProxyManager.initProxy(proxyList.get(proxyList.size() - 1).getSort()));
// 重置隧道列表视图
initProxyListView();
proxyListView.getSelectionModel().select(proxyListView.getItems().size() - 1);
});
// 启动
@ -433,27 +445,28 @@ public class MainController extends BaseController<StackPane> implements Initial
// 复制成功提示
AlertUtil.info("复制成功,快去分享给小伙伴吧!").show();
});
}
/**
* 显示隧道列表
*/
private void initProxyList(List<ProxySetup> proxyList) {
// 清空列表
proxyListView.getItems().clear();
for (ProxySetup proxy : proxyList) {
// 退出登录
logoutBtn.setOnMouseClicked(event -> {
for (String key : frpUtilMap.keySet()) {
frpUtilMap.get(key).stop();
}
ApplicatonStore.logout();
try {
Label label = FXMLLoader.load(getClass().getResource("/fxml/proxyItem.fxml"));
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);
jumpTo(new LoginController());
} catch (IOException e) {
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); // 外网子域名
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_RUN_CLASS);
styleClass.remove(PROXY_LIST_ITEM_CLOSE_CLASS);
@ -479,25 +492,73 @@ public class MainController extends BaseController<StackPane> implements Initial
}
}
/**
* TODO 启动代理
*/
private void startProxy() {
// http 隧道系统随机分配端口
if (!DomainUtil.isHttp(proxySetupModel)) {
proxySetupModel.setRemotePort(String.valueOf(ProxyUtil.randomPort()));
private void initProxyListView() {
ObservableList<ProxySetup> proxyList = proxyList();
for (ProxySetup proxy : proxyList.subList(proxyListView.getItems().size(), proxyList.size())) {
setProxyListView(proxyList.indexOf(proxy), proxy);
}
// 设置选中
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)) {
// 删除旧隧道
ProxyUtil.delete(proxySetupModel.getId());
ProxyManager.delete(Integer.parseInt(proxySetupModel.getId()));
// 添加隧道
ProxyUtil.add(new OnHttpListener<ProxySetup>() {
ProxyManager.add(new OnHttpListener<ProxySetup>() {
@Override
public void onSucceed(ProxySetup result) {
proxySetup = result;
proxySetupModel.set(proxySetup);
result.setRuning(frpUtilMap.containsKey(result.getProxy_name()));
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
@ -508,23 +569,33 @@ public class MainController extends BaseController<StackPane> implements Initial
}
// 初始化frputil
FrpUtil frpUtil;
if ((frpUtil = frpUtilMap.get(proxySetupModel.getProxyName())) == null)
frpUtilMap.put(proxySetupModel.getProxyName(), frpUtil = FrpUtil.init(proxySetupModel));
FrpManager frpManager;
if ((frpManager = frpUtilMap.get(proxySetupModel.getProxyName())) == null)
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() {
// 关闭CMD
FrpUtil frpUtil = frpUtilMap.get(proxySetupModel.getProxyName());
if (frpUtil != null) frpUtil.stop();
FrpManager frpManager = frpUtilMap.get(proxySetupModel.getProxyName());
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.jetbrains.annotations.NotNull;
import top.octopusyan.base.BaseController;
import top.octopusyan.manager.http.EasyHttp;
import top.octopusyan.manager.http.listener.OnHttpListener;
import top.octopusyan.config.TextValidate;
import top.octopusyan.http.Api;
import top.octopusyan.http.request.RegisterParam;
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.ApplicatonStore;
import top.octopusyan.utils.JsoupUtil;
import top.octopusyan.config.TextValidate;
import java.io.IOException;
import java.io.Serializable;
@ -180,7 +180,7 @@ public class RegisterController extends BaseController<StackPane> implements Ser
if (JsoupUtil.isAlertSuccess(result)) {
// 注册成功
Platform.runLater(() ->
AlertUtil.info(JsoupUtil.getHtmlMessage(result)).header(null).show()
AlertUtil.info(JsoupUtil.getSuccessMessage(result)).header(null).show()
);
ApplicatonStore.setRegisterSuccess(true);
Platform.runLater(() -> {

View File

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

View File

@ -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<FrpManager> 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"));
}
}
}
}

View File

@ -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<Integer, String> 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<String>() {
});
@ -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();
}
}

View File

@ -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');
}

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
*/
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()
);
}

View File

@ -65,23 +65,32 @@ public class AlertUtil {
alert.showAndWait();
}
private void show(OnClickListener listener) {
/**
* AlertUtil.confirm
* @param listener
*/
public void show(OnClickListener listener) {
Optional<ButtonType> result = alert.showAndWait();
listener.onClicked(result.get().getText());
}
private void show(OnChoseListener listener) {
/**
* AlertUtil.confirm
* @param listener
*/
public void show(OnChoseListener listener) {
Optional<ButtonType> 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 <R>
* @return
*/
public <R> R getChoice(R... choices) {
Optional<R> 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 {

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;
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;
}
/**
* 是否自定义隧道
*/

View File

@ -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) {

View File

@ -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);
}

View File

@ -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";
}
/* 启动 */

View File

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

View File

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