Compare commits

..

No commits in common. "1e104c499d51007bb7ac7c8570dd2546801fd093" and "db9a3c2272f22a09d0a649b7941205bccdf5f963" have entirely different histories.

19 changed files with 263 additions and 487 deletions

44
.gitignore vendored
View File

@ -1,44 +0,0 @@
.mvn/
mvnw
mvnw.cmd
log/
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
/bin/
### IntelliJ IDEA ###
.idea/
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@ -1,36 +1,37 @@
# YanFrp # Yan Frp
基于javafx开发的 frp 客户端 基于javafx开发的 frp 客户端
后台管理面板使用的是开源的 frp 管理面板 [SakuraPanel](https://github.com/ZeroDream-CN/SakuraPanel)
### 说明: ps:为了方便 YanFrp 我自己修改了些面板代码,如果想自己搭建需要改造
- 后台管理面板使用的是开源的 frp 管理面板 [SakuraPanel](https://github.com/ZeroDream-CN/SakuraPanel) 管理面板网址https://frp.octopusyan.top/?page=login
- 管理面板网址https://frp.octopusyan.top/?page=login
### 安装教程 # 说明:
1. git clone https://gitee.com/octopus_yan/yan-frp.git 1.
2. mvn jfx:jar
### 后续? # 后续开发计划
- ~~HTTP Basic Auth~~ - ~~HTTP Basic Auth~~ (已完成)
- ~~添加连接方式(xtcp,stcp)~~ - ~~添加p2p连接方式(xtcp,stcp)~~ (已完成)
- ~~删除隧道~~
- HTTP URL路由 - HTTP URL路由
- 待添加... - 待添加...
# 界面:
### 界面: 登录
#### 登录
![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/login.png) ![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/login.png)
#### 注册 注册
![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/register.png) ![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/register.png)
#### 主界面 主界面
![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/main.png) ![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/main.png)
#### 主界面-运行 主界面-运行
![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/main-run.png) ![Image text](https://git.octopusyan.top/octopus_yan/YanFrp/raw/branch/master/readme/main-run.png)

View File

@ -5,9 +5,9 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>top.octopusyan</groupId> <groupId>top.octopusyan</groupId>
<artifactId>yanfrp</artifactId> <artifactId>YanFrp</artifactId>
<version>1.1.4</version> <version>1.1.2</version>
<name>yanfrp</name> <name>YanFrp</name>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@ -55,7 +55,7 @@ public class YanFrpApplication extends Application {
HttpLoggingInterceptor.Level.HEADERS HttpLoggingInterceptor.Level.HEADERS
)) ))
// 请求服务地址 // 请求服务地址
.serverPath("https://example.com") .serverPath("https://frp.octopusyan.top")
// 是否打印日志 // 是否打印日志
.setLogEnabled(true) .setLogEnabled(true)
// 设置日志打印策略 // 设置日志打印策略

View File

@ -82,7 +82,7 @@ public abstract class BaseController<P extends Pane> implements Initializable {
} }
// app 版本信息 // app 版本信息
if (getAppVersionLabel() != null) getAppVersionLabel().setText("v" + ApplicatonStore.APP_VERSION); if (getAppVersionLabel() != null) getAppVersionLabel().setText("version : v" + ApplicatonStore.APP_VERSION);
// 这个位置的左边第一个 JFXBtn 会莫名其妙会的焦点效果启动时禁用焦点取消按钮效果 // 这个位置的左边第一个 JFXBtn 会莫名其妙会的焦点效果启动时禁用焦点取消按钮效果
if (getFirstBtn() != null) getFirstBtn().setDisableVisualFocus(true); if (getFirstBtn() != null) getFirstBtn().setDisableVisualFocus(true);

View File

@ -20,18 +20,18 @@ public class ProxyConfig {
private static final Map<String, Integer> typePort = new HashMap<>(); private static final Map<String, Integer> typePort = new HashMap<>();
static { static {
serverPath.put("香港", "example.com"); serverPath.put("香港", "xg.frp.octopusyan.top");
serverPath.put("北京", "example.com"); serverPath.put("北京", "bj.frp.octopusyan.top");
serverPath.put("上海", "example.com"); serverPath.put("上海", "frp.octopusyan.top");
serverIp.put("香港", "127.0.0.1"); serverIp.put("香港", "101.32.202.135");
serverIp.put("北京", "127.0.0.1"); serverIp.put("北京", "112.125.120.135");
serverIp.put("上海", "127.0.0.1"); serverIp.put("上海", "81.68.214.67");
typePort.put("http", 80); typePort.put("http", 80);
typePort.put("https", 443); typePort.put("https", 80);
typePort.put("tcp", 25565); typePort.put("tcp", 0);
typePort.put("udp", 53); typePort.put("udp", 0);
} }
@ -63,7 +63,7 @@ public class ProxyConfig {
UDP, UDP,
STCP, STCP,
XTCP, XTCP,
;
} }
public static String getServerPath(String serverName) { public static String getServerPath(String serverName) {

View File

@ -88,7 +88,7 @@ public class TextValidate {
static { static {
EmailFormat.setRegexPattern("^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$"); 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_-]*$"); AccoountValidator.setRegexPattern("^[a-zA-Z0-9_-]*$");
PortFormat.setRegexPattern("^([1-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])$"); 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])\\" + 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])$"); ".(\\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_-]*$"); HttpUserFormat.setRegexPattern("^[a-zA-Z0-9_-]*$");
@ -113,7 +113,7 @@ public class TextValidate {
RegexValidator validator = new RegexValidator(message); RegexValidator validator = new RegexValidator(message);
validator.setRegexPattern("[\\w@-_\\.]{" + min + "," + max + "}$"); validator.setRegexPattern("[a-zA-Z0-9_-]{" + min + "," + max + "}$");
return validator; return validator;
} }

View File

@ -16,7 +16,6 @@ import javafx.scene.input.KeyCode;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import okhttp3.Call;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
@ -163,8 +162,8 @@ public class LoginController extends BaseController<StackPane> implements Initia
// 添加文本校验 // 添加文本校验
accountTextField.getValidators().add(TextValidate.AccoountRequired); accountTextField.getValidators().add(TextValidate.AccoountRequired);
// accountTextField.getValidators().add(TextValidate.AccoountValidator); accountTextField.getValidators().add(TextValidate.AccoountValidator);
accountTextField.getValidators().add(TextValidate.getLengthValidator(6, 25, "账号")); accountTextField.getValidators().add(TextValidate.getLengthValidator(6, 18, "账号"));
passwordTextField.getValidators().add(TextValidate.PasswordRequired); passwordTextField.getValidators().add(TextValidate.PasswordRequired);
seePwdTextField.getValidators().add(TextValidate.PasswordRequired); seePwdTextField.getValidators().add(TextValidate.PasswordRequired);
@ -262,11 +261,6 @@ public class LoginController extends BaseController<StackPane> implements Initia
.api(Api.Login) .api(Api.Login)
.param(new LoginParam(accountTextField.getText(), tmpPwd.get())) .param(new LoginParam(accountTextField.getText(), tmpPwd.get()))
.request(new OnHttpListener<String>() { .request(new OnHttpListener<String>() {
@Override
public void onStart(Call call) {
Platform.runLater(() -> loginBtn.setDisable(true));
}
@Override @Override
public void onSucceed(String result) { public void onSucceed(String result) {
// 登录出错 // 登录出错
@ -276,8 +270,6 @@ public class LoginController extends BaseController<StackPane> implements Initia
} }
// 登录成功 // 登录成功
setCsrf(); setCsrf();
// 用户token
setUSerToken();
// 记住我 // 记住我
ApplicatonStore.rememberMe(tmpPwd.get()); ApplicatonStore.rememberMe(tmpPwd.get());
// 跳转 // 跳转
@ -294,30 +286,6 @@ public class LoginController extends BaseController<StackPane> implements Initia
public void onFail(Exception e) { public void onFail(Exception e) {
} }
@Override
public void onEnd(Call call) {
Platform.runLater(() -> loginBtn.setDisable(false));
}
});
}
private void setUSerToken() {
EasyHttp.builder()
.api(Api.getServerConfiguration(1))
.request(new OnHttpListener<String>() {
@Override
public void onSucceed(String result) {
String config = JsoupUtil.getServerConfiguration(result);
int start = config.indexOf("user = ");
String userToken = config.substring(start + 7, config.indexOf("\n", start));
Platform.runLater(() -> ApplicatonStore.setUserToken(userToken));
}
@Override
public void onFail(Exception e) {
}
}); });
} }

View File

@ -5,12 +5,17 @@ import com.alibaba.fastjson.JSONObject;
import com.jfoenix.controls.*; import com.jfoenix.controls.*;
import com.jfoenix.validation.base.ValidatorBase; import com.jfoenix.validation.base.ValidatorBase;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
@ -30,9 +35,9 @@ import top.octopusyan.manager.http.request.ProxySetup;
import top.octopusyan.model.ApplicatonStore; import top.octopusyan.model.ApplicatonStore;
import top.octopusyan.model.ProxySetupModel; import top.octopusyan.model.ProxySetupModel;
import top.octopusyan.utils.*; import top.octopusyan.utils.*;
import top.octopusyan.view.ProxyListItemView;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.*; import java.util.*;
import static top.octopusyan.model.ApplicatonStore.*; import static top.octopusyan.model.ApplicatonStore.*;
@ -46,6 +51,9 @@ import static top.octopusyan.model.ApplicatonStore.*;
public class MainController extends BaseController<StackPane> implements Initializable { public class MainController extends BaseController<StackPane> implements Initializable {
public static final String PROXY_LIST_ITEM_CLASS = "proxyListItem"; public static final String PROXY_LIST_ITEM_CLASS = "proxyListItem";
public static final String PROXY_LIST_ITEM_STOP_CLASS = "proxyListItem-stop";
public static final String PROXY_LIST_ITEM_RUN_CLASS = "proxyListItem-run";
public static final String PROXY_LIST_ITEM_CLOSE_CLASS = "proxyListItem-close";
public static final String PROXY_LIST_ITEM_SELECT_CLASS = "proxyListItem-select"; public static final String PROXY_LIST_ITEM_SELECT_CLASS = "proxyListItem-select";
@ -80,7 +88,7 @@ public class MainController extends BaseController<StackPane> implements Initial
public JFXButton copyDomainBtn; public JFXButton copyDomainBtn;
/* 隧道列表控件 */ /* 隧道列表控件 */
public JFXListView<ProxyListItemView> proxyListView; public JFXListView<Label> proxyListView;
public JFXButton addProxyBtn; public JFXButton addProxyBtn;
/* 日志帮助面板控件 */ /* 日志帮助面板控件 */
@ -302,7 +310,7 @@ public class MainController extends BaseController<StackPane> implements Initial
if (!children.contains(p2pRolePane)) { if (!children.contains(p2pRolePane)) {
children.add(index, p2pPwdPane); children.add(index, p2pPwdPane);
children.add(index - 1, p2pRolePane); children.add(index - 1, p2pRolePane);
if (proxySetupModel.isProvider != null && !proxySetupModel.isProvider) { if(proxySetupModel.isProvider != null && !proxySetupModel.isProvider){
children.add(index, serverNamePane); children.add(index, serverNamePane);
} }
} }
@ -368,8 +376,7 @@ public class MainController extends BaseController<StackPane> implements Initial
copyP2pConfig.setVisible(false); copyP2pConfig.setVisible(false);
importP2pConfig.setVisible(true); importP2pConfig.setVisible(true);
if (!children.contains(serverNamePane)) children.add(children.size() - 3, serverNamePane); if (!children.contains(serverNamePane)) children.add(children.size() - 3, serverNamePane);
String serverName = proxySetupModel.getServerName(); proxySetupModel.setServerName("");
proxySetupModel.setServerName("提供者".equals(serverName) ? "" : serverName);
} }
ObservableList<Node> children = p2pRolePane.getChildren(); ObservableList<Node> children = p2pRolePane.getChildren();
JFXButton remove = proxySetupModel.isProvider ? importP2pConfig : copyP2pConfig; JFXButton remove = proxySetupModel.isProvider ? importP2pConfig : copyP2pConfig;
@ -381,7 +388,7 @@ public class MainController extends BaseController<StackPane> implements Initial
// 隧道名称 // 隧道名称
proxySetupModel.proxyNameProperty().addListener((observable, oldValue, newValue) -> { proxySetupModel.proxyNameProperty().addListener((observable, oldValue, newValue) -> {
if (proxyListView.getItems().size() > 0) if (proxyListView.getItems().size() > 0)
proxyListView.getItems().get(selectProxy()).setName(newValue); proxyListView.getItems().get(selectProxy()).setText(newValue);
}); });
// 运行状态监听 // 运行状态监听
@ -394,8 +401,11 @@ public class MainController extends BaseController<StackPane> implements Initial
startProxyBtn.getStyleClass().add(newValue ? stopClass : startClass); startProxyBtn.getStyleClass().add(newValue ? stopClass : startClass);
startProxyBtn.setText(newValue ? "停止" : "启动"); startProxyBtn.setText(newValue ? "停止" : "启动");
// 列表显示 // 列表显示
ProxyListItemView itemView = proxyListView.getItems().get(selectProxy()); ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass();
itemView.setStatus(newValue); styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS);
styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS);
styleClass.add(newValue ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
setDomainLink(); setDomainLink();
}); });
@ -424,11 +434,11 @@ public class MainController extends BaseController<StackPane> implements Initial
* 设置访问链接 * 设置访问链接
*/ */
private void setDomainLink() { private void setDomainLink() {
domainLink.setVisible(true);
copyDomainBtn.setVisible(true);
// 外网访问连接 // 外网访问连接
if (ProxyConfig.isHttp(proxySetupModel)) { if (ProxyConfig.isHttp(proxySetupModel)) {
runingLabel.setText("启动成功!立即访问 "); runingLabel.setText("启动成功!立即访问 ");
domainLink.setVisible(true);
copyDomainBtn.setVisible(true);
// http / https // http / https
String prefix = proxySetupModel.getProxyType() + "://"; String prefix = proxySetupModel.getProxyType() + "://";
domainLink.textProperty().set(prefix + proxySetupModel.getDomain() + proxySetupModel.getDomainSuffix()); domainLink.textProperty().set(prefix + proxySetupModel.getDomain() + proxySetupModel.getDomainSuffix());
@ -440,9 +450,7 @@ public class MainController extends BaseController<StackPane> implements Initial
copyDomainBtn.setVisible(false); copyDomainBtn.setVisible(false);
} else { } else {
// ssh / tcp // ssh / tcp
domainLink.textProperty().set( domainLink.textProperty().set(ProxyConfig.getServerIP(ProxyConfig.getServerNode(proxySetupModel.getServer())) + ":" + proxySetupModel.getRemotePort());
ProxyConfig.getServerIP(proxySetupModel.getServer()) + ":" + proxySetupModel.getRemotePort()
);
} }
} }
@ -537,9 +545,9 @@ public class MainController extends BaseController<StackPane> implements Initial
return; return;
ProxySetup setup = proxySetupModel.get(); ProxySetup setup = proxySetupModel.get();
// 整理服务设置
String serverName = setup.getProxy_name() + "_" + setup.getSort(); String serverName = getAccount() + "_" + setup.getSort();
setup.setServer_name(ApplicatonStore.getUserToken() + "." + EncryptionUtil.MD5_16(serverName)); setup.setServer_name(EncryptionUtil.MD5_16(serverName));
setup.setId(null); setup.setId(null);
setup.setRuning(false); setup.setRuning(false);
setup.setSort(null); setup.setSort(null);
@ -597,11 +605,11 @@ public class MainController extends BaseController<StackPane> implements Initial
// 点击隧道列表 // 点击隧道列表
proxyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { proxyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
ObservableList<ProxyListItemView> items = proxyListView.getItems(); ObservableList<Label> items = proxyListView.getItems();
if (items.size() == 0) return; if (items.size() == 0) return;
for (HBox item : items) { for (Label item : items) {
item.getStyleClass().remove(PROXY_LIST_ITEM_SELECT_CLASS); item.getStyleClass().remove(PROXY_LIST_ITEM_SELECT_CLASS);
} }
if (newValue != null) { if (newValue != null) {
@ -617,9 +625,10 @@ public class MainController extends BaseController<StackPane> implements Initial
Platform.runLater(() -> { Platform.runLater(() -> {
setup.set(false); setup.set(false);
proxySetupModel.set(proxySetup); proxySetupModel.set(proxySetup);
// 设置日志面板 if (proxySetup.getId() != null) {
FrpManager frpManager = frpUtilMap.get(String.valueOf(proxySetup.getId())); FrpManager frpManager = frpUtilMap.get(proxySetup.getId().toString());
proxyLogPane.contentProperty().set(frpManager == null ? null : frpManager.getConsole()); proxyLogPane.contentProperty().set(frpManager == null ? null : frpManager.getConsole());
}
setup.set(true); setup.set(true);
setDomainLink(); setDomainLink();
}); });
@ -716,15 +725,20 @@ public class MainController extends BaseController<StackPane> implements Initial
p2pRoleView.setDisable(close); // p2p角色 p2pRoleView.setDisable(close); // p2p角色
p2pPwdTextField.setDisable(close); // p2p访问面板 p2pPwdTextField.setDisable(close); // p2p访问面板
// 运行状态 ObservableList<String> styleClass = proxyListView.getItems().get(selectProxy()).getStyleClass();
ProxyListItemView itemView = proxyListView.getItems().get(selectProxy()); styleClass.remove(PROXY_LIST_ITEM_STOP_CLASS);
itemView.setStatus(close ? null : proxySetupModel.isRunning()); styleClass.remove(PROXY_LIST_ITEM_RUN_CLASS);
styleClass.remove(PROXY_LIST_ITEM_CLOSE_CLASS);
if (close) {
styleClass.add(PROXY_LIST_ITEM_CLOSE_CLASS);
} else {
styleClass.add(proxySetupModel.isRunning() ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
}
} }
private void initProxyListView() { private void initProxyListView() {
ObservableList<ProxySetup> proxyList = proxyList(); ObservableList<ProxySetup> proxyList = proxyList();
for (ProxySetup proxy : proxyList) { for (ProxySetup proxy : proxyList.subList(proxyListView.getItems().size(), proxyList.size())) {
setProxyListView(proxyList.indexOf(proxy), proxy); setProxyListView(proxyList.indexOf(proxy), proxy);
} }
// 设置选中 // 设置选中
@ -732,57 +746,21 @@ public class MainController extends BaseController<StackPane> implements Initial
} }
private void setProxyListView(int index, ProxySetup setup) { private void setProxyListView(int index, ProxySetup setup) {
ObservableList<ProxyListItemView> items = proxyListView.getItems(); ObservableList<Label> items = proxyListView.getItems();
// 是否添加 try {
boolean isNew = index >= items.size(); Label label = FXMLLoader.load(getClass().getResource("/fxml/proxyItem.fxml"));
ProxyListItemView itemView; label.textProperty().set(setup.getProxy_name());
if (isNew) { ObservableList<String> styleClass = label.getStyleClass();
itemView = new ProxyListItemView(); styleClass.addAll(PROXY_LIST_ITEM_CLASS, setup.isRuning() ? PROXY_LIST_ITEM_RUN_CLASS : PROXY_LIST_ITEM_STOP_CLASS);
itemView.setName(setup.getProxy_name()); if (selectProxy() == index) styleClass.add(PROXY_LIST_ITEM_SELECT_CLASS);
if (index < items.size()) {
items.set(index, label);
} else { } else {
itemView = items.get(index); items.add(label);
} }
// 运行状态 } catch (IOException e) {
itemView.setStatus(setup.isRuning()); e.printStackTrace();
// 点击删除
itemView.getDeleteBtn().setOnMouseClicked(event -> deleteProxyListItem(itemView));
// 是否选中
if (selectProxy() == index) itemView.getStyleClass().add(PROXY_LIST_ITEM_SELECT_CLASS);
// 添加
if (isNew) items.add(index, itemView);
} }
private void deleteProxyListItem(ProxyListItemView itemView) {
ObservableList<ProxyListItemView> items = proxyListView.getItems();
int index = items.indexOf(itemView);
if (index < 0) return;
if (items.size() == 1) {
AlertUtil.error("最后一个连接不可删除!").show();
return;
}
ProxySetup setup = proxyList().get(index);
// 关闭连接
String key = String.valueOf(setup.getId());
FrpManager frpManager = frpUtilMap.get(key);
if (setup.isRuning() || frpManager != null) {
frpManager.stop();
frpUtilMap.remove(key);
}
// 调用删除接口
if (setup.getId() != null) {
ProxyManager.delete(setup.getId());
}
// 列表删除
items.remove(itemView);
proxyList().remove(setup);
// 重新选择
if (selectProxy() == index) {
proxyListView.getSelectionModel().select(-1);
proxyListView.getSelectionModel().select(index < items.size() ? index : items.size() - 1);
} else if (selectProxy() > index)
selectProxy(selectProxy() - 1);
} }
/** /**

View File

@ -4,7 +4,6 @@ import javafx.application.Platform;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import top.octopusyan.http.EasyHttp; import top.octopusyan.http.EasyHttp;
@ -17,7 +16,6 @@ import top.octopusyan.utils.JsoupUtil;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*; import java.util.*;
/** /**
@ -26,7 +24,6 @@ import java.util.*;
* @description : frp 客户端 工具 * @description : frp 客户端 工具
* @create : 2022-4-7 23:19 * @create : 2022-4-7 23:19
*/ */
@Slf4j
public class FrpManager { public class FrpManager {
public static final String serverConfigHeader = "yanfrp://"; public static final String serverConfigHeader = "yanfrp://";
public static List<FrpManager> frpcList = new ArrayList<>(); public static List<FrpManager> frpcList = new ArrayList<>();
@ -97,7 +94,6 @@ public class FrpManager {
Platform.runLater(() -> { Platform.runLater(() -> {
model.setRunning(false); model.setRunning(false);
console.appendText("yan-frp-info:已停止\n"); console.appendText("yan-frp-info:已停止\n");
log.info("yan-frp-info-" + model.getProxyName() + ":已停止");
}); });
// 尝试删除缓存配置文件 // 尝试删除缓存配置文件
@ -133,7 +129,6 @@ public class FrpManager {
} }
frpcConfigFile.deleteOnExit(); frpcConfigFile.deleteOnExit();
// 写入服务配置 // 写入服务配置
FileUtils.write(frpcConfigFile, getUserFrpServerConfig(model.get().getNode()), StandardCharsets.UTF_8); FileUtils.write(frpcConfigFile, getUserFrpServerConfig(model.get().getNode()), StandardCharsets.UTF_8);
// 写入隧道配置 // 写入隧道配置
@ -141,25 +136,17 @@ public class FrpManager {
} }
public String getUserFrpServerConfig(int node) { public static String getUserFrpServerConfig(int node) {
boolean isp2p = "xtcp".equals(model.getProxyType()) || "stcp".equals(model.getProxyType());
String config = serverConfigraution.get(node); String config = serverConfigraution.get(node);
if (StringUtils.isNotEmpty(config) && !isp2p) return config; if (StringUtils.isNotEmpty(config)) return config;
try { try {
if (StringUtils.isEmpty(config)) {
String result = EasyHttp.builder() String result = EasyHttp.builder()
.api(Api.getServerConfiguration(node)) .api(Api.getServerConfiguration(node))
.pathParam(String.valueOf(node))
.execute(new ResponseClass<String>() { .execute(new ResponseClass<String>() {
}); });
config = JsoupUtil.getServerConfiguration(result); config = JsoupUtil.getServerConfiguration(result);
if (StringUtils.isNotEmpty(config)) serverConfigraution.put(node, config); if (StringUtils.isNotEmpty(config)) serverConfigraution.put(node, config);
}
// user配置 会加在访问服务端名称的前边 , 清空让访问到
if (isp2p && !model.isProvider())
config = config.replaceAll("user = .*", "");
return config; return config;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -167,14 +154,14 @@ public class FrpManager {
return null; return null;
} }
public String getProxyFrpConfig(ProxySetupModel setup) { public static String getProxyFrpConfig(ProxySetupModel setup) {
String n = "\n"; String n = "\n";
boolean ishttp = setup.getProxyType().contains("http"); boolean ishttp = setup.getProxyType().contains("http");
boolean isp2p = "xtcp".equals(setup.getProxyType()) || "stcp".equals(setup.getProxyType()); boolean isp2p = "xtcp".equals(setup.getProxyType()) || "stcp".equals(setup.getProxyType());
// 基础配置 // 基础配置
StringBuilder stringBuilder = new StringBuilder("["); StringBuilder stringBuilder = new StringBuilder("[");
// 服务名称 // 服务名称
StringBuilder serverName = new StringBuilder(setup.getProxyName()).append("_").append(setup.getSort()); StringBuilder serverName = new StringBuilder(ApplicatonStore.getAccount()+"_"+setup.getSort());
// p2p 服务名 // p2p 服务名
if (isp2p) { if (isp2p) {
if (setup.isProvider()) { if (setup.isProvider()) {
@ -182,8 +169,10 @@ public class FrpManager {
serverName = new StringBuilder(EncryptionUtil.MD5_16(serverName.toString())); serverName = new StringBuilder(EncryptionUtil.MD5_16(serverName.toString()));
} else { } else {
// 访问者 // 访问者
serverName = new StringBuilder(setup.getServerName()).append("_visitor"); serverName.append(setup.getServerName()).append("_visitor");
} }
} else {
serverName.append(ApplicatonStore.getAccount()).append("_").append(setup.getSort());
} }
stringBuilder.append(serverName).append("]\n"); stringBuilder.append(serverName).append("]\n");
stringBuilder.append("privilege_mode = true\n") stringBuilder.append("privilege_mode = true\n")
@ -285,7 +274,7 @@ public class FrpManager {
throws IOException { throws IOException {
OutputStream output = null; OutputStream output = null;
try { try {
output = Files.newOutputStream(FrpManager.frpc.toPath()); output = new FileOutputStream(FrpManager.frpc);
byte[] buf = new byte[1024]; byte[] buf = new byte[1024];
int bytesRead; int bytesRead;
while ((bytesRead = input.read(buf)) > 0) { while ((bytesRead = input.read(buf)) > 0) {
@ -304,7 +293,6 @@ public class FrpManager {
@Override @Override
public void run() { public void run() {
log.info("yan-frp-info:正在启动");
Platform.runLater(() -> console.appendText("yan-frp-info:正在启动\n")); Platform.runLater(() -> console.appendText("yan-frp-info:正在启动\n"));
try { try {
// 检查客户端文件 // 检查客户端文件
@ -315,7 +303,7 @@ public class FrpManager {
// 执行命令 // 执行命令
String command = FRPC_CLIENT_FILE_PATH + " -c " + getConfigFilePath(); String command = FRPC_CLIENT_FILE_PATH + " -c " + getConfigFilePath();
log.info(command); System.out.println(command);
exec = Runtime.getRuntime().exec(command, null, frpconfigDir); exec = Runtime.getRuntime().exec(command, null, frpconfigDir);
// 设置运行状态 // 设置运行状态
@ -328,7 +316,7 @@ public class FrpManager {
String line; String line;
while ((line = readStdout.readLine()) != null && !exit) { while ((line = readStdout.readLine()) != null && !exit) {
String finalLine = line; String finalLine = line;
log.info(line); System.out.println(line);
Platform.runLater(() -> console.appendText(finalLine + "\n")); Platform.runLater(() -> console.appendText(finalLine + "\n"));
} }
@ -337,8 +325,6 @@ public class FrpManager {
// TODO 报错 // TODO 报错
Platform.runLater(() -> console.appendText("yan-frp-error:" + e.getMessage() + "\n")); Platform.runLater(() -> console.appendText("yan-frp-error:" + e.getMessage() + "\n"));
Platform.runLater(() -> console.appendText("yan-frp-error:启动失败\n")); Platform.runLater(() -> console.appendText("yan-frp-error:启动失败\n"));
log.info("yan-frp-error:" + e.getMessage());
log.info("yan-frp-error:启动失败");
} }
} }
} }

View File

@ -61,9 +61,9 @@ public class ProxyManager {
EasyHttp.builder() EasyHttp.builder()
.api(Api.DeleteProxy()) .api(Api.DeleteProxy())
.pathParam(String.valueOf(id), csrf) .pathParam(String.valueOf(id), csrf)
.request(new OnHttpListener<String>() { .request(new OnHttpListener<Void>() {
@Override @Override
public void onSucceed(String result) { public void onSucceed(Void result) {
} }

View File

@ -44,8 +44,8 @@ public class Api {
); );
/** 获取服务器配置 */ /** 获取服务器配置 */
public static NotParamApi<String> getServerConfiguration(int node){ public static PathParamApi<String> getServerConfiguration(int node){
return new NotParamApi<>( return new PathParamApi<>(
"/?page=panel&module=configuration&server="+node, "/?page=panel&module=configuration&server="+node,
HttpConstant.Method.GET HttpConstant.Method.GET
); );
@ -75,7 +75,7 @@ public class Api {
} }
/** 删除隧道 */ /** 删除隧道 */
public static PathParamApi<String> DeleteProxy() { public static PathParamApi<Void> DeleteProxy() {
return new PathParamApi<>( return new PathParamApi<>(
"/?page=panel&module=proxies&delete={0}&csrf=" + ProxyManager.getCsrf(), "/?page=panel&module=proxies&delete={0}&csrf=" + ProxyManager.getCsrf(),
HttpConstant.Method.GET HttpConstant.Method.GET

View File

@ -43,7 +43,6 @@ public class ApplicatonStore {
private static final SimpleBooleanProperty registerSuccess = new SimpleBooleanProperty(); private static final SimpleBooleanProperty registerSuccess = new SimpleBooleanProperty();
private static final ObservableList<ProxySetup> proxyList = FXCollections.observableList(new ArrayList<>()); private static final ObservableList<ProxySetup> proxyList = FXCollections.observableList(new ArrayList<>());
private static final SimpleIntegerProperty selectProxy = new SimpleIntegerProperty(0); private static final SimpleIntegerProperty selectProxy = new SimpleIntegerProperty(0);
private static final SimpleStringProperty userToken = new SimpleStringProperty();
public static String selectProxyName = null; public static String selectProxyName = null;
public static void setAccount(String account) { public static void setAccount(String account) {
@ -122,18 +121,6 @@ public class ApplicatonStore {
proxyList.add(proxySetup); proxyList.add(proxySetup);
} }
public static SimpleStringProperty userTokenProperty() {
return userToken;
}
public static String getUserToken() {
return userToken.get();
}
public static void setUserToken(String token) {
userToken.set(token);
}
public static void selectProxy(int selectProxy) { public static void selectProxy(int selectProxy) {
ApplicatonStore.selectProxy.set(selectProxy); ApplicatonStore.selectProxy.set(selectProxy);
} }

View File

@ -1,80 +0,0 @@
package top.octopusyan.view;
import com.jfoenix.controls.JFXButton;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import org.kordamp.ikonli.javafx.FontIcon;
import java.io.IOException;
/**
* 隧道列表视图子项
*
* @author : octopus yan
* @email : octopus_yan@foxmail.com
* @description :
* @create : 2022-4-23 16:40
*/
public class ProxyListItemView extends HBox {
private String style = null;
@FXML
private Label proxyName;
@FXML
private FontIcon statusIcon;
@FXML
private JFXButton deleteBtn;
public ProxyListItemView() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/proxyItem.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
/**
* 设置运行状态
* <p> true:run false:stop null:disable
*
* @param status 运行状态
*/
public void setStatus(Boolean status) {
// 初始化样式
if (style == null) style = statusIcon.getStyle();
if (status == null) {
statusIcon.setStyle(style + "-fx-icon-color: linear-gradient(grey, #524e50);");
} else if (status) {
statusIcon.setStyle(style + "-fx-icon-color: linear-gradient(#95f257, #91e5ac);");
} else {
statusIcon.setStyle(style + "-fx-icon-color: linear-gradient(#f25757, #e591b1);");
}
}
public void setName(String proxyName) {
this.proxyName.setText(proxyName);
}
public StringProperty textProperty() {
return proxyName.textProperty();
}
public Label getProxyName() {
return proxyName;
}
public JFXButton getDeleteBtn() {
return deleteBtn;
}
public enum Status {
RUN, STOP, DISABLED
}
}

View File

@ -51,10 +51,26 @@
-fx-opacity: 0.7; -fx-opacity: 0.7;
-fx-background-color: linear-gradient(#9198e5, #57b4f2); -fx-background-color: linear-gradient(#9198e5, #57b4f2);
} }
#proxyListView .list-cell:hover lable, #proxyListView .list-cell:selected label, .proxyListItem-select label{ #proxyListView .list-cell:hover lable, #proxyListView .list-cell:selected label, .proxyListItem-select{
-fx-text-fill: white; -fx-text-fill: white;
} }
.proxyListItemLabel {
-fx-font-size: 16px;
}
.proxyListItemIcon {
-fx-font-size: 14px;
}
.proxyListItem-run FontIcon {
-fx-icon-color: linear-gradient(#95f257, #91e5ac);
}
.proxyListItem-stop FontIcon {
-fx-icon-color: linear-gradient(#f25757, #e591b1);
}
.proxyListItem-close FontIcon {
-fx-icon-color: linear-gradient(grey, #524e50);
}
/* 面板背景 */ /* 面板背景 */
#proxySetupPane, #proxyListPane, .whitePane { #proxySetupPane, #proxyListPane, .whitePane {

View File

@ -157,7 +157,7 @@
<Cursor fx:constant="HAND"/> <Cursor fx:constant="HAND"/>
</cursor> </cursor>
</JFXButton> </JFXButton>
<JFXButton fx:id="importP2pConfig" text="导入"> <JFXButton fx:id="importP2pConfig" text="从剪切板导入">
<HBox.margin> <HBox.margin>
<Insets left="10.0"/> <Insets left="10.0"/>
</HBox.margin> </HBox.margin>
@ -190,6 +190,7 @@
</HBox> </HBox>
</HBox> </HBox>
<HBox fx:id="serverNamePane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox"> <HBox fx:id="serverNamePane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
<children>
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0" <Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
styleClass="proxySetupLabel" text="服务名称"> styleClass="proxySetupLabel" text="服务名称">
<HBox.margin> <HBox.margin>
@ -219,6 +220,7 @@
<Insets left="10.0"/> <Insets left="10.0"/>
</HBox.margin> </HBox.margin>
</JFXButton> </JFXButton>
</children>
</HBox> </HBox>
<HBox fx:id="p2pPwdPane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox"> <HBox fx:id="p2pPwdPane" alignment="CENTER_LEFT" styleClass="proxySetupItemBox">
<Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0" <Label alignment="CENTER_RIGHT" prefHeight="Infinity" prefWidth="100.0"
@ -354,6 +356,7 @@
</VBox> </VBox>
<AnchorPane prefHeight="Infinity" prefWidth="Infinity" GridPane.rowIndex="1"> <AnchorPane prefHeight="Infinity" prefWidth="Infinity" GridPane.rowIndex="1">
<JFXTabPane fx:id="tabPane" prefHeight="200.0" prefWidth="520.0" styleClass="mainPane"> <JFXTabPane fx:id="tabPane" prefHeight="200.0" prefWidth="520.0" styleClass="mainPane">
<tabs>
<Tab fx:id="proxyLogPane" text=" 日志 "/> <Tab fx:id="proxyLogPane" text=" 日志 "/>
<Tab text="常见问题"> <Tab text="常见问题">
<VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" <VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"
@ -367,12 +370,13 @@
<Label prefHeight="30" text="* 请勿将非法、暴力、色情等信息映射到外网上去,一经发现立即封号"/> <Label prefHeight="30" text="* 请勿将非法、暴力、色情等信息映射到外网上去,一经发现立即封号"/>
</VBox> </VBox>
</Tab> </Tab>
<!-- TODO 使用场景 --> <!-- TODO 使用场景 -->
<!-- <Tab text="使用场景">--> <!-- <Tab text="使用场景">-->
<!-- <VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">--> <!-- <VBox minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">-->
<!-- </VBox>--> <!-- </VBox>-->
<!-- </Tab>--> <!-- </Tab>-->
</tabs>
</JFXTabPane> </JFXTabPane>
<JFXButton fx:id="clearLogBtn" layoutX="480.0" layoutY="-1.0" prefHeight="35.0" prefWidth="26.0" <JFXButton fx:id="clearLogBtn" layoutX="480.0" layoutY="-1.0" prefHeight="35.0" prefWidth="26.0"
text=""> text="">
@ -408,7 +412,7 @@
<Insets top="80.0"/> <Insets top="80.0"/>
</VBox.margin> </VBox.margin>
<font> <font>
<Font size="12.0"/> <Font size="11.0"/>
</font> </font>
<opaqueInsets> <opaqueInsets>
<Insets/> <Insets/>

View File

@ -1,18 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<?import org.kordamp.ikonli.javafx.FontIcon?> <?import org.kordamp.ikonli.javafx.FontIcon?>
<?import javafx.scene.Cursor?> <Label text=" 默认连接" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<fx:root xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"
prefWidth="170.0" maxWidth="170.0" type="HBox">
<Label fx:id="proxyName" text="默认连接" prefWidth="150" maxWidth="150">
<graphic> <graphic>
<FontIcon fx:id="statusIcon" iconLiteral="fa-circle" iconSize="14"/> <FontIcon iconLiteral="fa-circle" iconSize="14"/>
</graphic> </graphic>
<font> <font>
<Font size="16.0"/> <Font size="16.0"/>
@ -20,13 +14,4 @@
<padding> <padding>
<Insets left="10.0"/> <Insets left="10.0"/>
</padding> </padding>
</Label> </Label>
<JFXButton fx:id="deleteBtn" text="">
<graphic>
<FontIcon iconLiteral="fa-close" iconSize="14" iconColor="grey"/>
</graphic>
<cursor>
<Cursor fx:constant="HAND"/>
</cursor>
</JFXButton>
</fx:root>

View File

@ -1,18 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?> <?import com.jfoenix.controls.*?>
<?import com.jfoenix.controls.JFXPasswordField?>
<?import com.jfoenix.controls.JFXTextField?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?> <?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.*?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Rectangle?> <?import javafx.scene.shape.Rectangle?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.*?>
<StackPane fx:id="root" prefHeight="330.0" prefWidth="430.0" stylesheets="@../css/register.css"
<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"> 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"> <ImageView fx:id="registBkgPane" fitHeight="330.0" fitWidth="430.0">
<clip> <clip>
<Rectangle height="330" width="430"> <Rectangle height="330" width="430">
@ -21,45 +17,49 @@
</Rectangle> </Rectangle>
</clip> </clip>
</ImageView> </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"> <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="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="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" /> <JFXButton fx:id="closeBtn" layoutX="395.0" prefHeight="35.0" prefWidth="36.0" text="X"/>
</AnchorPane> </AnchorPane>
<AnchorPane fx:id="registBottomPane" prefHeight="200.0" prefWidth="430.0"> <AnchorPane fx:id="registBottomPane" prefHeight="200.0" prefWidth="430.0">
<JFXTextField fx:id="accooundTextField" labelFloat="true" layoutX="25.0" layoutY="15.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>
<Font size="14.0" /> <Font size="14.0"/>
</font> </font>
</JFXTextField> </JFXTextField>
<JFXTextField fx:id="emailTf" labelFloat="true" layoutX="25.0" layoutY="80.0" promptText="请输入邮箱"> <JFXTextField fx:id="emailTf" labelFloat="true" layoutX="25.0" layoutY="70.0" promptText="请输入邮箱">
<font> <font>
<Font size="14.0" /> <Font size="14.0"/>
</font> </font>
</JFXTextField> </JFXTextField>
<HBox layoutX="25.0" layoutY="140.0" prefHeight="30.0" prefWidth="170.0"> <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位验证号码"> <JFXTextField fx:id="emailCheckCodeTf" prefHeight="30.0" prefWidth="94.0" promptText="6位验证号码">
<font> <font>
<Font size="14.0" /> <Font size="14.0"/>
</font> </font>
</JFXTextField> </JFXTextField>
<JFXButton fx:id="sendCheckCodeBtn" buttonType="RAISED" prefHeight="30.0" text="发送验证码" /> <JFXButton fx:id="sendCheckCodeBtn" buttonType="RAISED" prefHeight="30.0" text="发送验证码"/>
</HBox> </HBox>
<JFXPasswordField fx:id="passwordTextField" labelFloat="true" layoutX="233.0" layoutY="60.0" promptText="请输入密码不少于6位"> <JFXPasswordField fx:id="passwordTextField" labelFloat="true" layoutX="233.0" layoutY="70.0"
promptText="请输入密码不少于6位">
<font> <font>
<Font size="14.0" /> <Font size="14.0"/>
</font> </font>
</JFXPasswordField> </JFXPasswordField>
<JFXButton fx:id="registerBtn" buttonType="RAISED" layoutX="227.0" layoutY="110.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>
<Font size="15.0" /> <Font size="15.0"/>
</font> </font>
</JFXButton> </JFXButton>
<JFXButton fx:id="loginBtn" layoutX="306.0" layoutY="24.0" text="已有账号?去登录" textFill="#00000080" /> <JFXButton fx:id="loginBtn" layoutX="306.0" layoutY="24.0" text="已有账号,去登录" textFill="#00000080"/>
<Label fx:id="appVersionLabel" disable="true" layoutX="380.0" layoutY="175.0" text="Label"> <Label fx:id="appVersionLabel" disable="true" layoutX="80.0" layoutY="171.0" text="Label">
<font> <font>
<Font size="14.0" /> <Font size="14.0"/>
</font> </font>
</Label> </Label>
</AnchorPane> </AnchorPane>

View File

@ -1,36 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false"> <configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>febs</contextName>
<property name="log.path" value="log" />
<property name="log.maxHistory" value="30" />
<property name="log.colorPattern" value="%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<property name="log.pattern" value="%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<property name="logback.logdir" value="log"/> <!--输出到控制台-->
<property name="logback.app" value="yanfrp"/> <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<Encoding>UTF-8</Encoding>
<!--输出到控制台 ConsoleAppender--> <encoder>
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender"> <pattern>${log.colorPattern}</pattern>
<!--展示格式 layout--> </encoder>
<!--
日志输出格式:%d表示日期时间%thread表示线程名%-5level级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符否则按照句点分割。 %msg日志消息%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout"> <layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} ${logback.app} [%thread] %-5level %logger{36} - %mdc{client} [%X{trace_id}] %msg%n</pattern> <pattern>${log.colorPattern}</pattern>
</layout> </layout>
</appender> </appender>
<!--输出到文件 fileLog--> <!--输出到文件-->
<appender name="fileLog" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的ThresholdFilter-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"/>
<File>${logback.logdir}/${logback.app}.info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间--> <fileNamePattern>${log.path}/info/info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<FileNamePattern>${logback.logdir}/${logback.app}_%d{yyyy-MM-dd}.info.log</FileNamePattern> <MaxHistory>${log.maxHistory}</MaxHistory>
<!--只保留最近30天的日志--> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxHistory>30</maxHistory> <!-- 日志文件的最大大小 -->
<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志--> <maxFileSize>5MB</maxFileSize>
<totalSizeCap>1GB</totalSizeCap> </timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy> </rollingPolicy>
<!--日志输出编码格式化-->
<encoder> <encoder>
<charset>UTF-8</charset> <pattern>${log.pattern}</pattern>
<pattern>%d [%thread] %-5level %logger{36} %line - %mdc{client} [%X{trace_id}] %msg%n</pattern>
</encoder> </encoder>
<!--只打印错误日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level> <level>INFO</level>
<onMatch>ACCEPT</onMatch> <onMatch>ACCEPT</onMatch>
@ -38,44 +41,17 @@
</filter> </filter>
</appender> </appender>
<appender name="fileLog-debug" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的ThresholdFilter-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"/>
<File>${logback.logdir}/${logback.app}.debug.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间--> <fileNamePattern>${log.path}/error/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<FileNamePattern>${logback.logdir}/${logback.app}_%d{yyyy-MM-dd}.debug.log</FileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!--只保留最近30天的日志--> <!-- 日志文件的最大大小 -->
<maxHistory>30</maxHistory> <maxFileSize>5MB</maxFileSize>
<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志--> </timeBasedFileNamingAndTriggeringPolicy>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<!--日志输出编码格式化-->
<encoder>
<charset>UTF-8</charset>
<pattern>%d [%thread] %-5level %logger{36} %line - %mdc{client} [%X{trace_id}] %msg%n</pattern>
</encoder>
<!--只打印错误日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 错误日志 -->
<appender name="fileLog-err" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${logback.logdir}/${logback.app}.err.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logback.logdir}/${logback.app}_%d{yyyy-MM-dd}.err.log</FileNamePattern>
<maxHistory>7</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>
<charset>UTF-8</charset> <pattern>${log.pattern}</pattern>
<pattern>%d [%thread] %-5level %logger{36} %line - %mdc{client} [%X{trace_id}] %msg%n</pattern>
</encoder> </encoder>
<!--只打印错误日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level> <level>ERROR</level>
<onMatch>ACCEPT</onMatch> <onMatch>ACCEPT</onMatch>
@ -83,13 +59,12 @@
</filter> </filter>
</appender> </appender>
<!--指定最基础的日志输出级别--> <root level="debug">
<root level="INFO"> <appender-ref ref="console" />
<!--appender将会添加到这个loger-->
<appender-ref ref="consoleLog"/>
<appender-ref ref="fileLog"/>
<appender-ref ref="fileLog-debug"/>
<appender-ref ref="fileLog-err"/>
</root> </root>
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
</configuration> </configuration>