添加stcp,xtcp隧道类型
添加stcp,xtcp隧道类型设置导入、导出功能 添加HTTP Basic Auth设置
This commit is contained in:
parent
4bf2a8f4ce
commit
4b0c793e42
15
README.md
15
README.md
@ -1,12 +1,23 @@
|
||||
# Yan Frp
|
||||
基于javafx开发的 frp 客户端, 后端使用的是开源的 frp管理面板 [SakuraPanel](https://github.com/ZeroDream-CN/SakuraPanel)
|
||||
|
||||
网址:https://frp.octopusyan.top
|
||||
基于javafx开发的 frp 客户端
|
||||
后台管理面板使用的是开源的 frp 管理面板 [SakuraPanel](https://github.com/ZeroDream-CN/SakuraPanel)
|
||||
|
||||
ps:为了方便 YanFrp 我自己修改了些面板代码,如果想自己搭建需要改造
|
||||
|
||||
管理面板网址:https://frp.octopusyan.top/?page=login
|
||||
|
||||
# 说明:
|
||||
|
||||
1.
|
||||
|
||||
# 后续开发计划
|
||||
|
||||
- ~~HTTP Basic Auth~~ (已完成)
|
||||
- ~~添加p2p连接方式(xtcp,stcp)~~ (已完成)
|
||||
- HTTP URL路由
|
||||
- 待添加...
|
||||
|
||||
# 界面:
|
||||
|
||||
登录
|
||||
|
3
pom.xml
3
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>top.octopusyan</groupId>
|
||||
<artifactId>YanFrp</artifactId>
|
||||
<version>1.0.3-SNAPSHOT</version>
|
||||
<version>1.1</version>
|
||||
<name>YanFrp</name>
|
||||
|
||||
<properties>
|
||||
@ -84,7 +84,6 @@
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 算法库 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
|
@ -86,12 +86,7 @@ public class YanFrpApplication extends Application {
|
||||
FXMLLoader fxmlLoader = FxmlUtil.init("/fxml/login.fxml");
|
||||
StackPane root = fxmlLoader.load();//底层面板
|
||||
stage.initStyle(StageStyle.TRANSPARENT);
|
||||
Scene scene = new Scene(
|
||||
root,
|
||||
root.getPrefWidth() + 20,
|
||||
root.getPrefHeight() + 20,
|
||||
Color.TRANSPARENT
|
||||
);
|
||||
Scene scene = new Scene(root, root.getPrefWidth() + 20, root.getPrefHeight() + 20, Color.TRANSPARENT);
|
||||
scene.getStylesheets().addAll(Objects.requireNonNull(getClass().getResource("/css/root.css")).toExternalForm());
|
||||
stage.setScene(scene);
|
||||
stage.show();
|
||||
|
@ -7,6 +7,7 @@ import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.stage.Stage;
|
||||
@ -17,6 +18,7 @@ import top.octopusyan.manager.FrpManager;
|
||||
import top.octopusyan.model.ApplicatonStore;
|
||||
import top.octopusyan.utils.FxmlUtil;
|
||||
import top.octopusyan.utils.Loading;
|
||||
import top.octopusyan.utils.TooltipUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
@ -36,6 +38,8 @@ public abstract class BaseController<P extends Pane> implements Initializable {
|
||||
|
||||
private volatile Loading loading;
|
||||
|
||||
protected TooltipUtil tooltipUtil;
|
||||
|
||||
public void jumpTo(BaseController<P> controller) throws IOException {
|
||||
FXMLLoader fxmlLoader = FxmlUtil.init(controller.getRootFxmlPath());
|
||||
|
||||
@ -77,11 +81,14 @@ public abstract class BaseController<P extends Pane> implements Initializable {
|
||||
});
|
||||
}
|
||||
|
||||
// app 版本信息
|
||||
if (getAppVersionLabel() != null) getAppVersionLabel().setText("version : v" + ApplicatonStore.APP_VERSION);
|
||||
|
||||
// 这个位置的左边第一个 JFXBtn 会莫名其妙会的焦点效果,启动时禁用焦点,取消按钮效果
|
||||
getFirstBtn().setDisableVisualFocus(true);
|
||||
if (getFirstBtn() != null) getFirstBtn().setDisableVisualFocus(true);
|
||||
|
||||
// 最小化窗口
|
||||
getMinimizeBtn().setOnMouseClicked(event -> {
|
||||
if (getMinimizeBtn() != null) getMinimizeBtn().setOnMouseClicked(event -> {
|
||||
if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) {
|
||||
Stage stage = (Stage) getRootPanel().getScene().getWindow();
|
||||
stage.setIconified(true);
|
||||
@ -89,7 +96,7 @@ public abstract class BaseController<P extends Pane> implements Initializable {
|
||||
});
|
||||
|
||||
// 关闭窗口
|
||||
getClooseBtn().setOnMouseClicked(event -> {
|
||||
if (getClooseBtn() != null) getClooseBtn().setOnMouseClicked(event -> {
|
||||
if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) {
|
||||
onDestroy();
|
||||
}
|
||||
@ -111,7 +118,7 @@ public abstract class BaseController<P extends Pane> implements Initializable {
|
||||
|
||||
public void showLoading(String message) {
|
||||
if (loading == null) loading = new Loading((Stage) getRootPanel().getScene().getWindow());
|
||||
if (!StringUtils.isEmpty(message)) loading.showMessage(message);
|
||||
if (StringUtils.isNotEmpty(message)) loading.showMessage(message);
|
||||
loading.show();
|
||||
}
|
||||
|
||||
@ -131,6 +138,11 @@ public abstract class BaseController<P extends Pane> implements Initializable {
|
||||
loading.closeStage();
|
||||
}
|
||||
|
||||
protected TooltipUtil getTooltipUtil() {
|
||||
if (tooltipUtil == null) tooltipUtil = TooltipUtil.getInstance(getRootPanel());
|
||||
return tooltipUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* 窗口拖拽设置
|
||||
*
|
||||
@ -153,6 +165,11 @@ public abstract class BaseController<P extends Pane> implements Initializable {
|
||||
@NotNull
|
||||
public abstract String getRootFxmlPath();
|
||||
|
||||
/**
|
||||
* App版本信息标签
|
||||
*/
|
||||
public abstract Label getAppVersionLabel();
|
||||
|
||||
/**
|
||||
* 关闭按钮
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@ package top.octopusyan.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import top.octopusyan.model.ProxySetupModel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -29,7 +30,6 @@ public class ProxyConfig {
|
||||
|
||||
typePort.put("http", 80);
|
||||
typePort.put("https", 80);
|
||||
typePort.put("ssh", 22);
|
||||
typePort.put("tcp", 0);
|
||||
typePort.put("udp", 0);
|
||||
}
|
||||
@ -57,22 +57,13 @@ public class ProxyConfig {
|
||||
}
|
||||
|
||||
public enum ProxyType {
|
||||
HTTP("http"),
|
||||
HTTPS("https"),
|
||||
SSH("tcp"),
|
||||
TCP("tcp"),
|
||||
UDP("udp"),
|
||||
HTTP,
|
||||
HTTPS,
|
||||
TCP,
|
||||
UDP,
|
||||
STCP,
|
||||
XTCP,
|
||||
;
|
||||
|
||||
private final String type;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
ProxyType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getServerPath(String serverName) {
|
||||
@ -80,7 +71,7 @@ public class ProxyConfig {
|
||||
}
|
||||
|
||||
public static int getServerNode(String serverName) {
|
||||
for (ProxyServer server : Arrays.asList(ProxyServer.values())) {
|
||||
for (ProxyServer server : ProxyServer.values()) {
|
||||
if (server.serverName.equals(serverName)) return server.value;
|
||||
}
|
||||
|
||||
@ -89,6 +80,7 @@ public class ProxyConfig {
|
||||
|
||||
/**
|
||||
* 获取服务名称
|
||||
*
|
||||
* @param node 服务器标签
|
||||
*/
|
||||
public static String getServerName(int node) {
|
||||
@ -97,8 +89,17 @@ public class ProxyConfig {
|
||||
return proxyServer.getServerName();
|
||||
}
|
||||
|
||||
public static boolean isHttp(ProxySetupModel model) {
|
||||
return ProxyConfig.getTypeIndex(model.getProxyType()) < 2;
|
||||
}
|
||||
|
||||
public static boolean isP2p(ProxySetupModel model) {
|
||||
return ProxyConfig.getTypeIndex(model.getProxyType()) > 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器IP地址
|
||||
*
|
||||
* @param serverName 服务器名称
|
||||
*/
|
||||
public static String getServerIP(String serverName) {
|
||||
@ -107,6 +108,7 @@ public class ProxyConfig {
|
||||
|
||||
/**
|
||||
* 获取服务器IP地址
|
||||
*
|
||||
* @param node 服务器标签
|
||||
*/
|
||||
public static String getServerIP(int node) {
|
||||
@ -115,10 +117,12 @@ public class ProxyConfig {
|
||||
|
||||
/**
|
||||
* 获取代理类型默认端口
|
||||
*
|
||||
* @param type 类型名称
|
||||
*/
|
||||
public static Integer getTypePort(String type) {
|
||||
return typePort.get(type);
|
||||
Integer port = typePort.get(type);
|
||||
return port == null ? 0 : port;
|
||||
}
|
||||
|
||||
public static Integer getTypeIndex(String type) {
|
||||
|
@ -48,6 +48,14 @@ public class TextValidate {
|
||||
* 域名为空检查
|
||||
*/
|
||||
public static RequiredFieldValidator DomainRequired = new RequiredFieldValidator("域名不能为空!");
|
||||
/**
|
||||
* 访问密码为空检查
|
||||
*/
|
||||
public static RequiredFieldValidator p2pPwdRequired = new RequiredFieldValidator("访问密码不能为空!");
|
||||
/**
|
||||
* 访问服务名为空检查
|
||||
*/
|
||||
public static RequiredFieldValidator p2pServerNameRequired = new RequiredFieldValidator("访问服务名称不能为空!");
|
||||
/**
|
||||
* 端口为空检查
|
||||
*/
|
||||
@ -60,6 +68,18 @@ public class TextValidate {
|
||||
* IP为空检查
|
||||
*/
|
||||
public static RequiredFieldValidator IpRequired = new RequiredFieldValidator("本地IP不能为空");
|
||||
/**
|
||||
* http访问用户名格式检查
|
||||
*/
|
||||
public static final RegexValidator HttpUserFormat = new RegexValidator("用户名格式错误");
|
||||
/**
|
||||
* http访问密码格式检查
|
||||
*/
|
||||
public static final RegexValidator HttpPwdFormat = new RegexValidator("密码格式错误");
|
||||
/**
|
||||
* http访问密码格式检查
|
||||
*/
|
||||
public static final RegexValidator P2pPwdFormat = new RegexValidator("密码格式错误");
|
||||
/**
|
||||
* IP格式检查
|
||||
*/
|
||||
@ -69,7 +89,11 @@ public class TextValidate {
|
||||
EmailFormat.setRegexPattern("^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$");
|
||||
AccoountValidator.setRegexPattern("^[a-zA-Z0-9_-]*$");
|
||||
PortFormat.setRegexPattern("^([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{4}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$");
|
||||
IpFormat.setRegexPattern("^(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])$");
|
||||
IpFormat.setRegexPattern("^(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\" +
|
||||
".(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])$");
|
||||
HttpUserFormat.setRegexPattern("^[a-zA-Z0-9_-]*$");
|
||||
HttpPwdFormat.setRegexPattern("^[a-zA-Z0-9_-]*$");
|
||||
P2pPwdFormat.setRegexPattern("^[a-zA-Z0-9_-]{3,18}$");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,7 +127,7 @@ public class TextValidate {
|
||||
@Override
|
||||
protected void eval() {
|
||||
if (!DomainUtil.isCustomize(model.get())) {
|
||||
if (DomainUtil.isHttp(model)) {
|
||||
if (ProxyConfig.isHttp(model)) {
|
||||
// http / https
|
||||
boolean matches = Pattern.compile("^[a-zA-Z0-9_-]{3,18}$").matcher(model.getDomain()).matches();
|
||||
hasErrors.set(!matches);
|
||||
@ -129,7 +153,7 @@ public class TextValidate {
|
||||
@Override
|
||||
protected void eval() {
|
||||
setRegexPattern("^[a-zA-Z0-9_-]{3,18}$");
|
||||
if (DomainUtil.isHttp(model) && !DomainUtil.isCustomize(model.get())) {
|
||||
if (ProxyConfig.isHttp(model) && !DomainUtil.isCustomize(model.get())) {
|
||||
super.eval();
|
||||
} else {
|
||||
hasErrors.set(false);
|
||||
|
@ -9,6 +9,8 @@ import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
@ -76,8 +78,10 @@ public class LoginController extends BaseController<StackPane> implements Initia
|
||||
public JFXCheckBox rememberCBox;
|
||||
@FXML
|
||||
public JFXButton findpassBtn;
|
||||
@FXML
|
||||
public Label appVersionLabel;
|
||||
|
||||
private SimpleStringProperty tmppwd = new SimpleStringProperty();
|
||||
private final SimpleStringProperty tmpPwd = new SimpleStringProperty();
|
||||
|
||||
@Override
|
||||
public boolean dragWindow() {
|
||||
@ -95,6 +99,11 @@ public class LoginController extends BaseController<StackPane> implements Initia
|
||||
return "/fxml/login.fxml";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Label getAppVersionLabel() {
|
||||
return appVersionLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Button getClooseBtn() {
|
||||
return closeBtn;
|
||||
@ -116,22 +125,22 @@ public class LoginController extends BaseController<StackPane> implements Initia
|
||||
// 账号
|
||||
accountTextField.textProperty().bindBidirectional(ApplicatonStore.accountProperty());
|
||||
// 密码
|
||||
if (ApplicatonStore.isRememberMe()) tmppwd.set(ApplicatonStore.getPassword());
|
||||
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);
|
||||
passwordTextField.textProperty().bindBidirectional(tmpPwd);
|
||||
seePwdTextField.textProperty().bindBidirectional(tmpPwd);
|
||||
|
||||
}
|
||||
|
||||
// 隐藏展示密码框
|
||||
private AnchorPane pwdParent;
|
||||
|
||||
private AlertUtil.Builder findpassAlert;
|
||||
private AlertUtil.Builder<TextInputDialog> findpassAlert;
|
||||
|
||||
@Override
|
||||
public void initViewStyle() {
|
||||
@ -169,7 +178,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
|
||||
|
||||
// 注册
|
||||
registerBtn.setOnMouseClicked(event -> {
|
||||
ApplicatonStore.setPassword(tmppwd.get());
|
||||
ApplicatonStore.setPassword(tmpPwd.get());
|
||||
try {
|
||||
jumpTo(new RegisterController());
|
||||
} catch (IOException e) {
|
||||
@ -183,7 +192,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(tmppwd.get());
|
||||
(isHide ? seePwdTextField : passwordTextField).setText(tmpPwd.get());
|
||||
seePwdIcon.setIconColor(isHide ? Color.BLUE : Color.BLACK);
|
||||
});
|
||||
|
||||
@ -206,8 +215,8 @@ public class LoginController extends BaseController<StackPane> implements Initia
|
||||
|
||||
// 自动登录
|
||||
if ((ApplicatonStore.isAutoLogin() || ApplicatonStore.isRegisterSuccess()) &&
|
||||
!StringUtils.isEmpty(ApplicatonStore.getAccount()) &&
|
||||
!StringUtils.isEmpty(ApplicatonStore.getPassword())
|
||||
StringUtils.isNotEmpty(ApplicatonStore.getAccount()) &&
|
||||
StringUtils.isNotEmpty(ApplicatonStore.getPassword())
|
||||
) {
|
||||
login();
|
||||
}
|
||||
@ -250,7 +259,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
|
||||
if (pwdValidate && accountValidate)
|
||||
EasyHttp.builder()
|
||||
.api(Api.Login)
|
||||
.param(new LoginParam(accountTextField.getText(), tmppwd.get()))
|
||||
.param(new LoginParam(accountTextField.getText(), tmpPwd.get()))
|
||||
.request(new OnHttpListener<String>() {
|
||||
@Override
|
||||
public void onSucceed(String result) {
|
||||
@ -262,7 +271,7 @@ public class LoginController extends BaseController<StackPane> implements Initia
|
||||
// 登录成功
|
||||
setCsrf();
|
||||
// 记住我
|
||||
ApplicatonStore.rememberMe(tmppwd.get());
|
||||
ApplicatonStore.rememberMe(tmpPwd.get());
|
||||
// 跳转
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
|
@ -1,18 +1,25 @@
|
||||
package top.octopusyan.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jfoenix.controls.*;
|
||||
import com.jfoenix.validation.base.ValidatorBase;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -27,12 +34,8 @@ import top.octopusyan.manager.ProxyManager;
|
||||
import top.octopusyan.manager.http.request.ProxySetup;
|
||||
import top.octopusyan.model.ApplicatonStore;
|
||||
import top.octopusyan.model.ProxySetupModel;
|
||||
import top.octopusyan.utils.AlertUtil;
|
||||
import top.octopusyan.utils.DomainUtil;
|
||||
import top.octopusyan.utils.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
@ -53,50 +56,57 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
public static final String PROXY_LIST_ITEM_CLOSE_CLASS = "proxyListItem-close";
|
||||
public static final String PROXY_LIST_ITEM_SELECT_CLASS = "proxyListItem-select";
|
||||
|
||||
@FXML
|
||||
|
||||
/* 窗口通用控件 */
|
||||
public StackPane root;
|
||||
public JFXButton minimizeBtn, closeBtn;
|
||||
public Label appVersionLabel;
|
||||
|
||||
@FXML
|
||||
public JFXButton closeBtn, minimizeBtn;
|
||||
|
||||
@FXML
|
||||
public JFXButton startProxyBtn, addProxyBtn;
|
||||
@FXML
|
||||
public JFXComboBox<String> proxyProtocolComboBox;
|
||||
@FXML
|
||||
public JFXButton customizeDomainBtn;
|
||||
@FXML
|
||||
public JFXRadioButton openProxyRBtn, closeProxyRBtn;
|
||||
@FXML
|
||||
public JFXTextField domainTextField, domainSuffixTextField;
|
||||
@FXML
|
||||
public JFXTextField proxyNameTextField, localHostTextField, localPortTextField;
|
||||
@FXML
|
||||
/* 隧道设置控件 */
|
||||
public JFXComboBox<String> proxyServerComboBox;
|
||||
@FXML
|
||||
public JFXTabPane tabPane;
|
||||
@FXML
|
||||
public JFXListView<Label> proxyListView;
|
||||
@FXML
|
||||
public Label domainHtinTextField;
|
||||
@FXML
|
||||
public JFXButton resetProxyBtn;
|
||||
public JFXTextField proxyNameTextField;
|
||||
public JFXRadioButton openProxyRBtn, closeProxyRBtn;
|
||||
public JFXComboBox<String> proxyProtocolComboBox;
|
||||
public JFXRadioButton serverProviderRBtn, serverVisitorRBtn;
|
||||
public Label localHostLabel;
|
||||
public JFXTextField localHostTextField, localPortTextField;
|
||||
public HBox domainPane;
|
||||
@FXML
|
||||
public JFXButton resetProxyBtn, clearLogBtn, copyDomainBtn;
|
||||
@FXML
|
||||
public Tab proxyLogPane;
|
||||
@FXML
|
||||
public Hyperlink domainLink;
|
||||
@FXML
|
||||
public JFXTextField domainTextField, domainSuffixTextField;
|
||||
public JFXButton customizeDomainBtn;
|
||||
public HBox httpUserPane;
|
||||
public JFXTextField httpUserTextField;
|
||||
public HBox httpPwdPane;
|
||||
public JFXTextField httpPwdTextField;
|
||||
public HBox p2pRolePane, p2pPwdPane, p2pRoleView, serverNamePane;
|
||||
public JFXButton copyP2pConfig, importP2pConfig;
|
||||
public JFXTextField p2pPwdTextField, serverNameTextField;
|
||||
public JFXButton proxyStatusTip, httpUserTip, proxyTypeTip, p2pPwdTip, serverNameTip;
|
||||
public HBox proxyStatusPane;
|
||||
@FXML
|
||||
public JFXButton logoutBtn;
|
||||
public Label runingLabel;
|
||||
public Hyperlink domainLink;
|
||||
public JFXButton copyDomainBtn;
|
||||
|
||||
private final ToggleGroup openProxyGroup = new ToggleGroup();
|
||||
private final SimpleBooleanProperty customizeDomain = new SimpleBooleanProperty(false);
|
||||
private final Map<String, FrpManager> frpUtilMap = new HashMap<>();
|
||||
/* 隧道列表控件 */
|
||||
public JFXListView<Label> proxyListView;
|
||||
public JFXButton addProxyBtn;
|
||||
|
||||
/* 日志帮助面板控件 */
|
||||
public JFXTabPane tabPane;
|
||||
public Tab proxyLogPane;
|
||||
public JFXButton clearLogBtn;
|
||||
|
||||
/* 功能控件 */
|
||||
public JFXButton startProxyBtn, logoutBtn;
|
||||
|
||||
/* 自定义属性 */
|
||||
private ObservableList<Node> children;
|
||||
private final ProxySetupModel proxySetupModel = new ProxySetupModel(ProxyManager.initProxy(null));
|
||||
private final BooleanProperty customizeDomain = new SimpleBooleanProperty(false);
|
||||
private final Map<String, ProxySetup> userProxy = new HashMap<>();
|
||||
private ProxySetupModel proxySetupModel;
|
||||
private final Map<String, FrpManager> frpUtilMap = new HashMap<>();
|
||||
private final ToggleGroup openProxyGroup = new ToggleGroup();
|
||||
private final ToggleGroup serverRoleGroup = new ToggleGroup();
|
||||
private final SimpleBooleanProperty setup = new SimpleBooleanProperty(false);
|
||||
|
||||
@Override
|
||||
@ -115,6 +125,11 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
return "/fxml/main.fxml";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Label getAppVersionLabel() {
|
||||
return appVersionLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Button getClooseBtn() {
|
||||
return closeBtn;
|
||||
@ -132,18 +147,15 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
|
||||
@Override
|
||||
public void initData() {
|
||||
// 初始化视图模型
|
||||
ProxySetup proxySetup = ProxyManager.initProxy(null);
|
||||
proxySetupModel = new ProxySetupModel(proxySetup);
|
||||
// 初始化用户隧道列表
|
||||
setProxyList(Collections.singletonList(proxySetup));
|
||||
setProxyList(Collections.singletonList(proxySetupModel.get()));
|
||||
// 重置隧道列表视图
|
||||
initProxyListView();
|
||||
|
||||
// 隧道类型
|
||||
// 初始化隧道类型
|
||||
Arrays.asList(ProxyType.values()).forEach((type) -> proxyProtocolComboBox.getItems().add(type.name()));
|
||||
|
||||
// 服务器
|
||||
// 初始化服务器
|
||||
Arrays.asList(ProxyServer.values()).forEach((server) -> proxyServerComboBox.getItems().add(server.getServerName()));
|
||||
|
||||
// 获取用户隧道列表
|
||||
@ -185,7 +197,6 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/* 数据绑定 */
|
||||
|
||||
// 服务器
|
||||
@ -204,12 +215,27 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
domainSuffixTextField.textProperty().bindBidirectional(proxySetupModel.domainSuffixProperty());
|
||||
// 是否自定义访问域名
|
||||
customizeDomain.bindBidirectional(proxySetupModel.isCustomizeProperty());
|
||||
// http访问用户名
|
||||
httpUserTextField.textProperty().bindBidirectional(proxySetupModel.httpUserProperty());
|
||||
// http访问密码
|
||||
httpPwdTextField.textProperty().bindBidirectional(proxySetupModel.httpPwdProperty());
|
||||
// p2p访问密码
|
||||
p2pPwdTextField.textProperty().bindBidirectional(proxySetupModel.skProperty());
|
||||
// p2p访问服务提示
|
||||
serverNameTextField.textProperty().bindBidirectional(proxySetupModel.serverNameProperty());
|
||||
// 启动成功提示
|
||||
proxyStatusPane.visibleProperty().bindBidirectional(proxySetupModel.runningProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initViewStyle() {
|
||||
children = ((Pane) domainPane.getParent()).getChildren();
|
||||
|
||||
// 默认隧道协议为http
|
||||
children.remove(p2pRolePane);
|
||||
children.remove(p2pPwdPane);
|
||||
children.remove(serverNamePane);
|
||||
|
||||
// 设置列表
|
||||
proxyListView.getSelectionModel().select(0);
|
||||
// 启用链接
|
||||
@ -219,30 +245,144 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
closeProxyRBtn.setUserData(false);
|
||||
openProxyRBtn.setSelected(true);
|
||||
|
||||
// 点对点服务角色
|
||||
serverProviderRBtn.setToggleGroup(serverRoleGroup);
|
||||
serverVisitorRBtn.setToggleGroup(serverRoleGroup);
|
||||
serverProviderRBtn.setUserData(true);
|
||||
serverVisitorRBtn.setUserData(false);
|
||||
serverProviderRBtn.setSelected(true);
|
||||
|
||||
// 是否启用
|
||||
proxySetupModel.statusProperty().addListener((observable, oldValue, newValue) -> {
|
||||
openProxyRBtn.setSelected(newValue);
|
||||
closeProxyRBtn.setSelected(!newValue);
|
||||
closeProxy(!newValue);
|
||||
});
|
||||
|
||||
// 隧道类型监听
|
||||
proxySetupModel.proxyTypeProperty().addListener((observable, oldValue, newValue) -> {
|
||||
String newType = StringUtils.lowerCase(newValue);
|
||||
String port, host;
|
||||
if (!newType.equals(userProxy.get(proxySetupModel.getId()))) {
|
||||
if (!newType.equals(StringUtils.upperCase(userProxy.get(proxySetupModel.getId()).getProxy_type()))) {
|
||||
host = "127.0.0.1";
|
||||
port = ProxyConfig.getTypePort(newType).toString();
|
||||
} else {
|
||||
host = proxySetupModel.getLocalIp();
|
||||
port = proxySetupModel.getLocalPort();
|
||||
}
|
||||
// 域名提示
|
||||
domainPane.setVisible(DomainUtil.isHttp(proxySetupModel));
|
||||
// 隧道类型相关控件显示
|
||||
boolean isHttp = ProxyConfig.isHttp(proxySetupModel);
|
||||
boolean isP2p = ProxyConfig.isP2p(proxySetupModel);
|
||||
if (isHttp) {
|
||||
// http / https
|
||||
children.remove(p2pRolePane);
|
||||
children.remove(p2pPwdPane);
|
||||
children.remove(serverNamePane);
|
||||
if (!children.contains(domainPane)) {
|
||||
int index = children.size() - 1;
|
||||
children.add(index, httpPwdPane);
|
||||
children.add(index, httpUserPane);
|
||||
children.add(index, domainPane);
|
||||
}
|
||||
// 添加验证
|
||||
ObservableList<ValidatorBase> httpValid = domainTextField.getValidators();
|
||||
if (!httpValid.contains(TextValidate.DomainRequired)) {
|
||||
httpValid.add(TextValidate.DomainRequired);// 域名为空检查
|
||||
httpValid.add(TextValidate.domainFormatValidator(proxySetupModel));// 域名格式检查
|
||||
httpValid.add(TextValidate.domainLengthValidator(proxySetupModel));// 域名长度检查
|
||||
httpValid.add(TextValidate.domainAddressValidator(proxySetupModel));// 自定义域名解析检查
|
||||
}
|
||||
p2pPwdTextField.getValidators().clear();
|
||||
// 默认系统分配域名
|
||||
ProxySetup proxySetup = proxySetupModel.get();
|
||||
if (proxySetup.getDomain().equals("")) {
|
||||
proxySetup.setDomain("." + ProxyConfig.getServerPath(proxySetupModel.getServer()));
|
||||
proxySetupModel.set(proxySetup);
|
||||
}
|
||||
} else if (isP2p) {
|
||||
// stcp / xtcp
|
||||
children.remove(httpPwdPane);
|
||||
children.remove(httpUserPane);
|
||||
children.remove(domainPane);
|
||||
int index = children.size() - 1;
|
||||
if (!children.contains(p2pRolePane)) {
|
||||
children.add(index, p2pPwdPane);
|
||||
children.add(index - 1, p2pRolePane);
|
||||
if(proxySetupModel.isProvider != null && !proxySetupModel.isProvider){
|
||||
children.add(index, serverNamePane);
|
||||
}
|
||||
}
|
||||
Boolean provider = proxySetupModel.isProvider();
|
||||
importP2pConfig.setVisible(provider != null && !provider);
|
||||
copyP2pConfig.setVisible(provider == null || provider);
|
||||
// 添加验证
|
||||
ObservableList<ValidatorBase> p2pValid = p2pPwdTextField.getValidators();
|
||||
if (!p2pValid.contains(TextValidate.p2pPwdRequired)) {
|
||||
p2pValid.add(TextValidate.p2pPwdRequired);
|
||||
p2pValid.add(TextValidate.p2pServerNameRequired);
|
||||
}
|
||||
|
||||
// 设置
|
||||
domainTextField.getValidators().clear();
|
||||
} else {
|
||||
// tcp / udp
|
||||
children.remove(p2pRolePane);
|
||||
children.remove(p2pPwdPane);
|
||||
children.remove(serverNamePane);
|
||||
children.remove(httpPwdPane);
|
||||
children.remove(httpUserPane);
|
||||
children.remove(domainPane);
|
||||
|
||||
// 验证
|
||||
p2pPwdTextField.getValidators().clear();
|
||||
domainTextField.getValidators().clear();
|
||||
}
|
||||
|
||||
// 设置本地端口
|
||||
localHostTextField.setText(host);
|
||||
localPortTextField.setText(port);
|
||||
});
|
||||
|
||||
// 服务角色监听
|
||||
proxySetupModel.serverNameProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue == null) {
|
||||
serverProviderRBtn.setSelected(false);
|
||||
serverVisitorRBtn.setSelected(false);
|
||||
proxySetupModel.isProvider = null;
|
||||
} else if (oldValue == null) {
|
||||
if (proxySetupModel.isProvider == null) {
|
||||
proxySetupModel.isProvider = proxySetupModel.isProvider();
|
||||
}
|
||||
serverProviderRBtn.setSelected(proxySetupModel.isProvider);
|
||||
serverVisitorRBtn.setSelected(!proxySetupModel.isProvider);
|
||||
}
|
||||
});
|
||||
serverRoleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue == null) return;
|
||||
proxySetupModel.isProvider = (boolean) newValue.getUserData();
|
||||
if (proxySetupModel.isProvider) {
|
||||
localHostLabel.setText("内网服务地址");
|
||||
localHostTextField.setPromptText("内网服务IP (IPv4)");
|
||||
localPortTextField.setPromptText("端口");
|
||||
copyP2pConfig.setVisible(true);
|
||||
importP2pConfig.setVisible(false);
|
||||
children.remove(serverNamePane);
|
||||
proxySetupModel.setServerName("提供者");
|
||||
} else {
|
||||
localHostLabel.setText("服务绑定地址");
|
||||
localHostTextField.setPromptText("服务绑定IP (IPv4)");
|
||||
localPortTextField.setPromptText("端口");
|
||||
copyP2pConfig.setVisible(false);
|
||||
importP2pConfig.setVisible(true);
|
||||
if (!children.contains(serverNamePane)) children.add(children.size() - 3, serverNamePane);
|
||||
proxySetupModel.setServerName("");
|
||||
}
|
||||
ObservableList<Node> children = p2pRolePane.getChildren();
|
||||
JFXButton remove = proxySetupModel.isProvider ? importP2pConfig : copyP2pConfig;
|
||||
JFXButton add = proxySetupModel.isProvider ? copyP2pConfig : importP2pConfig;
|
||||
if (children.contains(add)) children.add(add);
|
||||
children.remove(remove);
|
||||
});
|
||||
|
||||
// 隧道名称
|
||||
proxySetupModel.proxyNameProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (proxyListView.getItems().size() > 0)
|
||||
@ -267,38 +407,20 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
setDomainLink();
|
||||
});
|
||||
|
||||
// 自定义外网访问地址按钮
|
||||
// 外网访问地址
|
||||
customizeDomain.addListener((observable, oldValue, newValue) -> {
|
||||
AnchorPane parent = (AnchorPane) domainTextField.getParent();
|
||||
// 是否切换为自定义域名
|
||||
if (newValue) {
|
||||
// 隐藏系统域名
|
||||
parent.getChildren().remove(domainSuffixTextField);
|
||||
// 用户域名是否自定义
|
||||
if (!DomainUtil.isCustomize(proxySetupModel.get())) {
|
||||
proxySetupModel.setDomain("");
|
||||
} else {
|
||||
proxySetupModel.setDomain(proxySetupModel.getDomain());
|
||||
}
|
||||
proxySetupModel.setDomainSuffix("");
|
||||
domainTextField.promptTextProperty().set("自定义域名");
|
||||
customizeDomainBtn.setText("系统分配");
|
||||
domainHtinTextField.setText("请输入您的域名,并解析至: " + ProxyConfig.getServerIP(proxySetupModel.getServer()));
|
||||
} else {
|
||||
// 显示系统域名
|
||||
if (!parent.getChildren().contains(domainSuffixTextField))
|
||||
parent.getChildren().add(domainSuffixTextField);
|
||||
// 用户域名是否自定义
|
||||
if (DomainUtil.isCustomize(proxySetupModel.get())) {
|
||||
proxySetupModel.setDomain("");
|
||||
proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(proxySetupModel.getServer()));
|
||||
} else {
|
||||
proxySetupModel.setDomain(DomainUtil.getCustomize(proxySetupModel.get()));
|
||||
proxySetupModel.setDomainSuffix(DomainUtil.getSuffix(userProxy.get(proxySetupModel.getId())));
|
||||
}
|
||||
domainTextField.promptTextProperty().set("自定义子域名 大于3位");
|
||||
customizeDomainBtn.setText("自定义");
|
||||
domainHtinTextField.setText("请输入子域名,长度不小于3个字符");
|
||||
}
|
||||
});
|
||||
|
||||
@ -311,10 +433,19 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
*/
|
||||
private void setDomainLink() {
|
||||
// 外网访问连接
|
||||
if (DomainUtil.isHttp(proxySetupModel)) {
|
||||
if (ProxyConfig.isHttp(proxySetupModel)) {
|
||||
runingLabel.setText("启动成功!立即访问 ");
|
||||
domainLink.setVisible(true);
|
||||
copyDomainBtn.setVisible(true);
|
||||
// http / https
|
||||
String prefix = proxySetupModel.getProxyType() + "://";
|
||||
domainLink.textProperty().set(prefix + proxySetupModel.getDomain() + proxySetupModel.getDomainSuffix());
|
||||
} else if (ProxyConfig.isP2p(proxySetupModel)) {
|
||||
// stcp / xtcp
|
||||
runingLabel.setText("启动成功!");
|
||||
domainLink.setText("");
|
||||
domainLink.setVisible(false);
|
||||
copyDomainBtn.setVisible(false);
|
||||
} else {
|
||||
// ssh / tcp
|
||||
domainLink.textProperty().set(ProxyConfig.getServerIP(ProxyConfig.getServerNode(proxySetupModel.getServer())) + ":" + proxySetupModel.getRemotePort());
|
||||
@ -336,20 +467,22 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
// 日志清理
|
||||
clearLogBtn.setOnMouseClicked(event -> {
|
||||
ProxySetup proxySetup = userProxy.get(proxySetupModel.getId());
|
||||
if (frpUtilMap.get(proxySetup.getId()) != null)
|
||||
frpUtilMap.get(proxySetup.getId()).clearLog();
|
||||
if (frpUtilMap.get(proxySetup.getId().toString()) != null)
|
||||
frpUtilMap.get(proxySetup.getId().toString()).clearLog();
|
||||
});
|
||||
|
||||
// 更换服务器地址
|
||||
proxyServerComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
// 重新设置是否自定义
|
||||
proxySetupModel.set(proxySetupModel.get());
|
||||
if (!customizeDomain.get()) {
|
||||
boolean customize = DomainUtil.isCustomize(proxySetupModel.get());
|
||||
if (!DomainUtil.isCustomize(proxySetupModel.get(), oldValue) || !customize) {
|
||||
proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(newValue));
|
||||
customizeDomain.set(false);
|
||||
} else {
|
||||
proxySetupModel.setDomainSuffix("");
|
||||
// 域名检查
|
||||
domainTextField.validate();
|
||||
customizeDomain.set(true);
|
||||
}
|
||||
});
|
||||
|
||||
@ -365,7 +498,28 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
);
|
||||
|
||||
// 自定义访问域名
|
||||
customizeDomainBtn.setOnMouseClicked((event) -> customizeDomain.set(!customizeDomain.get()));
|
||||
customizeDomainBtn.setOnMouseClicked((event) -> {
|
||||
// 是否切换为自定义域名
|
||||
if (!customizeDomain.get()) {
|
||||
// 用户域名是否自定义
|
||||
if (!DomainUtil.isCustomize(proxySetupModel.get())) {
|
||||
proxySetupModel.setDomain("");
|
||||
} else {
|
||||
proxySetupModel.setDomain(proxySetupModel.getDomain());
|
||||
}
|
||||
proxySetupModel.setDomainSuffix("");
|
||||
} else {
|
||||
// 用户域名是否自定义
|
||||
if (DomainUtil.isCustomize(proxySetupModel.get())) {
|
||||
proxySetupModel.setDomain("");
|
||||
proxySetupModel.setDomainSuffix("." + ProxyConfig.getServerPath(proxySetupModel.getServer()));
|
||||
} else {
|
||||
proxySetupModel.setDomain(DomainUtil.getCustomize(proxySetupModel.get()));
|
||||
proxySetupModel.setDomainSuffix(DomainUtil.getSuffix(userProxy.get(proxySetupModel.getId())));
|
||||
}
|
||||
}
|
||||
customizeDomain.set(!customizeDomain.get());
|
||||
});
|
||||
|
||||
// 本地IP检查
|
||||
localHostTextField.getValidators().add(TextValidate.IpRequired);
|
||||
@ -380,15 +534,60 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
// 域名检查
|
||||
setup.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) domainTextField.validate();
|
||||
if (newValue) p2pPwdTextField.validate();
|
||||
});
|
||||
domainTextField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!domainTextField.getValidators().contains(TextValidate.DomainRequired)) {
|
||||
domainTextField.getValidators().add(TextValidate.DomainRequired);// 域名为空检查
|
||||
domainTextField.getValidators().add(TextValidate.domainFormatValidator(proxySetupModel));// 域名格式检查
|
||||
domainTextField.getValidators().add(TextValidate.domainLengthValidator(proxySetupModel));// 域名长度检查
|
||||
domainTextField.getValidators().add(TextValidate.domainAddressValidator(proxySetupModel));// 自定义域名解析检查
|
||||
|
||||
// 复制服务设置
|
||||
copyP2pConfig.setOnMouseClicked(event -> {
|
||||
if (!localPortTextField.validate() || !localPortTextField.validate() || !p2pPwdTextField.validate())
|
||||
return;
|
||||
|
||||
ProxySetup setup = proxySetupModel.get();
|
||||
setup.setServer_name(EncryptionUtil.MD5_16(ApplicatonStore.getAccount() + "_" + setup.getSort()));
|
||||
JSONObject json = JSON.parseObject(JSON.toJSONString(setup));
|
||||
|
||||
try {
|
||||
String serverConfig = EncryptionUtil.DESencode(json.toJSONString(), EncryptionUtil.getDefaultPassword());
|
||||
// 设置剪切板
|
||||
ClipUtil.setClip(FrpManager.serverConfigHeader + serverConfig);
|
||||
AlertUtil.info("复制成功").show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
// 导入设置
|
||||
importP2pConfig.setOnMouseClicked(event -> {
|
||||
try {
|
||||
String clipData = ClipUtil.getString();
|
||||
// 服务设置检验
|
||||
if (clipData == null || !clipData.contains(FrpManager.serverConfigHeader)) throw new Exception();
|
||||
// 解密服务设置
|
||||
clipData = clipData.replace(FrpManager.serverConfigHeader, "");
|
||||
String jsonStr = EncryptionUtil.DESdecode(clipData, EncryptionUtil.getDefaultPassword());
|
||||
JSONObject jsonObject = JSON.parseObject(jsonStr);
|
||||
proxySetupModel.set(jsonObject.toJavaObject(ProxySetup.class));
|
||||
AlertUtil.info("导入成功").show();
|
||||
} catch (Exception e) {
|
||||
AlertUtil.error("导入失败,请重新复制服务设置").show();
|
||||
}
|
||||
});
|
||||
|
||||
// 添加验证
|
||||
ObservableList<ValidatorBase> httpValid = domainTextField.getValidators();
|
||||
httpValid.add(TextValidate.DomainRequired);// 域名为空检查
|
||||
httpValid.add(TextValidate.domainFormatValidator(proxySetupModel));// 域名格式检查
|
||||
httpValid.add(TextValidate.domainLengthValidator(proxySetupModel));// 域名长度检查
|
||||
httpValid.add(TextValidate.domainAddressValidator(proxySetupModel));// 自定义域名解析检查
|
||||
domainTextField.textProperty().addListener((observable, oldValue, newValue) -> domainTextField.validate());
|
||||
p2pPwdTextField.textProperty().addListener((observable, oldValue, newValue) -> p2pPwdTextField.validate());
|
||||
|
||||
// 访问密码检查
|
||||
p2pPwdTextField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!p2pPwdTextField.getValidators().contains(TextValidate.p2pPwdRequired)) {
|
||||
p2pPwdTextField.getValidators().add(TextValidate.p2pPwdRequired); // 访问密码为空检查
|
||||
p2pPwdTextField.getValidators().add(TextValidate.HttpPwdFormat); // 访问密码格式检查
|
||||
} else
|
||||
domainTextField.validate();
|
||||
p2pPwdTextField.validate();
|
||||
});
|
||||
|
||||
// 点击隧道列表
|
||||
@ -446,12 +645,8 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
|
||||
// 复制外网访问地址
|
||||
copyDomainBtn.setOnMouseClicked(event -> {
|
||||
//获取系统剪切板
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
//构建String数据类型
|
||||
StringSelection selection = new StringSelection(domainLink.getText());
|
||||
//添加文本到系统剪切板
|
||||
clipboard.setContents(selection, null);
|
||||
// 设置剪切板
|
||||
ClipUtil.setClip(domainLink.getText());
|
||||
// 复制成功提示
|
||||
AlertUtil.info("复制成功,快去分享给小伙伴吧!").show();
|
||||
});
|
||||
@ -468,15 +663,37 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
// 隧道类型提示
|
||||
proxyTypeTip.setOnMouseClicked(event -> {
|
||||
TooltipUtil util = getTooltipUtil();
|
||||
if (util.isShowing()) util.hide();
|
||||
else util.showProxyTypeTip(event);
|
||||
});
|
||||
// http访问用户名提示
|
||||
httpUserTip.setOnMouseClicked(event -> {
|
||||
TooltipUtil util = getTooltipUtil();
|
||||
if (util.isShowing()) util.hide();
|
||||
else util.showHttpUserTip(event);
|
||||
});
|
||||
// 隧道启用提示
|
||||
proxyStatusTip.setOnMouseClicked(event -> {
|
||||
TooltipUtil util = getTooltipUtil();
|
||||
if (util.isShowing()) util.hide();
|
||||
else util.showProxyStatusTip(event);
|
||||
});
|
||||
// p2p 访问密码提示
|
||||
p2pPwdTip.setOnMouseClicked(event -> {
|
||||
TooltipUtil util = getTooltipUtil();
|
||||
if (util.isShowing()) util.hide();
|
||||
else util.showP2pPwdTip(event);
|
||||
});
|
||||
// p2p 访问服务提示
|
||||
serverNameTip.setOnMouseClicked(event -> {
|
||||
TooltipUtil util = getTooltipUtil();
|
||||
if (util.isShowing()) util.hide();
|
||||
else util.showP2pServerName(event);
|
||||
});
|
||||
}
|
||||
//
|
||||
// private void initEmptyProxyListView() {
|
||||
// // 初始化视图模型
|
||||
// proxySetup = ProxyManager.initProxy(null);
|
||||
// proxySetupModel = new ProxySetupModel(proxySetup);
|
||||
// setProxyList(Collections.singletonList(proxySetup));
|
||||
// initProxyListView();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 是否启用当前隧道
|
||||
@ -489,7 +706,11 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
localHostTextField.setDisable(close); // 本地地址
|
||||
localPortTextField.setDisable(close); // 本地端口
|
||||
domainTextField.setDisable(close); // 外网子域名
|
||||
httpUserTextField.setDisable(close); // 外网子域名
|
||||
httpPwdTextField.setDisable(close); // 外网子域名
|
||||
startProxyBtn.setDisable(close); // 启动按钮
|
||||
p2pRoleView.setDisable(close); // p2p角色
|
||||
p2pPwdTextField.setDisable(close); // p2p访问面板
|
||||
|
||||
ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass();
|
||||
styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS);
|
||||
@ -518,6 +739,7 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
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 (selectProxy() == index) styleClass.add(PROXY_LIST_ITEM_SELECT_CLASS);
|
||||
if (index < items.size()) {
|
||||
items.set(index, label);
|
||||
} else {
|
||||
@ -532,16 +754,32 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
* 启动代理
|
||||
*/
|
||||
private void startProxy() {
|
||||
// http访问用户名密码输入验证
|
||||
if (httpUserPane.isVisible() && StringUtils.isNotEmpty(httpUserTextField.getText())) {
|
||||
httpUserTextField.getValidators().add(TextValidate.HttpUserFormat);
|
||||
httpPwdTextField.getValidators().add(TextValidate.HttpPwdFormat);
|
||||
} else {
|
||||
httpUserTextField.getValidators().remove(TextValidate.HttpUserFormat);
|
||||
httpPwdTextField.getValidators().remove(TextValidate.HttpPwdFormat);
|
||||
}
|
||||
|
||||
// 验证
|
||||
if (!domainTextField.validate() || !localHostTextField.validate() || !localPortTextField.validate())
|
||||
return;
|
||||
if (!domainTextField.validate() || !localHostTextField.validate() || !localPortTextField.validate() ||
|
||||
!httpUserTextField.validate() || !httpPwdTextField.validate()
|
||||
) return;
|
||||
|
||||
// 非 http 隧道系统随机分配端口
|
||||
if (!DomainUtil.isHttp(proxySetupModel)) {
|
||||
// 非 http 隧道系统随机分配远程端口
|
||||
if (!ProxyConfig.isHttp(proxySetupModel) && !ProxyConfig.isP2p(proxySetupModel)) {
|
||||
// 远程端口为空
|
||||
if (StringUtils.isEmpty(proxySetupModel.getRemotePort()))
|
||||
proxySetupModel.setRemotePort(String.valueOf(ProxyManager.randomPort()));
|
||||
} else {
|
||||
proxySetupModel.setRemotePort(null);
|
||||
}
|
||||
|
||||
// 清空非隧道类型属性
|
||||
clearModel();
|
||||
|
||||
// 是否有修改
|
||||
if (!proxySetupModel.get().equals(userProxy.get(proxySetupModel.getId()))) {
|
||||
// 添加隧道
|
||||
@ -564,6 +802,33 @@ public class MainController extends BaseController<StackPane> implements Initial
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空无效属性
|
||||
*/
|
||||
private void clearModel() {
|
||||
boolean http = ProxyConfig.isHttp(proxySetupModel);
|
||||
boolean p2p = ProxyConfig.isP2p(proxySetupModel);
|
||||
if (http) {
|
||||
proxySetupModel.setSk(null);
|
||||
proxySetupModel.setServerName(null);
|
||||
proxySetupModel.setRemotePort("");
|
||||
} else if (p2p) {
|
||||
proxySetupModel.setHttpPwd(null);
|
||||
proxySetupModel.setHttpUser(null);
|
||||
proxySetupModel.setDomain("");
|
||||
proxySetupModel.setDomainSuffix("");
|
||||
proxySetupModel.setRemotePort("");
|
||||
} else {
|
||||
// tcp / udp
|
||||
proxySetupModel.setSk(null);
|
||||
proxySetupModel.setServerName(null);
|
||||
proxySetupModel.setHttpPwd(null);
|
||||
proxySetupModel.setHttpUser(null);
|
||||
proxySetupModel.setDomain("");
|
||||
proxySetupModel.setDomainSuffix("");
|
||||
}
|
||||
}
|
||||
|
||||
private void start(ProxySetup setup) {
|
||||
// 添加成功,设置运行状态
|
||||
setup.setRuning(frpUtilMap.containsKey(setup.getId().toString()));
|
||||
|
@ -6,6 +6,7 @@ import com.jfoenix.controls.JFXTextField;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -44,6 +45,8 @@ public class RegisterController extends BaseController<StackPane> implements Ser
|
||||
public JFXPasswordField passwordTextField;
|
||||
@FXML
|
||||
public JFXButton loginBtn;
|
||||
@FXML
|
||||
public Label appVersionLabel;
|
||||
|
||||
@Override
|
||||
public boolean dragWindow() {
|
||||
@ -61,6 +64,11 @@ public class RegisterController extends BaseController<StackPane> implements Ser
|
||||
return "/fxml/register.fxml";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Label getAppVersionLabel() {
|
||||
return appVersionLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Button getClooseBtn() {
|
||||
return closeBtn;
|
||||
@ -81,8 +89,8 @@ public class RegisterController extends BaseController<StackPane> implements Ser
|
||||
String account = ApplicatonStore.getAccount();
|
||||
String password = ApplicatonStore.getPassword();
|
||||
|
||||
if (!StringUtils.isEmpty(account)) accooundTextField.setText(account);
|
||||
if (!StringUtils.isEmpty(password)) passwordTextField.setText(password);
|
||||
if (StringUtils.isNotEmpty(account)) accooundTextField.setText(account);
|
||||
if (StringUtils.isNotEmpty(password)) passwordTextField.setText(password);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -11,6 +11,7 @@ import top.octopusyan.http.model.ResponseClass;
|
||||
import top.octopusyan.manager.http.Api;
|
||||
import top.octopusyan.model.ApplicatonStore;
|
||||
import top.octopusyan.model.ProxySetupModel;
|
||||
import top.octopusyan.utils.EncryptionUtil;
|
||||
import top.octopusyan.utils.JsoupUtil;
|
||||
|
||||
import java.io.*;
|
||||
@ -24,11 +25,12 @@ import java.util.*;
|
||||
* @create : 2022-4-7 23:19
|
||||
*/
|
||||
public class FrpManager {
|
||||
public static final String serverConfigHeader = "yanfrp://";
|
||||
public static List<FrpManager> frpcList = new ArrayList<>();
|
||||
private static final String YAN_FRP_TEMP_DIR_NAME = "frpclienttmp";
|
||||
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 FRPC_CONF_PREFIX_NAME = "proxy_";
|
||||
private static final String FRPC_CONF_SUFFIX_NAME = ".ini";
|
||||
private static final Map<Integer, String> serverConfigraution = new HashMap<>(3);
|
||||
/**
|
||||
* 应用临时目录 地址
|
||||
@ -154,27 +156,66 @@ public class FrpManager {
|
||||
|
||||
public static String getProxyFrpConfig(ProxySetupModel setup) {
|
||||
String n = "\n";
|
||||
boolean ishttp = setup.getProxyType().contains("http");
|
||||
boolean isp2p = "xtcp".equals(setup.getProxyType()) || "stcp".equals(setup.getProxyType());
|
||||
// 基础配置
|
||||
StringBuilder stringBuilder = new StringBuilder("[" + ApplicatonStore.getAccount() + "_" + setup.getSort() + "]\n");
|
||||
StringBuilder stringBuilder = new StringBuilder("[");
|
||||
// 服务名称
|
||||
StringBuilder serverName = new StringBuilder();
|
||||
// p2p 服务名
|
||||
if (isp2p) {
|
||||
if (setup.isProvider()) {
|
||||
// 提供者
|
||||
serverName.append(EncryptionUtil.MD5_16(serverName.toString()));
|
||||
} else {
|
||||
// 访问者
|
||||
serverName.append(setup.getServerName()).append("_visitor");
|
||||
}
|
||||
} else {
|
||||
serverName.append(ApplicatonStore.getAccount()).append("_").append(setup.getSort());
|
||||
}
|
||||
stringBuilder.append(serverName).append("]\n");
|
||||
stringBuilder.append("privilege_mode = true\n")
|
||||
.append("type = ").append(setup.getProxyType().contains("http") ? "http" : setup.getProxyType()).append(n)
|
||||
.append("type = ").append(setup.getProxyType().contains("http") ? "http" : setup.getProxyType()).append(n);
|
||||
|
||||
if (ishttp) {
|
||||
// HTTP / HTTPS
|
||||
stringBuilder.append("custom_domains = ").append(setup.getDomain()).append(setup.getDomainSuffix()).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
|
||||
stringBuilder.append("custom_domains = ").append(setup.getDomain()).append(setup.getDomainSuffix()).append(n);
|
||||
if (!StringUtils.isEmpty(setup.getLocations()))
|
||||
if (StringUtils.isNotEmpty(setup.getLocations()))
|
||||
stringBuilder.append("locations = ").append(setup.getLocations()).append(n);
|
||||
if (!StringUtils.isEmpty(setup.getLocations()))
|
||||
|
||||
if (StringUtils.isNotEmpty(setup.getHttpUser()))
|
||||
stringBuilder.append("http_user = ").append(setup.getHttpUser()).append(n);
|
||||
|
||||
if (StringUtils.isNotEmpty(setup.getHttpPwd()))
|
||||
stringBuilder.append("http_pwd = ").append(setup.getHttpPwd()).append(n);
|
||||
|
||||
if (StringUtils.isNotEmpty(setup.getHostHeaderRewrite()))
|
||||
stringBuilder.append("host_header_rewrite = ").append(setup.getHostHeaderRewrite()).append(n);
|
||||
if (!StringUtils.isEmpty(setup.getLocations()))
|
||||
|
||||
if (StringUtils.isNotEmpty(setup.getHeader_X_From_Where()))
|
||||
stringBuilder.append("header_X-From-Where = ").append(setup.getHeader_X_From_Where()).append(n);
|
||||
|
||||
} else if (isp2p) {
|
||||
// xtcp / stcp
|
||||
if (!setup.isProvider()) {
|
||||
stringBuilder.append("role = visitor").append(n);
|
||||
stringBuilder.append("server_name = ").append(setup.getServerName()).append(n);
|
||||
stringBuilder.append("bind_addr = ").append(setup.getLocalIp()).append(n);
|
||||
stringBuilder.append("bind_port = ").append(setup.getLocalPort()).append(n);
|
||||
} else {
|
||||
// TCP / UDP / XTCP / STCP
|
||||
stringBuilder.append("remote_port = ").append(setup.getRemotePort()).append(n);
|
||||
if (!StringUtils.isEmpty(setup.getSk()))
|
||||
stringBuilder.append("local_ip = ").append(setup.getLocalIp()).append(n);
|
||||
stringBuilder.append("local_port = ").append(setup.getLocalPort()).append(n);
|
||||
}
|
||||
stringBuilder.append("sk = ").append(setup.getSk()).append(n);
|
||||
} else {
|
||||
// TCP / UDP
|
||||
stringBuilder.append("local_ip = ").append(setup.getLocalIp()).append(n)
|
||||
.append("local_port = ").append(setup.getLocalPort()).append(n).append(n)
|
||||
.append("remote_port = ").append(setup.getRemotePort()).append(n);
|
||||
}
|
||||
// 压缩和加密
|
||||
stringBuilder.append("use_encryption = ").append(setup.isUseEncryption()).append(n)
|
||||
|
@ -28,6 +28,10 @@ import java.util.stream.Collectors;
|
||||
public class ProxyManager {
|
||||
private static String csrf;
|
||||
|
||||
|
||||
private static final String Null = "无";
|
||||
private static final String isOpen = "启用";
|
||||
|
||||
/**
|
||||
* 初始化隧道设置
|
||||
*/
|
||||
@ -48,6 +52,11 @@ public class ProxyManager {
|
||||
return setup;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除隧道
|
||||
*
|
||||
* @param id 隧道ID
|
||||
*/
|
||||
public static void delete(Integer id) {
|
||||
EasyHttp.builder()
|
||||
.api(Api.DeleteProxy())
|
||||
@ -92,7 +101,11 @@ public class ProxyManager {
|
||||
String useEnc = select.get(6).text();
|
||||
String useCom = select.get(7).text();
|
||||
String domain = select.get(8).text();
|
||||
String sk = select.get(11).text();
|
||||
String status = select.get(13).text();
|
||||
String httpUser = select.get(15).text();
|
||||
String httpPwd = select.get(16).text();
|
||||
String serverName = select.get(17).text();
|
||||
|
||||
for (ProxyServer value : ProxyServer.values()) {
|
||||
String name = value.getServerName();
|
||||
@ -101,11 +114,15 @@ public class ProxyManager {
|
||||
}
|
||||
setup.setLocal_ip(localIp);
|
||||
setup.setLocal_port(Integer.parseInt(localPort));
|
||||
setup.setRemote_port("无".equals(webPort) ? "" : webPort);
|
||||
setup.setDomain(domain);
|
||||
setup.setUse_compression(useCom.equals("启用"));
|
||||
setup.setUse_encryption(useEnc.equals("启用"));
|
||||
setup.setStatus(status.equals("启用"));
|
||||
setup.setRemote_port(Null.equals(webPort) ? "" : webPort);
|
||||
setup.setDomain(Null.equals(domain) ? "" : domain);
|
||||
setup.setUse_compression(useCom.equals(isOpen));
|
||||
setup.setUse_encryption(useEnc.equals(isOpen));
|
||||
setup.setStatus(status.equals(isOpen));
|
||||
setup.setSk(Null.equals(sk) ? null : sk);
|
||||
setup.setHttp_user(Null.equals(httpUser) ? null : httpUser);
|
||||
setup.setHttp_pwd(Null.equals(httpPwd) ? null : httpPwd);
|
||||
setup.setServer_name(Null.equals(serverName) ? null : serverName);
|
||||
|
||||
return setup;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package top.octopusyan.manager.http.request;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
@ -22,6 +23,8 @@ public class ProxySetup {
|
||||
private Integer local_port;
|
||||
private String remote_port;
|
||||
private String domain;
|
||||
private String http_user;
|
||||
private String http_pwd;
|
||||
private Boolean use_encryption;
|
||||
private Boolean use_compression;
|
||||
private String locations;
|
||||
@ -29,9 +32,13 @@ public class ProxySetup {
|
||||
private String header_X_From_Where;
|
||||
private String sk;
|
||||
private Integer sort;
|
||||
// null:不是p2p, 空字符:提供者
|
||||
private String server_name;
|
||||
|
||||
// 非保留数据
|
||||
/** 是否启用 */
|
||||
@EqualsAndHashCode.Exclude
|
||||
private Boolean status;
|
||||
@EqualsAndHashCode.Exclude
|
||||
private boolean runing;
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ import java.util.List;
|
||||
* @create : 2022-4-4 17:22
|
||||
*/
|
||||
public class ApplicatonStore {
|
||||
public static final String APP_NAME = "YanFrp";
|
||||
public static final String APP_VERSION = "1.0.4";
|
||||
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);
|
||||
@ -155,7 +157,8 @@ public class ApplicatonStore {
|
||||
// 记住
|
||||
if (appDataJson.containsKey(REMEMBER_ME_KEY)) rememberMe.set(appDataJson.getBoolean(REMEMBER_ME_KEY));
|
||||
// 已选择隧道
|
||||
if (appDataJson.containsKey(SELECT_PROXY_NAME_KEY)) selectProxyName = appDataJson.getString(SELECT_PROXY_NAME_KEY);
|
||||
if (appDataJson.containsKey(SELECT_PROXY_NAME_KEY))
|
||||
selectProxyName = appDataJson.getString(SELECT_PROXY_NAME_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,9 +31,13 @@ public class ProxySetupModel {
|
||||
private final SimpleStringProperty header_X_From_Where = new SimpleStringProperty();
|
||||
private final SimpleStringProperty sk = new SimpleStringProperty();
|
||||
private final SimpleBooleanProperty status = new SimpleBooleanProperty();
|
||||
private final SimpleStringProperty serverName = new SimpleStringProperty();
|
||||
private final SimpleIntegerProperty sort = new SimpleIntegerProperty();
|
||||
private final SimpleBooleanProperty running = new SimpleBooleanProperty();
|
||||
private final SimpleBooleanProperty isCustomize = new SimpleBooleanProperty();
|
||||
private final SimpleStringProperty httpUser = new SimpleStringProperty();
|
||||
private final SimpleStringProperty httpPwd = new SimpleStringProperty();
|
||||
public volatile Boolean isProvider = null;
|
||||
|
||||
public ProxySetupModel(ProxySetup setup) {
|
||||
if (setup == null) return;
|
||||
@ -48,11 +52,13 @@ public class ProxySetupModel {
|
||||
setId(setup.getId());
|
||||
setServer(ProxyConfig.getServerName(setup.getNode()));
|
||||
setProxyName(setup.getProxy_name());
|
||||
setProxyType(setup.getProxy_type());
|
||||
setProxyType(StringUtils.upperCase(setup.getProxy_type()));
|
||||
setLocalIp(setup.getLocal_ip());
|
||||
setLocalPort(setup.getLocal_port());
|
||||
setRemotePort(setup.getRemote_port());
|
||||
setDomain(domainStr);
|
||||
setHttpUser(setup.getHttp_user());
|
||||
setHttpPwd(setup.getHttp_pwd());
|
||||
setDomainSuffix(suffix);
|
||||
setLocations(setup.getLocations());
|
||||
setUseEncryption(setup.getUse_encryption());
|
||||
@ -60,6 +66,7 @@ public class ProxySetupModel {
|
||||
setHeader_X_From_Where(setup.getHost_header_rewrite());
|
||||
setHostHeaderRewrite(setup.getHost_header_rewrite());
|
||||
setStatus(setup.getStatus() == null || setup.getStatus());
|
||||
setServerName(setup.getServer_name());
|
||||
setSk(setup.getSk());
|
||||
setSort(setup.getSort());
|
||||
setRunning(setup.isRuning());
|
||||
@ -129,6 +136,14 @@ public class ProxySetupModel {
|
||||
return status.get();
|
||||
}
|
||||
|
||||
public Boolean isProvider() {
|
||||
return serverName.get() == null ? null : serverName.get().equals("提供者");
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName.get();
|
||||
}
|
||||
|
||||
public boolean isUseCompression() {
|
||||
return useCompression.get();
|
||||
}
|
||||
@ -137,6 +152,14 @@ public class ProxySetupModel {
|
||||
return useEncryption.get();
|
||||
}
|
||||
|
||||
public String getHttpUser() {
|
||||
return httpUser.get();
|
||||
}
|
||||
|
||||
public String getHttpPwd() {
|
||||
return httpPwd.get();
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id.set(id == null ? null : id.toString());
|
||||
}
|
||||
@ -205,10 +228,30 @@ public class ProxySetupModel {
|
||||
this.status.set(status);
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName.set(serverName);
|
||||
}
|
||||
|
||||
public void setUseEncryption(boolean useEncryption) {
|
||||
this.useEncryption.set(useEncryption);
|
||||
}
|
||||
|
||||
public void setHttpUser(String httpUser) {
|
||||
this.httpUser.set(httpUser);
|
||||
}
|
||||
|
||||
public void setHttpPwd(String httpPwd) {
|
||||
this.httpPwd.set(httpPwd);
|
||||
}
|
||||
|
||||
public SimpleStringProperty httpPwdProperty() {
|
||||
return httpPwd;
|
||||
}
|
||||
|
||||
public SimpleStringProperty httpUserProperty() {
|
||||
return httpUser;
|
||||
}
|
||||
|
||||
public SimpleStringProperty serverProperty() {
|
||||
return server;
|
||||
}
|
||||
@ -281,6 +324,10 @@ public class ProxySetupModel {
|
||||
return hostHeaderRewrite;
|
||||
}
|
||||
|
||||
public SimpleStringProperty serverNameProperty() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public SimpleBooleanProperty isCustomizeProperty() {
|
||||
return isCustomize;
|
||||
}
|
||||
@ -307,6 +354,7 @@ public class ProxySetupModel {
|
||||
setHeader_X_From_Where(setup.getHost_header_rewrite());
|
||||
setHostHeaderRewrite(setup.getHost_header_rewrite());
|
||||
setStatus(setup.getStatus() == null || setup.getStatus());
|
||||
setServerName(setup.getServer_name());
|
||||
setSk(setup.getSk());
|
||||
setSort(setup.getSort());
|
||||
setRunning(setup.isRuning());
|
||||
@ -322,8 +370,10 @@ public class ProxySetupModel {
|
||||
StringUtils.lowerCase(proxyType.getValue()),
|
||||
getLocalIp(),
|
||||
getLocalPort() == null ? null : Integer.parseInt(getLocalPort()),
|
||||
getRemotePort() == null ? "" : getRemotePort(),
|
||||
getDomain() + getDomainSuffix(),
|
||||
getRemotePort(),
|
||||
getDomain() == null ? null : getDomain() + getDomainSuffix(),
|
||||
ProxyConfig.isHttp(this) ? getHttpUser() : null,
|
||||
ProxyConfig.isHttp(this) ? getHttpPwd() : null,
|
||||
isUseEncryption(),
|
||||
isUseCompression(),
|
||||
getLocations(),
|
||||
@ -331,6 +381,7 @@ public class ProxySetupModel {
|
||||
getHeader_X_From_Where(),
|
||||
getSk(),
|
||||
getSort(),
|
||||
serverName.get(),
|
||||
getStatus(),
|
||||
isRunning()
|
||||
);
|
||||
|
@ -12,6 +12,7 @@ import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -36,27 +37,27 @@ public class AlertUtil {
|
||||
if (mOwner != null) this.alert.initOwner(mOwner);
|
||||
}
|
||||
|
||||
public Builder title(String title) {
|
||||
public Builder<T> title(String title) {
|
||||
alert.setTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder header(String header) {
|
||||
public Builder<T> header(String header) {
|
||||
alert.setHeaderText(header);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder content(String content) {
|
||||
public Builder<T> content(String content) {
|
||||
alert.setContentText(content);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder icon(String path) {
|
||||
icon(new Image(this.getClass().getResource(path).toString()));
|
||||
public Builder<T> icon(String path) {
|
||||
icon(new Image(Objects.requireNonNull(this.getClass().getResource(path)).toString()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder icon(Image image) {
|
||||
public Builder<T> icon(Image image) {
|
||||
getStage().getIcons().add(image);
|
||||
return this;
|
||||
}
|
||||
@ -67,7 +68,6 @@ public class AlertUtil {
|
||||
|
||||
/**
|
||||
* AlertUtil.confirm
|
||||
* @param listener
|
||||
*/
|
||||
public void show(OnClickListener listener) {
|
||||
|
||||
@ -78,7 +78,6 @@ public class AlertUtil {
|
||||
|
||||
/**
|
||||
* AlertUtil.confirm
|
||||
* @param listener
|
||||
*/
|
||||
public void show(OnChoseListener listener) {
|
||||
Optional<ButtonType> result = alert.showAndWait();
|
||||
@ -103,13 +102,10 @@ public class AlertUtil {
|
||||
|
||||
/**
|
||||
* AlertUtil.choices
|
||||
* @param choices
|
||||
* @param <R>
|
||||
* @return
|
||||
*/
|
||||
public <R> R getChoice(R... choices) {
|
||||
Optional<R> result = alert.showAndWait();
|
||||
return result.get();
|
||||
Optional result = alert.showAndWait();
|
||||
return (R) result.get();
|
||||
}
|
||||
|
||||
private Stage getStage() {
|
||||
@ -117,24 +113,24 @@ public class AlertUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static Builder info(String content) {
|
||||
return new Builder(new Alert(AlertType.INFORMATION)).content(content).header(null);
|
||||
public static Builder<Alert> info(String content) {
|
||||
return new Builder<Alert>(new Alert(AlertType.INFORMATION)).content(content).header(null);
|
||||
}
|
||||
|
||||
public static Builder info() {
|
||||
return new Builder(new Alert(AlertType.INFORMATION));
|
||||
public static Builder<Alert> info() {
|
||||
return new Builder<Alert>(new Alert(AlertType.INFORMATION));
|
||||
}
|
||||
|
||||
public static Builder error(String message) {
|
||||
return new Builder(new Alert(AlertType.ERROR)).header(null).content(message);
|
||||
public static Builder<Alert> error(String message) {
|
||||
return new Builder<Alert>(new Alert(AlertType.ERROR)).header(null).content(message);
|
||||
}
|
||||
|
||||
public static Builder warning() {
|
||||
return new Builder(new Alert(AlertType.WARNING));
|
||||
public static Builder<Alert> warning() {
|
||||
return new Builder<Alert>(new Alert(AlertType.WARNING));
|
||||
}
|
||||
|
||||
public static Builder exception(Exception ex) {
|
||||
return new Builder(exceptionAlert(ex));
|
||||
public static Builder<Alert> exception(Exception ex) {
|
||||
return new Builder<Alert>(exceptionAlert(ex));
|
||||
}
|
||||
|
||||
public static Alert exceptionAlert(Exception ex) {
|
||||
@ -173,17 +169,17 @@ public class AlertUtil {
|
||||
/**
|
||||
* 确认对话框
|
||||
*/
|
||||
public static Builder confirm() {
|
||||
public static Builder<Alert> confirm() {
|
||||
Alert alert = new Alert(AlertType.CONFIRMATION);
|
||||
alert.setTitle("确认对话框");
|
||||
return new Builder(alert);
|
||||
return new Builder<Alert>(alert);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义确认对话框 <p>
|
||||
* <code>"Cancel"</code> OR <code>"取消"</code> 为取消按钮
|
||||
*/
|
||||
public static Builder confirm(String... buttons) {
|
||||
public static Builder<Alert> confirm(String... buttons) {
|
||||
Alert alert = new Alert(AlertType.CONFIRMATION);
|
||||
|
||||
List<ButtonType> buttonList = Arrays.stream(buttons).map((type) -> {
|
||||
@ -195,19 +191,19 @@ public class AlertUtil {
|
||||
|
||||
alert.getButtonTypes().setAll(buttonList);
|
||||
|
||||
return new Builder(alert);
|
||||
return new Builder<Alert>(alert);
|
||||
}
|
||||
|
||||
public static Builder input(String content) {
|
||||
public static Builder<TextInputDialog> input(String content) {
|
||||
TextInputDialog dialog = new TextInputDialog();
|
||||
dialog.setContentText(content);
|
||||
return new Builder(dialog);
|
||||
return new Builder<TextInputDialog>(dialog);
|
||||
}
|
||||
|
||||
public static <T> Builder choices(String hintText, T... choices) {
|
||||
public static <T> Builder<ChoiceDialog<T>> choices(String hintText, T... choices) {
|
||||
ChoiceDialog<T> dialog = new ChoiceDialog<T>(choices[0], choices);
|
||||
dialog.setContentText(hintText);
|
||||
return new Builder(dialog);
|
||||
return new Builder<ChoiceDialog<T>>(dialog);
|
||||
}
|
||||
|
||||
|
||||
|
37
src/main/java/top/octopusyan/utils/ClipUtil.java
Normal file
37
src/main/java/top/octopusyan/utils/ClipUtil.java
Normal file
@ -0,0 +1,37 @@
|
||||
package top.octopusyan.utils;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* <p> author : octopus yan
|
||||
* <p> email : octopus_yan@foxmail.com
|
||||
* <p> description : 剪切板工具
|
||||
* <p> create : 2022-4-14 23:21
|
||||
*/
|
||||
public class ClipUtil {
|
||||
//获取系统剪切板
|
||||
private static final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
|
||||
public static void setClip(String data) {
|
||||
//构建String数据类型
|
||||
StringSelection stringSelection = new StringSelection(data);
|
||||
//添加文本到系统剪切板
|
||||
clipboard.setContents(stringSelection, null);
|
||||
}
|
||||
|
||||
public static String getString() {
|
||||
Transferable content = clipboard.getContents(null);//从系统剪切板中获取数据
|
||||
if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) {//判断是否为文本类型
|
||||
try {
|
||||
//从数据中获取文本值
|
||||
return (String) content.getTransferData(DataFlavor.stringFlavor);
|
||||
} catch (UnsupportedFlavorException | IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package top.octopusyan.utils;
|
||||
|
||||
import top.octopusyan.config.ProxyConfig;
|
||||
import top.octopusyan.manager.http.request.ProxySetup;
|
||||
import top.octopusyan.model.ProxySetupModel;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
@ -28,10 +27,6 @@ public class DomainUtil {
|
||||
return "." + serverPath;
|
||||
}
|
||||
|
||||
public static boolean isHttp(ProxySetupModel model){
|
||||
return ProxyConfig.getTypeIndex(model.getProxyType()) < 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否自定义隧道
|
||||
*/
|
||||
@ -39,14 +34,17 @@ public class DomainUtil {
|
||||
return !setup.getDomain().contains(getServerPath(setup));
|
||||
}
|
||||
|
||||
public static boolean isCustomize(ProxySetup setup, String serverName) {
|
||||
return !setup.getDomain().contains(ProxyConfig.getServerPath(serverName));
|
||||
}
|
||||
|
||||
private static String getServerPath(ProxySetup setup) {
|
||||
return ProxyConfig.getServerPath(ProxyConfig.getServerName(setup.getNode()));
|
||||
}
|
||||
|
||||
public static String getDomainAddress(String domain) {
|
||||
try {
|
||||
String hostAddress = InetAddress.getByName(domain).getHostAddress();
|
||||
return hostAddress;
|
||||
return InetAddress.getByName(domain).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
|
@ -105,16 +105,14 @@ public class EncryptionUtil {
|
||||
}
|
||||
|
||||
public static String getSHA(String spara) {
|
||||
String sRtn = null;
|
||||
try {
|
||||
String sRtn;
|
||||
|
||||
byte[] plainText = spara.getBytes(UTF_8);
|
||||
// 开始使用算法
|
||||
MessageDigest_SHA1.update(plainText);
|
||||
// 输出算法运算结果
|
||||
sRtn = BASE64Encoder.encode(MessageDigest_SHA1.digest());
|
||||
} catch (Exception e) {
|
||||
logger.error("SHA-1 160加密出错:" + e.getMessage());
|
||||
}
|
||||
|
||||
return sRtn;
|
||||
}
|
||||
|
||||
@ -124,16 +122,13 @@ public class EncryptionUtil {
|
||||
* @param data 加密内容
|
||||
* @param password 秘钥
|
||||
*/
|
||||
public static String DESencode(String data, String password) {
|
||||
public static String DESencode(String data, String password) throws Exception {
|
||||
byte[] pasByte;
|
||||
try {
|
||||
|
||||
Key key = getDESKey(password);
|
||||
Cipher_ECB_MOB.init(Cipher.ENCRYPT_MODE, key);
|
||||
pasByte = Cipher_ECB_MOB.doFinal(data.getBytes(UTF_8));
|
||||
} catch (Exception e) {
|
||||
logger.error("DES加密出错:" + e.getMessage());
|
||||
throw new RuntimeException("DES加密出错:" + e.getMessage());
|
||||
}
|
||||
|
||||
return Base64.encode(pasByte);
|
||||
}
|
||||
|
||||
@ -259,7 +254,7 @@ public class EncryptionUtil {
|
||||
* @return MD5加密后生成32位(大写字母 + 数字)字符串
|
||||
*/
|
||||
public static String MD5Upper(String plainText, String saltValue) {
|
||||
try {
|
||||
|
||||
// 使用指定的字节更新摘要
|
||||
MessageDigest_MD5.update(plainText.getBytes());
|
||||
MessageDigest_MD5.update(saltValue.getBytes());
|
||||
@ -268,10 +263,7 @@ public class EncryptionUtil {
|
||||
byte[] mdResult = MessageDigest_MD5.digest();
|
||||
// 把密文转换成十六进制的字符串形式
|
||||
return getHexString(hexDigitsLower, mdResult);
|
||||
} catch (Exception e) {
|
||||
logger.error("MDS 加密出错:" + e.getMessage());
|
||||
throw new RuntimeException("MDS 加密出错:" + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -282,6 +274,14 @@ public class EncryptionUtil {
|
||||
return getMessageDigestString(plainText, hexDigitsLower, MessageDigest_MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* MD5加密后生成16位(小写字母+数字)字符串
|
||||
* 同 MD5Lower() 一样
|
||||
*/
|
||||
public static String MD5_16(String plainText) {
|
||||
return getMessageDigestString(plainText, hexDigitsLower, MessageDigest_MD5).substring(8, 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验MD5码
|
||||
*
|
||||
|
139
src/main/java/top/octopusyan/utils/TooltipUtil.java
Normal file
139
src/main/java/top/octopusyan/utils/TooltipUtil.java
Normal file
@ -0,0 +1,139 @@
|
||||
package top.octopusyan.utils;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.stage.Window;
|
||||
|
||||
/**
|
||||
* <p> author : octopus yan
|
||||
* <p> email : octopus_yan@foxmail.com
|
||||
* <p> description : 提示工具
|
||||
* <p> create : 2022-4-12 17:16 *
|
||||
*/
|
||||
public class TooltipUtil {
|
||||
private static TooltipUtil util;
|
||||
private final Tooltip tooltip = new Tooltip();
|
||||
private Window owner;
|
||||
private ChangeListener<Number> xListener;
|
||||
private ChangeListener<Number> yListener;
|
||||
private boolean paneMove = false;
|
||||
|
||||
private TooltipUtil(Window window) {
|
||||
this.owner = window;
|
||||
this.tooltip.styleProperty().set(
|
||||
"-fx-background-color: white;" +
|
||||
"-fx-text-fill: grey;" +
|
||||
"-fx-font-size: 12px;"
|
||||
);
|
||||
}
|
||||
|
||||
public static TooltipUtil getInstance(Pane pane) {
|
||||
if (pane == null) return null;
|
||||
Window window = pane.getScene().getWindow();
|
||||
if (window == null) return null;
|
||||
|
||||
if (util == null) {
|
||||
util = new TooltipUtil(window);
|
||||
// 窗口位置监听
|
||||
util.xListener = (observable, oldValue, newValue) -> {
|
||||
util.tooltip.setAnchorX(util.tooltip.getAnchorX() + (newValue.doubleValue() - oldValue.doubleValue()));
|
||||
util.paneMove = true;
|
||||
};
|
||||
util.yListener = (observable, oldValue, newValue) -> {
|
||||
util.tooltip.setAnchorY(util.tooltip.getAnchorY() + (newValue.doubleValue() - oldValue.doubleValue()));
|
||||
util.paneMove = true;
|
||||
};
|
||||
util.tooltip.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue) util.paneMove = false;
|
||||
});
|
||||
// 随窗口移动
|
||||
util.owner.xProperty().addListener(util.xListener);
|
||||
util.owner.yProperty().addListener(util.yListener);
|
||||
}
|
||||
|
||||
if (!window.equals(util.owner)) {
|
||||
// 删除旧监听
|
||||
util.owner.xProperty().removeListener(util.xListener);
|
||||
util.owner.yProperty().removeListener(util.yListener);
|
||||
// 新窗口
|
||||
util.owner = window;
|
||||
// 随窗口移动
|
||||
util.owner.xProperty().addListener(util.xListener);
|
||||
util.owner.yProperty().addListener(util.yListener);
|
||||
}
|
||||
|
||||
// 点击关闭
|
||||
pane.setOnMouseClicked(event -> {
|
||||
if (!util.paneMove) util.tooltip.hide();
|
||||
util.paneMove = false;
|
||||
});
|
||||
|
||||
util.tooltip.hide();
|
||||
|
||||
return util;
|
||||
}
|
||||
|
||||
public void showProxyTypeTip(MouseEvent event) {
|
||||
tooltip.setText(
|
||||
"提示:XTCP 映射成功率并不高,具体取决于 NAT 设备的复杂度。\n" +
|
||||
"TCP :基础的 TCP 映射,适用于大多数服务,例如远程桌面、SSH、Minecraft、泰拉瑞亚等\n" +
|
||||
"UDP :基础的 UDP 映射,适用于域名解析、部分基于 UDP 协议的游戏等\n" +
|
||||
"HTTP :搭建网站专用映射,并通过 80 端口访问\n" +
|
||||
"HTTPS :带有 SSL 加密的网站映射,通过 443 端口访问,服务器需要支持 SSL\n" +
|
||||
"XTCP :客户端之间点对点 (P2P) 连接协议,流量不经过服务器,适合大流量传输的场景,需要两台设备之间都运行一个客户端\n" +
|
||||
"STCP :安全交换 TCP 连接协议,基于 TCP,访问此服务的用户也需要运行一个客户端,才能建立连接,流量由服务器转发"
|
||||
);
|
||||
show(event);
|
||||
}
|
||||
|
||||
public void showHttpUserTip(MouseEvent event) {
|
||||
tooltip.setText(
|
||||
"您的内网服务映射到外网之后,所有知道外网访问地址的人均可以访问;\n" +
|
||||
"但如果您在此处设置用户名、密码,则只有知道用户名、密码的访客才能访问"
|
||||
);
|
||||
show(event);
|
||||
}
|
||||
|
||||
public void showProxyStatusTip(MouseEvent event) {
|
||||
tooltip.setText("当有多个连接时,您可以选择启用或停用某些连接");
|
||||
show(event);
|
||||
}
|
||||
|
||||
private void show(MouseEvent event) {
|
||||
|
||||
if (tooltip.isShowing()) {
|
||||
tooltip.hide();
|
||||
} else {
|
||||
tooltip.show(owner);
|
||||
double mx = event.getScreenX();
|
||||
double my = event.getScreenY();
|
||||
double tw = tooltip.widthProperty().doubleValue();
|
||||
double th = tooltip.heightProperty().doubleValue();
|
||||
|
||||
tooltip.setX(mx - tw / 2);
|
||||
tooltip.setY(my - th - 10);
|
||||
}
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
tooltip.hide();
|
||||
}
|
||||
|
||||
public boolean isShowing(){
|
||||
return tooltip.isShowing();
|
||||
}
|
||||
|
||||
public void showP2pPwdTip(MouseEvent event) {
|
||||
tooltip.setText("以访客模式连接时的密码,仅限 XTCP/STCP\n只有访问密码一致的用户才能访问到此服务");
|
||||
show(event);
|
||||
}
|
||||
|
||||
public void showP2pServerName(MouseEvent event) {
|
||||
tooltip.setText("要访问的服务的名字(系统加密)");
|
||||
show(event);
|
||||
}
|
||||
}
|
@ -55,6 +55,13 @@
|
||||
-fx-text-fill: white;
|
||||
}
|
||||
|
||||
.proxyListItemLabel {
|
||||
-fx-font-size: 16px;
|
||||
}
|
||||
.proxyListItemIcon {
|
||||
-fx-font-size: 14px;
|
||||
}
|
||||
|
||||
.proxyListItem-run FontIcon {
|
||||
-fx-icon-color: linear-gradient(#95f257, #91e5ac);
|
||||
}
|
||||
@ -66,7 +73,7 @@
|
||||
}
|
||||
|
||||
/* 面板背景 */
|
||||
#proxySetupPane, #proxyListPane {
|
||||
#proxySetupPane, #proxyListPane, .whitePane {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
@ -101,7 +108,7 @@
|
||||
-fx-font-size: 16px;
|
||||
-fx-font-family: "Microsoft YaHei";
|
||||
}
|
||||
.startProxyBtn {
|
||||
.startProxyBtn, #copyP2pConfig, #importP2pConfig {
|
||||
-fx-text-fill: white;
|
||||
-fx-background-color: linear-gradient(#57b4f2, #9198e5);
|
||||
}
|
||||
@ -120,3 +127,9 @@
|
||||
-fx-font-family: monospace;
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
.proxySetupItemBox {
|
||||
-fx-pref-width: 520;
|
||||
-fx-pref-height: 50;
|
||||
-fx-padding: 10 0 10 0;
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.shape.Rectangle?>
|
||||
@ -88,8 +89,8 @@
|
||||
<Font size="11.5"/>
|
||||
</font>
|
||||
</JFXCheckBox>
|
||||
<JFXButton fx:id="findpassBtn" 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>
|
||||
@ -108,6 +109,11 @@
|
||||
<Font size="11.0"/>
|
||||
</font>
|
||||
</JFXButton>
|
||||
<Label fx:id="appVersionLabel" disable="true" layoutX="369.0" layoutY="172.0" text="Label">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</Label>
|
||||
</AnchorPane>
|
||||
</VBox>
|
||||
</StackPane>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<?import javafx.scene.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||
<?import org.kordamp.ikonli.javafx.*?>
|
||||
<StackPane fx:id="root" prefHeight="700.0" prefWidth="750.0" stylesheets="@../css/main.css"
|
||||
xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="top.octopusyan.controller.MainController">
|
||||
@ -14,32 +14,33 @@
|
||||
<VBox fx:id="MainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
||||
prefHeight="700.0" prefWidth="750.0">
|
||||
<AnchorPane fx:id="MainTopPane" prefHeight="35.0" prefWidth="750.0">
|
||||
<JFXButton fx:id="titleLable" disable="true" prefHeight="35.0" prefWidth="90.0" text="Yan Frp"/>
|
||||
<JFXButton fx:id="minimizeBtn" layoutX="678.0" prefHeight="35.0" prefWidth="36.0" text="—"/>
|
||||
<JFXButton fx:id="closeBtn" layoutX="714.0" prefHeight="35.0" prefWidth="36.0" text="X"/>
|
||||
<JFXComboBox fx:id="proxyServerComboBox" layoutX="90.0" prefHeight="36.0" prefWidth="90.0">
|
||||
<Label fx:id="titleLable" alignment="CENTER" disable="true" prefHeight="35.0" prefWidth="90.0"
|
||||
text="YanFrp"/>
|
||||
<JFXButton fx:id="minimizeBtn" layoutX="680.0" prefHeight="35.0" prefWidth="35.0" text="—"/>
|
||||
<JFXButton fx:id="closeBtn" layoutX="715.0" prefHeight="35.0" prefWidth="35.0" text="X"/>
|
||||
<JFXComboBox fx:id="proxyServerComboBox" layoutX="90.0" prefHeight="35.0" prefWidth="90.0">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
</JFXComboBox>
|
||||
</AnchorPane>
|
||||
<GridPane fx:id="MainBottomPane" hgap="10.0" prefHeight="684.0" prefWidth="750.0" vgap="10.0">
|
||||
<GridPane fx:id="MainBottomPane" hgap="10.0" prefHeight="680.0" prefWidth="750.0" vgap="10.0">
|
||||
<VBox.margin>
|
||||
<Insets top="-10.0"/>
|
||||
</VBox.margin>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="539.0" minWidth="10.0" prefWidth="520.0"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="370.0" minWidth="10.0" prefWidth="200.0"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="493.0" minHeight="10.0" prefHeight="404.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="337.0" minHeight="10.0" prefHeight="231.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="493.0" minHeight="10.0" prefHeight="445.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="337.0" minHeight="10.0" prefHeight="200.0" vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<VBox fx:id="proxySetupPane" prefHeight="200.0" prefWidth="100.0" styleClass="mainPane">
|
||||
<HBox alignment="CENTER_RIGHT" prefHeight="40.0" prefWidth="520.0">
|
||||
<padding>
|
||||
<Insets left="10.0"/>
|
||||
</padding>
|
||||
<HBox alignment="CENTER_RIGHT" prefHeight="50.0" prefWidth="520.0">
|
||||
<padding>
|
||||
<Insets bottom="10.0" top="10.0"/>
|
||||
<Insets bottom="5.0" top="5.0"/>
|
||||
</padding>
|
||||
<JFXButton fx:id="resetProxyBtn" prefHeight="Infinity" text="重置">
|
||||
<HBox.margin>
|
||||
@ -50,21 +51,15 @@
|
||||
</cursor>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
<HBox alignment="CENTER_LEFT" prefHeight="50.0" prefWidth="520.0">
|
||||
<padding>
|
||||
<Insets bottom="10.0" top="10.0"/>
|
||||
</padding>
|
||||
<HBox alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="隧道名称">
|
||||
<HBox.margin>
|
||||
<Insets right="20.0"/>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<JFXTextField fx:id="proxyNameTextField" prefHeight="Infinity" prefWidth="320.0"
|
||||
<JFXTextField fx:id="proxyNameTextField" prefHeight="Infinity" prefWidth="310.0"
|
||||
styleClass="inputText">
|
||||
<HBox.margin>
|
||||
<Insets right="20.0"/>
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets left="15.0"/>
|
||||
</padding>
|
||||
@ -73,17 +68,14 @@
|
||||
</font>
|
||||
</JFXTextField>
|
||||
</HBox>
|
||||
<HBox alignment="CENTER_LEFT" prefHeight="50.0" prefWidth="520.0">
|
||||
<padding>
|
||||
<Insets bottom="10.0" top="10.0"/>
|
||||
</padding>
|
||||
<HBox alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="是否启用">
|
||||
<HBox.margin>
|
||||
<Insets right="20.0"/>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<HBox alignment="CENTER_LEFT" prefHeight="Infinity" prefWidth="320.0">
|
||||
<HBox alignment="CENTER_LEFT" prefHeight="Infinity" prefWidth="310.0">
|
||||
<JFXRadioButton fx:id="openProxyRBtn" text="启用">
|
||||
<HBox.margin>
|
||||
<Insets right="20.0"/>
|
||||
@ -97,38 +89,94 @@
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
</JFXRadioButton>
|
||||
<JFXButton fx:id="proxyStatusTip" text="">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
<graphic>
|
||||
<FontIcon iconColor="#a5a3a3" iconLiteral="fa-question-circle-o" iconSize="15"/>
|
||||
</graphic>
|
||||
<HBox.margin>
|
||||
<Insets left="20.0"/>
|
||||
</HBox.margin>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
</HBox>
|
||||
<HBox alignment="CENTER_LEFT" prefHeight="50.0" prefWidth="520.0">
|
||||
<padding>
|
||||
<Insets bottom="10.0" top="10.0"/>
|
||||
</padding>
|
||||
<HBox alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="协议类型">
|
||||
<HBox.margin>
|
||||
<Insets right="20.0"/>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<JFXComboBox fx:id="proxyProtocolComboBox" prefHeight="Infinity" prefWidth="320.0"
|
||||
<JFXComboBox fx:id="proxyProtocolComboBox" prefHeight="Infinity" prefWidth="310.0"
|
||||
styleClass="inputText">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
</JFXComboBox>
|
||||
<JFXButton fx:id="proxyTypeTip" text="">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0"/>
|
||||
</HBox.margin>
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
<graphic>
|
||||
<FontIcon iconColor="#a5a3a3" iconLiteral="fa-question-circle-o" iconSize="15">
|
||||
</FontIcon>
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
<HBox alignment="CENTER_LEFT" prefHeight="50.0" prefWidth="520.0">
|
||||
<padding>
|
||||
<Insets bottom="10.0" top="10.0"/>
|
||||
</padding>
|
||||
<HBox fx:id="p2pRolePane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="内网服务地址">
|
||||
styleClass="proxySetupLabel" text="服务角色">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<HBox fx:id="p2pRoleView" alignment="CENTER_LEFT" prefHeight="Infinity" prefWidth="310.0">
|
||||
<JFXRadioButton fx:id="serverProviderRBtn" text="提供者">
|
||||
<HBox.margin>
|
||||
<Insets right="20.0"/>
|
||||
</HBox.margin>
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
</JFXRadioButton>
|
||||
<JFXRadioButton fx:id="serverVisitorRBtn" text="访问者">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
</JFXRadioButton>
|
||||
<JFXButton fx:id="copyP2pConfig" text="复制">
|
||||
<HBox.margin>
|
||||
<Insets left="20.0"/>
|
||||
</HBox.margin>
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
</JFXButton>
|
||||
<JFXButton fx:id="importP2pConfig" text="从剪切板导入">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0"/>
|
||||
</HBox.margin>
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
</HBox>
|
||||
<HBox alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label fx:id="localHostLabel" alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="内网服务地址">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<HBox prefHeight="Infinity" prefWidth="320.0">
|
||||
<HBox prefHeight="Infinity" prefWidth="310.0">
|
||||
<JFXTextField fx:id="localHostTextField" alignment="CENTER" prefWidth="220.0"
|
||||
promptText="内网服务IP (IPV4)" styleClass="inputText-left" text="127.0.0.1">
|
||||
promptText="内网服务IP (IPv4)" styleClass="inputText-left" text="127.0.0.1">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
@ -141,28 +189,89 @@
|
||||
</JFXTextField>
|
||||
</HBox>
|
||||
</HBox>
|
||||
<HBox fx:id="domainPane" alignment="CENTER_LEFT" prefHeight="40.0" prefWidth="520.0">
|
||||
<HBox fx:id="serverNamePane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<children>
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="服务名称">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<JFXTextField fx:id="serverNameTextField" prefHeight="Infinity" prefWidth="310.0"
|
||||
styleClass="inputText">
|
||||
<HBox.margin>
|
||||
<Insets/>
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets top="10.0"/>
|
||||
<Insets left="15.0"/>
|
||||
</padding>
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXTextField>
|
||||
<JFXButton fx:id="serverNameTip" text="">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
<graphic>
|
||||
<FontIcon iconColor="#a5a3a3" iconLiteral="fa-question-circle-o" iconSize="15"/>
|
||||
</graphic>
|
||||
<HBox.margin>
|
||||
<Insets left="10.0"/>
|
||||
</HBox.margin>
|
||||
</JFXButton>
|
||||
</children>
|
||||
</HBox>
|
||||
<HBox fx:id="p2pPwdPane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="访问密码">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<JFXTextField fx:id="p2pPwdTextField" prefHeight="Infinity" prefWidth="310.0"
|
||||
styleClass="inputText">
|
||||
<HBox.margin>
|
||||
<Insets/>
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets left="15.0"/>
|
||||
</padding>
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXTextField>
|
||||
<JFXButton fx:id="p2pPwdTip" text="">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
<graphic>
|
||||
<FontIcon iconColor="#a5a3a3" iconLiteral="fa-question-circle-o" iconSize="15"/>
|
||||
</graphic>
|
||||
<HBox.margin>
|
||||
<Insets left="10.0"/>
|
||||
</HBox.margin>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
<HBox fx:id="domainPane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="外网访问地址">
|
||||
<HBox.margin>
|
||||
<Insets right="20.0"/>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<AnchorPane maxHeight="30.0" prefHeight="30" prefWidth="320.0">
|
||||
<JFXTextField fx:id="domainTextField" prefHeight="31.0" prefWidth="320.0"
|
||||
promptText="自定义子域名大于3位" styleClass="inputText">
|
||||
<AnchorPane maxHeight="30.0" prefHeight="30" prefWidth="310.0">
|
||||
<JFXTextField fx:id="domainTextField" prefHeight="31.0" prefWidth="310.0"
|
||||
promptText="自定义子域名" styleClass="inputText">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
<padding>
|
||||
<Insets left="15.0"/>
|
||||
<Insets left="10.0"/>
|
||||
</padding>
|
||||
</JFXTextField>
|
||||
<JFXTextField fx:id="domainSuffixTextField" alignment="CENTER" disable="true"
|
||||
layoutX="165.0" prefHeight="30.0" prefWidth="155.0"
|
||||
layoutX="155.0" prefHeight="30.0" prefWidth="155.0"
|
||||
styleClass="inputText-right" text=".frp.octopusyan.top">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
@ -178,21 +287,54 @@
|
||||
</cursor>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
<Label fx:id="domainHtinTextField" alignment="CENTER" prefHeight="20.0" prefWidth="320.0"
|
||||
text="Label" visible="false">
|
||||
<VBox.margin>
|
||||
<Insets left="120.0"/>
|
||||
</VBox.margin>
|
||||
<HBox fx:id="httpUserPane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="用户名">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<JFXTextField fx:id="httpUserTextField" prefHeight="Infinity" prefWidth="310.0"
|
||||
promptText="(可选)外网访问用户名" styleClass="inputText">
|
||||
<padding>
|
||||
<Insets left="15.0"/>
|
||||
</padding>
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXTextField>
|
||||
<JFXButton fx:id="httpUserTip" text="">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
<graphic>
|
||||
<FontIcon iconColor="#a5a3a3" iconLiteral="fa-question-circle-o" iconSize="15"/>
|
||||
</graphic>
|
||||
<HBox.margin>
|
||||
<Insets left="10.0"/>
|
||||
</HBox.margin>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
<HBox fx:id="httpPwdPane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
|
||||
styleClass="proxySetupLabel" text="密码">
|
||||
<HBox.margin>
|
||||
<Insets left="10.0" right="20.0"/>
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<HBox fx:id="proxyStatusPane" alignment="CENTER" prefHeight="40.0" prefWidth="520.0">
|
||||
<JFXTextField fx:id="httpPwdTextField" prefHeight="Infinity" prefWidth="310.0"
|
||||
promptText="(可选)外网访问密码" styleClass="inputText">
|
||||
<padding>
|
||||
<Insets bottom="10.0"/>
|
||||
<Insets left="15.0"/>
|
||||
</padding>
|
||||
<Label alignment="CENTER_RIGHT" prefHeight="30.0" styleClass="proxySetupLabel"
|
||||
text="启动成功!立即访问 ">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXTextField>
|
||||
</HBox>
|
||||
<HBox fx:id="proxyStatusPane" alignment="CENTER" prefHeight="40.0" prefWidth="520.0">
|
||||
<Label fx:id="runingLabel" alignment="CENTER_RIGHT" prefHeight="30.0"
|
||||
styleClass="proxySetupLabel" text="启动成功!立即访问 ">
|
||||
<HBox.margin>
|
||||
<Insets/>
|
||||
</HBox.margin>
|
||||
@ -202,7 +344,7 @@
|
||||
</HBox>
|
||||
</VBox>
|
||||
<VBox fx:id="proxyListPane" prefHeight="Infinity" prefWidth="Infinity" GridPane.columnIndex="1">
|
||||
<JFXListView fx:id="proxyListView" prefHeight="360.0" prefWidth="Infinity"/>
|
||||
<JFXListView fx:id="proxyListView" prefHeight="400.0" prefWidth="200.0"/>
|
||||
<JFXButton fx:id="addProxyBtn" prefHeight="45.0" prefWidth="Infinity" text="新增连接">
|
||||
<graphic>
|
||||
<FontIcon iconColor="white" iconLiteral="fa-plus"/>
|
||||
@ -213,19 +355,15 @@
|
||||
</JFXButton>
|
||||
</VBox>
|
||||
<AnchorPane prefHeight="Infinity" prefWidth="Infinity" GridPane.rowIndex="1">
|
||||
<JFXTabPane fx:id="tabPane" prefHeight="230" prefWidth="520.0" styleClass="mainPane">
|
||||
<JFXTabPane fx:id="tabPane" prefHeight="200.0" prefWidth="520.0" styleClass="mainPane">
|
||||
<tabs>
|
||||
<Tab fx:id="proxyLogPane" 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"
|
||||
styleClass="whitePane">
|
||||
<padding>
|
||||
<Insets left="20.0" top="20.0"/>
|
||||
</padding>
|
||||
<HBox prefHeight="30.0" prefWidth="Infinity" visible="false">
|
||||
<Label prefHeight="Infinity" text="* 若启动后无法通过外网访问,请点击"/>
|
||||
<Hyperlink prefHeight="Infinity" text="此处"/>
|
||||
<Label prefHeight="Infinity" text="排查问题"/>
|
||||
</HBox>
|
||||
<Label prefHeight="30" text="* 修改配置信息后,必须重启才能生效"/>
|
||||
<Label prefHeight="30" text="* 只有启动或重启内网穿透服务时,配置信息才会被保存"/>
|
||||
<Label prefHeight="30" text="* 同一个账号在同一时间只能在一台电脑上登录使用"/>
|
||||
@ -265,7 +403,21 @@
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND"/>
|
||||
</cursor>
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXButton>
|
||||
<Label fx:id="appVersionLabel" contentDisplay="CENTER" disable="true">
|
||||
<VBox.margin>
|
||||
<Insets top="80.0"/>
|
||||
</VBox.margin>
|
||||
<font>
|
||||
<Font size="11.0"/>
|
||||
</font>
|
||||
<opaqueInsets>
|
||||
<Insets/>
|
||||
</opaqueInsets>
|
||||
</Label>
|
||||
</VBox>
|
||||
</children>
|
||||
</GridPane>
|
||||
|
@ -1,11 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.shape.Rectangle?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<StackPane fx:id="root" prefHeight="330.0" prefWidth="430.0" stylesheets="@../css/register.css" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.octopusyan.controller.RegisterController">
|
||||
<StackPane fx:id="root" prefHeight="330.0" prefWidth="430.0" stylesheets="@../css/register.css"
|
||||
xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="top.octopusyan.controller.RegisterController">
|
||||
<ImageView fx:id="registBkgPane" fitHeight="330.0" fitWidth="430.0">
|
||||
<clip>
|
||||
<Rectangle height="330" width="430">
|
||||
@ -14,42 +17,51 @@
|
||||
</Rectangle>
|
||||
</clip>
|
||||
</ImageView>
|
||||
<VBox fx:id="registMainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="330.0" prefWidth="430.0">
|
||||
<VBox fx:id="registMainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
||||
prefHeight="330.0" prefWidth="430.0">
|
||||
<AnchorPane fx:id="registTopPane" prefHeight="130.0" prefWidth="430.0">
|
||||
<JFXButton fx:id="titleLable" disable="true" prefHeight="35.0" prefWidth="64.0" text="YanFrp"/>
|
||||
<JFXButton fx:id="minimizeBtn" layoutX="360.0" prefHeight="35.0" prefWidth="36.0" text="—"/>
|
||||
<JFXButton fx:id="closeBtn" layoutX="395.0" prefHeight="35.0" prefWidth="36.0" text="X"/>
|
||||
</AnchorPane>
|
||||
<AnchorPane fx:id="registBottomPane" prefHeight="200.0" prefWidth="430.0">
|
||||
<JFXTextField fx:id="accooundTextField" labelFloat="true" layoutX="25.0" layoutY="25.0" prefHeight="29.0" prefWidth="279.0" promptText="请输入6-18位账号,支持大小写数字下划线">
|
||||
<JFXTextField fx:id="accooundTextField" labelFloat="true" layoutX="25.0" layoutY="21.0" prefHeight="29.0"
|
||||
prefWidth="279.0" promptText="请输入6-18位账号,支持大小写数字下划线">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXTextField>
|
||||
<JFXTextField fx:id="emailTf" labelFloat="true" layoutX="25.0" layoutY="90.0" promptText="请输入邮箱">
|
||||
<JFXTextField fx:id="emailTf" labelFloat="true" layoutX="25.0" layoutY="70.0" promptText="请输入邮箱">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXTextField>
|
||||
<HBox layoutX="25.0" layoutY="145.0" prefHeight="30.0" prefWidth="171.0">
|
||||
<JFXTextField fx:id="emailCheckCodeTf" prefHeight="30.0" prefWidth="92.0" promptText="6位验证号码">
|
||||
<HBox layoutX="25.0" layoutY="127.0" prefHeight="30.0" prefWidth="170.0">
|
||||
<JFXTextField fx:id="emailCheckCodeTf" prefHeight="30.0" prefWidth="94.0" promptText="6位验证号码">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXTextField>
|
||||
<JFXButton fx:id="sendCheckCodeBtn" buttonType="RAISED" layoutX="75.0" prefHeight="30.0" text="发送验证码" />
|
||||
<JFXButton fx:id="sendCheckCodeBtn" buttonType="RAISED" prefHeight="30.0" text="发送验证码"/>
|
||||
</HBox>
|
||||
<JFXPasswordField fx:id="passwordTextField" labelFloat="true" layoutX="233.0" layoutY="70.0" promptText="请输入密码不少于6位">
|
||||
<JFXPasswordField fx:id="passwordTextField" labelFloat="true" layoutX="233.0" layoutY="70.0"
|
||||
promptText="请输入密码不少于6位">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</JFXPasswordField>
|
||||
<JFXButton fx:id="registerBtn" buttonType="RAISED" layoutX="227.0" layoutY="127.0" prefHeight="51.0" prefWidth="177.0" text="注册" textFill="WHITE">
|
||||
<JFXButton fx:id="registerBtn" buttonType="RAISED" layoutX="227.0" layoutY="127.0" prefHeight="51.0"
|
||||
prefWidth="177.0" text="注册" textFill="WHITE">
|
||||
<font>
|
||||
<Font size="15.0"/>
|
||||
</font>
|
||||
</JFXButton>
|
||||
<JFXButton fx:id="loginBtn" layoutX="306.0" layoutY="24.0" text="已有账号,去登录" textFill="#00000080"/>
|
||||
<Label fx:id="appVersionLabel" disable="true" layoutX="80.0" layoutY="171.0" text="Label">
|
||||
<font>
|
||||
<Font size="14.0"/>
|
||||
</font>
|
||||
</Label>
|
||||
</AnchorPane>
|
||||
</VBox>
|
||||
</StackPane>
|
||||
|
Loading…
Reference in New Issue
Block a user