├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── README.md ├── desgin.md ├── docker ├── application.properties ├── forward_db.mv.db └── forward_db.trace.db ├── forward_db.mv.db ├── forward_db.trace.db ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── leeroy │ │ └── forwordpanel │ │ └── forwordpanel │ │ ├── ForwordpanelApplication.java │ │ ├── common │ │ ├── AuthInterceptor.java │ │ ├── GlobalExceptionHandler.java │ │ ├── IStatusMessage.java │ │ ├── RestResult.java │ │ ├── WebCurrentData.java │ │ ├── WebException.java │ │ ├── WebThreadLocalManager.java │ │ ├── annotation │ │ │ ├── FieldAuth.java │ │ │ ├── NoAuth.java │ │ │ ├── NoIp.java │ │ │ └── NoLogin.java │ │ ├── config │ │ │ ├── SpringFoxConfig.java │ │ │ └── WebMvcConfig.java │ │ ├── enums │ │ │ ├── ForwardStatusEnum.java │ │ │ └── ServerStatusEnum.java │ │ ├── response │ │ │ ├── ApiResponse.java │ │ │ ├── PageDataResult.java │ │ │ └── ResponseResult.java │ │ └── util │ │ │ ├── BeanCopyUtil.java │ │ │ ├── BeanCopyUtilCallBack.java │ │ │ ├── CertUtil.java │ │ │ ├── DateUtils.java │ │ │ ├── DigestUtils.java │ │ │ ├── IpUtil.java │ │ │ ├── ShellUtil.java │ │ │ ├── SimpleForward.java │ │ │ ├── TokenUtil.java │ │ │ └── remotessh │ │ │ ├── KeyLoginUserInfo.java │ │ │ ├── MyUserInfo.java │ │ │ └── SSHCommandExecutor.java │ │ ├── controller │ │ ├── ConfigController.java │ │ ├── IndexController.java │ │ ├── LoginController.java │ │ ├── PortController.java │ │ ├── ResourceController.java │ │ ├── ServerController.java │ │ ├── SysConfigController.java │ │ ├── SystemController.java │ │ ├── UserController.java │ │ ├── UserPortController.java │ │ └── UserPortForwardController.java │ │ ├── dao │ │ ├── ClashDao.java │ │ ├── ForwardFlowDao.java │ │ ├── PortDao.java │ │ ├── ServerDao.java │ │ ├── SysConfigDao.java │ │ ├── UserDao.java │ │ ├── UserPortDao.java │ │ ├── UserPortForwardDao.java │ │ ├── UserServerDao.java │ │ └── UserTokenDao.java │ │ ├── dto │ │ ├── ClashNodeBase.java │ │ ├── ClashNodeTrojan.java │ │ ├── DataFlow.java │ │ ├── ForwardFlowDTO.java │ │ ├── LoginDTO.java │ │ ├── PageRequest.java │ │ ├── PortAddDTO.java │ │ ├── PortDTO.java │ │ ├── PortPageRequest.java │ │ ├── UserDTO.java │ │ ├── UserPortDTO.java │ │ ├── UserPortForwardDTO.java │ │ ├── UserPortForwardPageReq.java │ │ ├── UserPortPageRequest.java │ │ └── UserSearchDTO.java │ │ ├── job │ │ ├── CheckDDNSJob.java │ │ ├── CheckExpireJob.java │ │ ├── DataUsageJob.java │ │ └── KeepForwardJob.java │ │ ├── jpa │ │ ├── ForwardFlowRepository.java │ │ ├── PortRepository.java │ │ ├── ServerRepository.java │ │ ├── SysConfigRepository.java │ │ ├── UserPortForwardRepository.java │ │ ├── UserPortRepository.java │ │ ├── UserRepository.java │ │ └── UserServerRepository.java │ │ ├── model │ │ ├── Clash.java │ │ ├── ForwardFlow.java │ │ ├── Port.java │ │ ├── Resource.java │ │ ├── Server.java │ │ ├── SysConfig.java │ │ ├── User.java │ │ ├── UserPort.java │ │ ├── UserPortForward.java │ │ ├── UserServer.java │ │ └── UserToken.java │ │ └── service │ │ ├── ConfigService.java │ │ ├── DataUpgradeService.java │ │ ├── ForwardFlowService.java │ │ ├── ForwardService.java │ │ ├── LoginService.java │ │ ├── PortService.java │ │ ├── RemoteForwardService.java │ │ ├── ServerService.java │ │ ├── SysConfigService.java │ │ ├── UserPortForwardService.java │ │ ├── UserPortService.java │ │ ├── UserService.java │ │ └── UserTokenService.java └── resources │ ├── application.properties │ ├── data.sql │ ├── scripts │ └── turnOnNat.sh │ └── static │ ├── avatar.gif │ ├── favicon.ico │ ├── favicon.png │ ├── index.html │ ├── reward.gif │ ├── static │ ├── css │ │ ├── app.cd5c539b.css │ │ ├── chunk-22fe9ae1.d62c05ee.css │ │ ├── chunk-2d423526.e6231e8e.css │ │ ├── chunk-480b22f6.a41dcdae.css │ │ ├── chunk-4c26ca85.31d18062.css │ │ ├── chunk-632b8606.9ca93816.css │ │ ├── chunk-76cac56b.fcf8ba06.css │ │ ├── chunk-76cea4de.3c7f5ad9.css │ │ ├── chunk-9fdcb8cc.e86064da.css │ │ ├── chunk-a2c7894a.dc547638.css │ │ ├── chunk-e732e32c.1a2a53c6.css │ │ ├── chunk-elementUI.68c70ad5.css │ │ └── chunk-libs.3dfb7769.css │ ├── fonts │ │ ├── element-icons.535877f5.woff │ │ └── element-icons.732389de.ttf │ ├── img │ │ ├── 404.a57b6f31.png │ │ └── 404_cloud.0f4bc32b.png │ └── js │ │ ├── app.c13641de.js │ │ ├── chunk-22fe9ae1.8c49d34d.js │ │ ├── chunk-2d423526.31af6728.js │ │ ├── chunk-397133d0.8e76ca20.js │ │ ├── chunk-480b22f6.4f228d1f.js │ │ ├── chunk-4c26ca85.00e82d61.js │ │ ├── chunk-632b8606.5ee39ba4.js │ │ ├── chunk-6474e2f4.44dee973.js │ │ ├── chunk-76cac56b.8ccb935e.js │ │ ├── chunk-76cea4de.78bea551.js │ │ ├── chunk-9fdcb8cc.b71478a3.js │ │ ├── chunk-a2c7894a.13b2fea3.js │ │ ├── chunk-e732e32c.36ae8557.js │ │ ├── chunk-elementUI.a450811c.js │ │ └── chunk-libs.d21abeae.js │ └── usdt.jpeg └── test └── java └── com └── leeroy └── forwordpanel └── forwordpanel ├── ForwordpanelApplicationTests.java ├── MyUserInfo.java └── SSHCommandExecutor.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | src/main/resources/static/.DS_Store 7 | forward_db.mv.db 8 | forward_db.mv.db.trace.db 9 | 10 | ### STS ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBeans 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | 25 | ### NetBeans ### 26 | /nbproject/private/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/ 31 | build/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | VOLUME /tmp 3 | ADD target/forwordpanel-1.0.5-SNAPSHOT.jar app.jar 4 | ADD docker/forward_db.mv.db forward_db.mv.db 5 | ADD docker/forward_db.trace.db forward_db.trace.db 6 | COPY docker/application.properties application.properties 7 | ENTRYPOINT ["java","-jar","/app.jar"] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 闲(的)蛋(疼)中转面板 2 | 一个轻量的中转面板, 一台面板管理多台中转机器, 实现出租, 限流, 转发等功能 3 | 4 | 介绍见wiki: https://github.com/xiaoli123/forwordpanel/wiki 5 | 6 | 基于Java编写的中转管理平台(中转面板) 7 | 使用的技术: 8 | - springboot 9 | - element-ui 10 | - h2 11 | 12 | > 注意, 系统不必每个nat上都部署, 也不需要一定要部署在nat, 只要部署的机器能ssh上nat即可, 部署一套系统管理多个nat 13 | 14 | 15 | 16 | ## 主要功能 17 | 集中管理中转机器, 出租, 限流, 到期禁用等等, 具体见wiki: 18 | https://github.com/xiaoli123/forwordpanel/wiki 19 | 20 | ## 使用方式 21 | 见wiki: https://github.com/xiaoli123/forwordpanel/wiki 22 | 23 | 24 | -------------------------------------------------------------------------------- /desgin.md: -------------------------------------------------------------------------------- 1 | # 功能 2 | 3 | ### 2020-09-20 4 | - 修复流量MB展示问题 5 | - 用户流量总量统计 6 | - 流量限制 7 | - 流量重置 8 | -------------------------------------------------------------------------------- /docker/application.properties: -------------------------------------------------------------------------------- 1 | #���þ�̬��Դ·��������Զ��ŷָ� 2 | server.port=8080 3 | spring.resources.static-locations= classpath:static/,file:static/ 4 | spring.datasource.url=jdbc:h2:./forward_db;DB_CLOSE_ON_EXIT=FALSE 5 | spring.datasource.driverClassName=org.h2.Driver 6 | spring.datasource.username=sa 7 | spring.datasource.password=*yBHJHud$f45fgf 8 | # H2 9 | spring.h2.console.enabled=true 10 | spring.h2.console.path=/h2 11 | spring.h2.console.settings.web-allow-others=true 12 | spring.jpa.generate-ddl=false 13 | spring.jpa.show-sql=true 14 | spring.jpa.hibernate.ddl-auto=update 15 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 16 | panel.version=1.0.5-SNAPSHOT 17 | spring.servlet.multipart.max-file-size=1024MB 18 | spring.servlet.multipart.max-request-size=1024MB 19 | 20 | #mybatis-plus配置控制台打印完整带参数SQL语句 21 | #mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 22 | #mybatis-plus.configuration.map-underscore-to-camel-case=true 23 | panel.default-password=XDZZ@123 24 | ssh.privateKeyPath=D:\\id_rsa_old 25 | -------------------------------------------------------------------------------- /docker/forward_db.mv.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/docker/forward_db.mv.db -------------------------------------------------------------------------------- /forward_db.mv.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/forward_db.mv.db -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.1.RELEASE 9 | 10 | 11 | com.leeroy.forwordpanel 12 | forwordpanel 13 | 1.0.5-SNAPSHOT 14 | forwordpanel 15 | port forword panel 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | dnsjava 28 | dnsjava 29 | 2.1.1 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-devtools 34 | runtime 35 | true 36 | 37 | 38 | com.h2database 39 | h2 40 | runtime 41 | 42 | 43 | org.apache.commons 44 | commons-lang3 45 | 46 | 47 | commons-codec 48 | commons-codec 49 | 50 | 51 | com.baomidou 52 | mybatis-plus-boot-starter 53 | 3.3.2 54 | 55 | 56 | com.github.pagehelper 57 | pagehelper-spring-boot-starter 58 | 1.2.12 59 | 60 | 61 | org.mybatis 62 | mybatis 63 | 64 | 65 | org.mybatis 66 | mybatis-spring 67 | 68 | 69 | 70 | 71 | org.projectlombok 72 | lombok 73 | true 74 | 75 | 76 | io.springfox 77 | springfox-swagger2 78 | 2.9.2 79 | 80 | 81 | io.springfox 82 | springfox-swagger-ui 83 | 2.9.2 84 | 85 | 86 | com.jcraft 87 | jsch 88 | 0.1.55 89 | 90 | 91 | com.alibaba 92 | fastjson 93 | 1.2.68 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-starter-test 98 | test 99 | 100 | 101 | org.junit.vintage 102 | junit-vintage-engine 103 | 104 | 105 | 106 | 107 | org.apache.commons 108 | commons-io 109 | 1.3.2 110 | 111 | 112 | org.springframework.boot 113 | spring-boot-starter-data-jpa 114 | 115 | 116 | org.yaml 117 | snakeyaml 118 | 119 | 120 | 121 | 122 | 123 | docker/application.properties 124 | 125 | 126 | 127 | docker 128 | true 129 | 130 | 131 | src/main/resources 132 | false 133 | 134 | 135 | 136 | 137 | org.springframework.boot 138 | spring-boot-maven-plugin 139 | 140 | 141 | com.spotify 142 | docker-maven-plugin 143 | 1.2.1 144 | 145 | 146 | build-image 147 | package 148 | 149 | build 150 | 151 | 152 | 153 | 154 | 155 | leeroydocker/${project.artifactId} 156 | 157 | ${project.version} 158 | 159 | true 160 | ${project.basedir} 161 | 162 | 163 | / 164 | ${project.build.directory} 165 | ${project.build.finalName}.jar 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/ForwordpanelApplication.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.autoconfigure.domain.EntityScan; 7 | import org.springframework.context.annotation.ComponentScan; 8 | 9 | @SpringBootApplication 10 | @MapperScan("com.leeroy.forwordpanel.forwordpanel.dao") 11 | public class ForwordpanelApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ForwordpanelApplication.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/AuthInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 5 | import com.leeroy.forwordpanel.forwordpanel.common.util.CertUtil; 6 | import com.leeroy.forwordpanel.forwordpanel.common.util.TokenUtil; 7 | import com.leeroy.forwordpanel.forwordpanel.dao.UserDao; 8 | import com.leeroy.forwordpanel.forwordpanel.model.User; 9 | import com.leeroy.forwordpanel.forwordpanel.model.UserToken; 10 | import com.leeroy.forwordpanel.forwordpanel.service.UserTokenService; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.io.IOException; 20 | import java.io.PrintWriter; 21 | import java.util.Objects; 22 | 23 | @Component 24 | @Slf4j 25 | public class AuthInterceptor extends HandlerInterceptorAdapter { 26 | 27 | @Autowired 28 | private UserTokenService userTokenService; 29 | 30 | @Autowired 31 | private UserDao userDao; 32 | 33 | @Override 34 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 35 | log.info(">>>>>>>>>>>>>>>登录验证 {}", request.getRequestURI()); 36 | if (CertUtil.checkNoLogin(handler)) { 37 | return true; 38 | } 39 | log.info(">>>>>>>>>>>>>>>权限验证 {}", request.getRequestURI()); 40 | // 验证权限 41 | if (CertUtil.checkNoAuth(handler)) { 42 | return true; 43 | } 44 | String token = TokenUtil.getToken(request); 45 | if (StringUtils.isEmpty(token)) { 46 | renderResponse(response, "403", "登录超时, 请重新登录"); 47 | return false; 48 | //todo 跳转到登录 49 | } 50 | //根据token获取用户信息 51 | UserToken userToken = userTokenService.getUserToken(token); 52 | if (Objects.isNull(userToken) || userToken.getUserId() == null) { 53 | //todo 跳转到登录页面 54 | renderResponse(response, "403", "登录超时, 请重新登录"); 55 | return false; 56 | } 57 | userToken.setExpireTime(userToken.getExpireTime()+1000*60*30); 58 | userTokenService.updateUserToken(userToken); 59 | //当前用户 60 | User user = userDao.selectById(userToken.getUserId()); 61 | WebCurrentData.setUser(user); 62 | WebCurrentData.setUserId(user.getId()); 63 | WebCurrentData.setUserName(user.getUsername()); 64 | return true; 65 | } 66 | 67 | 68 | /** 69 | * ajax响应 70 | * 71 | * @param response 72 | * @param code 73 | * @param msg 74 | */ 75 | private void renderResponse(HttpServletResponse response, String code, String msg) { 76 | ApiResponse baseResponse = ApiResponse.error(code, msg); 77 | response.setContentType("application/json;charset=UTF-8"); 78 | response.setHeader("Pragma", "No-cache"); 79 | response.setHeader("Cache-Control", "no-cache"); 80 | response.setDateHeader("Expires", 0); 81 | PrintWriter pWriter = null; 82 | try { 83 | pWriter = response.getWriter(); 84 | pWriter.write(JSON.toJSONString(baseResponse)); 85 | pWriter.flush(); 86 | } catch (IOException e) { 87 | e.getMessage(); 88 | } finally { 89 | pWriter.close(); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.ControllerAdvice; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | 11 | @Slf4j 12 | @ControllerAdvice 13 | public class GlobalExceptionHandler { 14 | 15 | @ExceptionHandler(value = Exception.class) 16 | @ResponseBody 17 | public ApiResponse defaultErrorHandler(HttpServletRequest req, Exception e) { 18 | if (e instanceof WebException) { 19 | return ApiResponse.error(((WebException) e).getErrorCode(), ((WebException) e).getErrorMsg()); 20 | } 21 | log.error(">>>>>>>>>>>>>>GlobalExceptionHandler catch exception {}", e); 22 | return ApiResponse.error("500", "操作失败"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/IStatusMessage.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common; 2 | 3 | /** 4 | * @Title: IStatusMessage 5 | * @Description: 响应状态信息 6 | * @author: youqing 7 | * @version: 1.0 8 | * @date: 2018/11/23 9:50 9 | */ 10 | public interface IStatusMessage { 11 | 12 | String getCode(); 13 | 14 | String getMessage(); 15 | 16 | public enum SystemStatus implements IStatusMessage{ 17 | 18 | SUCCESS("0","SUCCESS"), //请求成功 19 | ERROR("404","ERROR"), //请求失败 20 | PARAM_ERROR("1002","PARAM_ERROR"), //请求参数有误 21 | SUCCESS_MATCH("1003","SUCCESS_MATCH"), //表示成功匹配 22 | NO_LOGIN("1100","NO_LOGIN"), //未登录 23 | MANY_LOGINS("1101","MANY_LOGINS"), //多用户在线(踢出用户) 24 | UPDATE("1102","UPDATE"), //用户信息或权限已更新(退出重新登录) 25 | LOCK("1111","LOCK"); //用户已锁定 26 | private String code; 27 | private String message; 28 | 29 | private SystemStatus(String code,String message){ 30 | this.code = code; 31 | this.message = message; 32 | } 33 | 34 | public String getCode(){ 35 | return this.code; 36 | } 37 | 38 | public String getMessage(){ 39 | return this.message; 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/RestResult.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @AllArgsConstructor 7 | @Data 8 | public class RestResult { 9 | 10 | private Boolean success; 11 | 12 | private String message; 13 | 14 | public static RestResult success() { 15 | return new RestResult(true, ""); 16 | } 17 | 18 | public static RestResult success(String data) { 19 | return new RestResult(true, data); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/WebCurrentData.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.User; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.io.Serializable; 9 | 10 | @Builder 11 | @Getter 12 | @Setter 13 | public class WebCurrentData implements Serializable { 14 | 15 | private User user; 16 | private Integer userId; 17 | private String userName; 18 | private String ip; 19 | 20 | public static User getUser() { 21 | return WebThreadLocalManager.get().user; 22 | } 23 | 24 | 25 | public static WebCurrentData setUser(User user) { 26 | WebCurrentData currentData = WebThreadLocalManager.get(); 27 | currentData.user = user; 28 | setUserId(user.getId()); 29 | setUserName(user.getUsername()); 30 | return currentData; 31 | } 32 | 33 | 34 | public static String getUserName() { 35 | return WebThreadLocalManager.get().userName; 36 | } 37 | 38 | public static WebCurrentData setUserName(String userName) { 39 | WebCurrentData currentData = WebThreadLocalManager.get(); 40 | currentData.userName = userName; 41 | return currentData; 42 | } 43 | 44 | public static Integer getUserId() { 45 | return WebThreadLocalManager.get().userId; 46 | } 47 | 48 | public static WebCurrentData setUserId(Integer userId) { 49 | WebCurrentData currentData = WebThreadLocalManager.get(); 50 | currentData.userId = userId; 51 | return currentData; 52 | } 53 | 54 | public static String getIp() { 55 | return WebThreadLocalManager.get().ip; 56 | } 57 | 58 | private static WebCurrentData setIp(String ip) { 59 | WebCurrentData currentData = WebThreadLocalManager.get(); 60 | currentData.ip = ip; 61 | return currentData; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/WebException.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author: meishaoyong 9 | * @Date: 2019/8/28 10:58 10 | * @Description: 11 | */ 12 | 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class WebException extends RuntimeException { 17 | 18 | private static final long serialVersionUID = 1L; 19 | private Object errorData; 20 | private Throwable cause = null; 21 | private String errorCode = "500"; 22 | private String errorMsg = ""; 23 | 24 | public WebException(String errorCode, String msg) { 25 | super(msg); 26 | this.errorMsg = msg; 27 | this.errorCode = errorCode; 28 | } 29 | 30 | public WebException(String msg) { 31 | super(msg); 32 | this.errorMsg = msg; 33 | } 34 | 35 | 36 | public WebException(Throwable cause) { 37 | super(cause); 38 | } 39 | 40 | public WebException(String errorCode, String msg, Throwable cause) { 41 | super(msg); 42 | this.cause = cause; 43 | this.errorCode = errorCode; 44 | } 45 | 46 | public WebException(String errorCode, String msg, Object data) { 47 | super(msg); 48 | this.errorData = data; 49 | this.errorCode = errorCode; 50 | } 51 | 52 | @Override 53 | public Throwable getCause() { 54 | return this.cause; 55 | } 56 | 57 | public String getExceptionMessage() { 58 | if (super.getMessage() != null) { 59 | return super.getMessage(); 60 | } 61 | if (this.cause != null) { 62 | return this.cause.toString(); 63 | } 64 | return null; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/WebThreadLocalManager.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common; 2 | 3 | 4 | public class WebThreadLocalManager { 5 | 6 | private static ThreadLocal currentDataThreadLocal = new ThreadLocal<>(); 7 | 8 | public static WebCurrentData get() { 9 | WebCurrentData currentData = currentDataThreadLocal.get(); 10 | if (currentData == null) { 11 | currentDataThreadLocal.set(WebCurrentData.builder().build()); 12 | } 13 | return currentDataThreadLocal.get(); 14 | } 15 | 16 | public static void set(WebCurrentData currentData) { 17 | currentDataThreadLocal.set(currentData); 18 | } 19 | 20 | public static void remove() { 21 | currentDataThreadLocal.remove(); 22 | } 23 | 24 | private WebThreadLocalManager() { 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/annotation/FieldAuth.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target({ ElementType.METHOD, ElementType.TYPE }) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Documented 8 | public @interface FieldAuth { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/annotation/NoAuth.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 免权限校验 7 | */ 8 | 9 | @Target({ ElementType.METHOD, ElementType.TYPE }) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | public @interface NoAuth { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/annotation/NoIp.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 免IP校验 7 | */ 8 | 9 | @Target({ ElementType.METHOD, ElementType.TYPE }) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | public @interface NoIp { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/annotation/NoLogin.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 免登录校验 7 | */ 8 | 9 | @Target({ ElementType.METHOD, ElementType.TYPE }) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | public @interface NoLogin { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/config/SpringFoxConfig.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.PathSelectors; 6 | import springfox.documentation.builders.RequestHandlerSelectors; 7 | import springfox.documentation.spi.DocumentationType; 8 | import springfox.documentation.spring.web.plugins.Docket; 9 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 10 | 11 | @Configuration 12 | @EnableSwagger2 13 | public class SpringFoxConfig { 14 | @Bean 15 | public Docket api() { 16 | return new Docket(DocumentationType.SWAGGER_2) 17 | .select() 18 | .apis(RequestHandlerSelectors.any()) 19 | .paths(PathSelectors.any()) 20 | .build(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.config; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.AuthInterceptor; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.Ordered; 8 | import org.springframework.core.annotation.Order; 9 | import org.springframework.web.cors.CorsConfiguration; 10 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 11 | import org.springframework.web.filter.CorsFilter; 12 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 13 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 14 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | @Configuration 20 | @Order(Ordered.HIGHEST_PRECEDENCE) 21 | public class WebMvcConfig implements WebMvcConfigurer { 22 | 23 | 24 | @Autowired 25 | private AuthInterceptor authInterceptor; 26 | 27 | @Override 28 | public void addInterceptors(InterceptorRegistry registry) { 29 | registry.addInterceptor(authInterceptor).excludePathPatterns("/clash/**","/swagger-resources/**", "/*/api-docs/**", "/images/**", "/lib/**", "/h-ui/**", "/h-ui.admin/**", "/**/favicon.ico") 30 | .excludePathPatterns("/**/*.woff2", "/**/*.css", "/**/*.html", "/**/*.js", "/**/*.png", "/**/*.gif", "/**/*.jpg", "/**/*.jpeg", "/h2/**", "/login", "/error"); 31 | } 32 | 33 | @Override 34 | public void addCorsMappings(CorsRegistry registry) { 35 | registry.addMapping("/**") 36 | .allowedOrigins("*") 37 | .allowCredentials(true) 38 | .allowedHeaders("*") 39 | .allowedMethods("GET", "POST", "DELETE", "PUT") 40 | .maxAge(3600); 41 | } 42 | 43 | private CorsConfiguration buildConfig() { 44 | CorsConfiguration corsConfiguration = new CorsConfiguration(); 45 | List list = new ArrayList<>(); 46 | list.add("*"); 47 | corsConfiguration.setAllowedOrigins(list); 48 | corsConfiguration.addAllowedOrigin("*"); 49 | corsConfiguration.addAllowedHeader("*"); 50 | corsConfiguration.addAllowedMethod("*"); 51 | corsConfiguration.setAllowCredentials(true); 52 | return corsConfiguration; 53 | } 54 | 55 | @Bean 56 | public CorsFilter corsFilter() { 57 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 58 | source.registerCorsConfiguration("/**", buildConfig()); 59 | return new CorsFilter(source); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/enums/ForwardStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.enums; 2 | 3 | /** 4 | * @program: forwordpanel 5 | * @description: 服务器状态 6 | * @author: liruifeng 7 | * @created: 2020/10/13 16:55 8 | */ 9 | public enum ForwardStatusEnum { 10 | INIT(1, "未设置"), STOP(2, "停止中"), START(3, "启动中"), 11 | STOPPED(4, "已停止"), STARTED(5, "已启动"),STOP_FAIL(6, "停止失败"),START_FAIL(7, "启动失败"); 12 | 13 | ForwardStatusEnum(int code, String name) { 14 | this.code = code; 15 | this.name = name; 16 | } 17 | 18 | private int code; 19 | private String name; 20 | 21 | public int getCode() { 22 | return code; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/enums/ServerStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.enums; 2 | 3 | /** 4 | * @program: forwordpanel 5 | * @description: 服务器状态 6 | * @author: liruifeng 7 | * @created: 2020/10/13 16:55 8 | */ 9 | public enum ServerStatusEnum { 10 | INIT(1, "初始化中"), CONNECT_FAIL(2, "连接失败"), ONLINE(3, "在线"); 11 | 12 | ServerStatusEnum(int code, String name) { 13 | this.code = code; 14 | this.name = name; 15 | } 16 | 17 | private int code; 18 | private String name; 19 | 20 | public int getCode() { 21 | return code; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/response/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.response; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.IStatusMessage; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @Author: meishaoyong 10 | * @Date: 2019/11/8 13:41 11 | * @Description: 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class ApiResponse { 17 | 18 | private String code = "0"; 19 | 20 | private String msg = ""; 21 | 22 | private T data; 23 | 24 | private Boolean success; 25 | 26 | public static ApiResponse ok() { 27 | ApiResponse apiResponse = new ApiResponse(); 28 | apiResponse.setSuccess(true); 29 | return apiResponse; 30 | } 31 | 32 | public static ApiResponse ok(T data) { 33 | ApiResponse result = ok(); 34 | result.setData(data); 35 | return result; 36 | } 37 | 38 | public static ApiResponse ok(String code, String msg, T data) { 39 | ApiResponse result = ok(); 40 | result.setCode(code); 41 | result.setMsg(msg); 42 | result.setData(data); 43 | return result; 44 | } 45 | 46 | public static ApiResponse ok(IStatusMessage.SystemStatus systemStatus) { 47 | ApiResponse result = new ApiResponse(); 48 | result.setCode(systemStatus.getCode()); 49 | result.setMsg(systemStatus.getMessage()); 50 | result.setSuccess(true); 51 | return result; 52 | } 53 | 54 | public static ApiResponse error(String code, String msg) { 55 | ApiResponse result = new ApiResponse(); 56 | result.setCode(code); 57 | result.setMsg(msg); 58 | result.setSuccess(false); 59 | return result; 60 | } 61 | 62 | public static ApiResponse error(IStatusMessage.SystemStatus systemStatus) { 63 | ApiResponse result = new ApiResponse(); 64 | result.setCode(systemStatus.getCode()); 65 | result.setMsg(systemStatus.getMessage()); 66 | result.setSuccess(false); 67 | return result; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/response/PageDataResult.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.response; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @Title: PageDataResult 7 | * @Description: 封装DTO分页数据(记录数和所有记录) 8 | * @author: youqing 9 | * @version: 1.0 10 | * @date: 2018/11/21 11:15 11 | */ 12 | public class PageDataResult { 13 | 14 | private Integer code=200; 15 | 16 | //总记录数量 17 | private Long total; 18 | 19 | private List list; 20 | 21 | public Integer getCode() { 22 | return code; 23 | } 24 | 25 | public void setCode(Integer code) { 26 | this.code = code; 27 | } 28 | 29 | public Long getTotal() { 30 | return total; 31 | } 32 | 33 | public void setTotal(Long total) { 34 | this.total = total; 35 | } 36 | 37 | public List getList() { 38 | return list; 39 | } 40 | 41 | public void setList(List list) { 42 | this.list = list; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "PageDataResult{" + 48 | "code=" + code + 49 | ", totals=" + total + 50 | ", list=" + list + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/response/ResponseResult.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.response; 2 | 3 | 4 | import com.leeroy.forwordpanel.forwordpanel.common.IStatusMessage; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @Title: ResponseResult 10 | * @Description: 前端请求响应结果,code:编码,message:描述,obj对象,可以是单个数据对象,数据列表或者PageInfo 11 | * @author: youqing 12 | * @version: 1.0 13 | * @date: 2018/11/23 9:48 14 | */ 15 | public class ResponseResult implements Serializable{ 16 | 17 | private String code; 18 | private String message; 19 | private Object obj; 20 | 21 | public ResponseResult() { 22 | this.code = IStatusMessage.SystemStatus.SUCCESS.getCode(); 23 | this.message = IStatusMessage.SystemStatus.SUCCESS.getMessage(); 24 | } 25 | 26 | public ResponseResult(IStatusMessage statusMessage){ 27 | this.code = statusMessage.getCode(); 28 | this.message = statusMessage.getMessage(); 29 | 30 | } 31 | 32 | public String getCode() { 33 | return code; 34 | } 35 | 36 | public void setCode(String code) { 37 | this.code = code; 38 | } 39 | 40 | public String getMessage() { 41 | return message; 42 | } 43 | 44 | public void setMessage(String message) { 45 | this.message = message; 46 | } 47 | 48 | public Object getObj() { 49 | return obj; 50 | } 51 | 52 | public void setObj(Object obj) { 53 | this.obj = obj; 54 | } 55 | 56 | @Override public String toString() { 57 | return "ResponseResult{" + "code='" + code + '\'' + ", message='" 58 | + message + '\'' + ", obj=" + obj + '}'; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/BeanCopyUtil.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | import org.springframework.beans.BeanUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.function.Supplier; 8 | 9 | public class BeanCopyUtil extends BeanUtils { 10 | 11 | /** 12 | * 集合数据的拷贝 13 | * @param sources: 数据源类 14 | * @param target: 目标类::new(eg: UserVO::new) 15 | * @return 16 | */ 17 | public static List copyListProperties(List sources, Supplier target) { 18 | return copyListProperties(sources, target, null); 19 | } 20 | 21 | 22 | /** 23 | * 带回调函数的集合数据的拷贝(可自定义字段拷贝规则) 24 | * @param sources: 数据源类 25 | * @param target: 目标类::new(eg: UserVO::new) 26 | * @param callBack: 回调函数 27 | * @return 28 | */ 29 | public static List copyListProperties(List sources, Supplier target, BeanCopyUtilCallBack callBack) { 30 | List list = new ArrayList<>(sources.size()); 31 | for (S source : sources) { 32 | T t = target.get(); 33 | copyProperties(source, t); 34 | list.add(t); 35 | if (callBack != null) { 36 | // 回调 37 | callBack.callBack(source, t); 38 | } 39 | } 40 | return list; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/BeanCopyUtilCallBack.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | @FunctionalInterface 4 | public interface BeanCopyUtilCallBack { 5 | 6 | /** 7 | * 定义默认回调方法 8 | * @param t 9 | * @param s 10 | */ 11 | void callBack(S t, T s); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/CertUtil.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | 4 | import com.leeroy.forwordpanel.forwordpanel.common.annotation.NoAuth; 5 | import com.leeroy.forwordpanel.forwordpanel.common.annotation.NoIp; 6 | import com.leeroy.forwordpanel.forwordpanel.common.annotation.NoLogin; 7 | import org.springframework.web.method.HandlerMethod; 8 | 9 | public class CertUtil { 10 | 11 | public static boolean checkNoLogin(Object handler){ 12 | if (!(handler instanceof HandlerMethod)) { 13 | return true; 14 | } 15 | boolean result=false; 16 | HandlerMethod handlerMethod = (HandlerMethod) handler; 17 | //String methodName = handlerMethod.getMethod().getName(); 18 | // 判断是否需要检查登录(方法) 19 | NoLogin noLogin = handlerMethod.getMethod().getAnnotation(NoLogin.class); 20 | if (null != noLogin) { 21 | result= true; 22 | } 23 | // 判断是否需要检查登录(类) 24 | noLogin = handlerMethod.getMethod().getDeclaringClass().getAnnotation(NoLogin.class); 25 | if (null != noLogin) { 26 | result= true; 27 | } 28 | return result; 29 | } 30 | 31 | public static boolean checkNoAuth(Object handler){ 32 | if (!(handler instanceof HandlerMethod)) { 33 | return true; 34 | } 35 | boolean result=false; 36 | HandlerMethod handlerMethod = (HandlerMethod) handler; 37 | //String methodName = handlerMethod.getMethod().getName(); 38 | // 判断是否需要检查权限(方法) 39 | NoAuth noAuth = handlerMethod.getMethod().getAnnotation(NoAuth.class); 40 | if (null != noAuth) { 41 | result= true; 42 | } 43 | // 判断是否需要检查权限(类) 44 | noAuth = handlerMethod.getMethod().getDeclaringClass().getAnnotation(NoAuth.class); 45 | if (null != noAuth) { 46 | result= true; 47 | } 48 | return result; 49 | } 50 | 51 | public static boolean checkNoIp(Object handler){ 52 | if (!(handler instanceof HandlerMethod)) { 53 | return true; 54 | } 55 | boolean result=false; 56 | HandlerMethod handlerMethod = (HandlerMethod) handler; 57 | String methodName = handlerMethod.getMethod().getName(); 58 | // 判断是否需要检查IP(方法) 59 | NoIp noIp = handlerMethod.getMethod().getAnnotation(NoIp.class); 60 | if (null != noIp) { 61 | result= true; 62 | } 63 | // 判断是否需要检查IP(类) 64 | noIp = handlerMethod.getMethod().getDeclaringClass().getAnnotation(NoIp.class); 65 | if (null != noIp) { 66 | result= true; 67 | } 68 | return result; 69 | } 70 | 71 | public static boolean checkIpInRange(String ip, String cidr) { 72 | boolean result=false; 73 | if(cidr.contains("/")){ 74 | String[] ips = ip.split("\\."); 75 | int ipAddr = (Integer.parseInt(ips[0]) << 24) 76 | | (Integer.parseInt(ips[1]) << 16) 77 | | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]); 78 | int type = Integer.parseInt(cidr.replaceAll(".*/", "")); 79 | int mask = 0xFFFFFFFF << (32 - type); 80 | String cidrIp = cidr.replaceAll("/.*", ""); 81 | String[] cidrIps = cidrIp.split("\\."); 82 | int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) 83 | | (Integer.parseInt(cidrIps[1]) << 16) 84 | | (Integer.parseInt(cidrIps[2]) << 8) 85 | | Integer.parseInt(cidrIps[3]); 86 | 87 | result= (ipAddr & mask) == (cidrIpAddr & mask); 88 | }else{ 89 | result=ip.equals(cidr); 90 | } 91 | return result; 92 | 93 | } 94 | public static void main(String[] args) { 95 | System.out.println(checkIpInRange("192.168.1.128", "192.168.1.64/26")); 96 | System.out.println(checkIpInRange("192.168.1.2", "192.168.0.0/23")); 97 | System.out.println(checkIpInRange("192.168.0.1", "192.168.0.0/24")); 98 | System.out.println(checkIpInRange("192.168.0.0", "192.168.0.0/32")); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/DateUtils.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | import java.text.DateFormat; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Calendar; 7 | import java.util.Date; 8 | 9 | /** 10 | * @Title: DateUtils 11 | * @Description: 12 | * @author: youqing 13 | * @version: 1.0 14 | * @date: 2018/5/26 9:58 15 | */ 16 | public class DateUtils { 17 | 18 | /** 19 | * 20 | * 功能描述: 21 | * 22 | * @param: 获取当前系统时间 yyyy-MM-dd HH:mm:ss 23 | * @return: 24 | * @auther: youqing 25 | * @date: 2018/5/26 9:59 26 | */ 27 | public static String getCurrentDate(){ 28 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 29 | String date = df.format(System.currentTimeMillis()); 30 | return date; 31 | } 32 | 33 | 34 | /** 35 | * 36 | * 功能描述: 37 | * 38 | * @param: date类 获取当前系统时间 yyyy-MM-dd HH:mm:ss 39 | * @return: 40 | * @auther: youqing 41 | * @date: 2018/5/26 10:39 42 | */ 43 | public static Date getCurrentDateToDate () { 44 | DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); 45 | String date = df.format(System.currentTimeMillis()); 46 | Date d = null; 47 | try { 48 | d = df.parse( date.toString( ) ); 49 | } catch ( ParseException e ) { 50 | e.printStackTrace( ); 51 | } 52 | return d; 53 | } 54 | 55 | /** 56 | * 增加时间单位:天 57 | * @param day 58 | * @return 59 | */ 60 | public static String getCurrentAddDay(int day) { 61 | SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); 62 | Calendar cal = Calendar.getInstance(); 63 | cal.setTime(new Date()); 64 | cal.add(Calendar.DATE, day); 65 | return sdf.format(cal.getTime()); 66 | } 67 | 68 | /** 69 | * 增加时间单位:分钟 70 | * @param minute 71 | * @return 72 | */ 73 | public static String getCurrentAddMin(int minute) { 74 | SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); 75 | Calendar cal = Calendar.getInstance(); 76 | cal.setTime(new Date()); 77 | cal.add(Calendar.MINUTE, minute); 78 | return sdf.format(cal.getTime()); 79 | } 80 | 81 | /** 82 | * 获取当前时间 83 | * @return 84 | */ 85 | public static String getNowDateString ( ) { 86 | Date d = new Date(); 87 | SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd"); 88 | return sdf.format( d ); 89 | } 90 | 91 | /** 92 | * 把Date转为String 93 | * @param date 94 | * @param format 95 | * @return 96 | */ 97 | public static String getFormatTime(Date date, String format) { 98 | SimpleDateFormat sdf = new SimpleDateFormat(format); 99 | return sdf.format(date); 100 | } 101 | 102 | /** 103 | * 增加时间单位:天 104 | * @param day 105 | * @return 106 | */ 107 | public static Date addDay(int day) { 108 | Calendar cal = Calendar.getInstance(); 109 | cal.setTime(new Date()); 110 | cal.add(Calendar.DATE, day); 111 | return cal.getTime(); 112 | } 113 | 114 | /** 115 | * 增加时间单位:天 116 | * @param date 117 | * @param day 118 | * @return 119 | */ 120 | public static Date addDay(Date date, int day) { 121 | Calendar cal = Calendar.getInstance(); 122 | cal.setTime(date); 123 | cal.add(Calendar.DATE, day); 124 | return cal.getTime(); 125 | } 126 | 127 | /** 128 | * 减去多少天 129 | * @param date 130 | * @param day 131 | * @return 132 | */ 133 | public static Date minusDay(Date date, int day) { 134 | return addDay(date, -day); 135 | } 136 | 137 | public static void main(String[] args) { 138 | System.out.println(getCurrentAddDay(2)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/DigestUtils.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | /** 4 | * @Title: DigestUtils 5 | * @Description: 6 | * @author: youqing 7 | * @version: 1.0 8 | * @date: 2018/9/11 15:09 9 | */ 10 | public class DigestUtils { 11 | 12 | /** 13 | * 14 | * 功能描述: MD5加密账号密码 15 | * 16 | * @param: 17 | * @return: 18 | * @auther: youqing 19 | * @date: 2018/9/11 15:11 20 | */ 21 | public static String Md5(String userName,String password){ 22 | return org.apache.commons.codec.digest.DigestUtils.md5Hex(userName+password); 23 | } 24 | 25 | 26 | public static String Md5(String userName,String password, Long time){ 27 | return org.apache.commons.codec.digest.DigestUtils.md5Hex(userName+password+time); 28 | } 29 | 30 | public static void main(String[] args) { 31 | System.out.println(org.apache.commons.codec.digest.DigestUtils.md5Hex("admin"+"123456")); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/IpUtil.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | import java.net.*; 4 | import java.util.Enumeration; 5 | 6 | public class IpUtil { 7 | public static final String DEFAULT_IP = "127.0.0.1"; 8 | 9 | /** 10 | * 直接根据第一个网卡地址作为其内网ipv4地址,避免返回 127.0.0.1 11 | * 12 | * @return 13 | */ 14 | public static String getLocalIpByNetcard() { 15 | try { 16 | for (Enumeration e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) { 17 | NetworkInterface item = e.nextElement(); 18 | for (InterfaceAddress address : item.getInterfaceAddresses()) { 19 | if (item.isLoopback() || !item.isUp()) { 20 | continue; 21 | } 22 | if (address.getAddress() instanceof Inet4Address) { 23 | Inet4Address inet4Address = (Inet4Address) address.getAddress(); 24 | return inet4Address.getHostAddress(); 25 | } 26 | } 27 | } 28 | return InetAddress.getLocalHost().getHostAddress(); 29 | } catch (SocketException | UnknownHostException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } 33 | 34 | public static String getLocalIP() { 35 | try { 36 | return InetAddress.getLocalHost().getHostAddress(); 37 | } catch (UnknownHostException e) { 38 | throw new RuntimeException(e); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/ShellUtil.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.InputStreamReader; 7 | 8 | @Slf4j 9 | public class ShellUtil { 10 | 11 | public static String execShell(String shell) { 12 | 13 | log.info("execute shell: {}", shell); 14 | try { 15 | if(isWindows()){ 16 | return ""; 17 | } 18 | String[] cmd = new String[]{"/bin/sh", "-c", shell}; 19 | Process ps = Runtime.getRuntime().exec(cmd); 20 | ps.waitFor(); 21 | BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream())); 22 | StringBuffer sb = new StringBuffer(); 23 | String line; 24 | while ((line = br.readLine()) != null) { 25 | sb.append(line).append("\n"); 26 | } 27 | String result = sb.toString(); 28 | log.info("shell result: {}", result); 29 | return sb.toString(); 30 | } catch (Exception e) { 31 | log.error("----error-----"); 32 | e.printStackTrace(); 33 | } 34 | return null; 35 | } 36 | 37 | public static boolean isWindows() { 38 | return System.getProperties().getProperty("os.name").toUpperCase().indexOf("WINDOWS") != -1; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/SimpleForward.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.net.ServerSocket; 9 | import java.net.Socket; 10 | import java.util.*; 11 | 12 | @Slf4j 13 | public class SimpleForward extends Thread { 14 | // 服务器 15 | private ServerSocket server; 16 | // 监听本地端口 17 | private int localPort = 1080; 18 | // 目标主机地址 19 | private String remoteHostAddr = "0.0.0.0"; 20 | // 目标主机端口 21 | private int remoteHostPort = 8388; 22 | // 设置超时时间 30s 23 | private static int TIMEOUT = 30; 24 | // 结束标志 25 | volatile boolean runFlag = true; 26 | // 客户端列表 用于删除失效连接和超时连接 27 | private static HashMap clientList = new HashMap<>(); 28 | 29 | public static void main(String[] args) { 30 | new SimpleForward(1234, "143.92.44.202", 443); 31 | } 32 | 33 | 34 | public SimpleForward(int localPort, String remoteHostAddr, int remoteHostPort) { 35 | this.localPort = localPort; 36 | this.remoteHostAddr = remoteHostAddr; 37 | this.remoteHostPort = remoteHostPort; 38 | } 39 | 40 | public void stopForward(){ 41 | try { 42 | server.close(); 43 | } catch (IOException e) { 44 | log.error("close error", e); 45 | } 46 | this.runFlag = false; 47 | } 48 | 49 | @Override 50 | public void run() { 51 | try { 52 | this.server = new ServerSocket(this.localPort); 53 | log.info("服务器开启成功"); 54 | log.info("监听端口 : " + this.localPort); 55 | } catch (IOException e) { 56 | log.info("服务器开启失败"); 57 | log.info(e.getMessage()); 58 | log.info("退出运行"); 59 | return; 60 | } 61 | new Thread(new AutoDestroy()).start(); 62 | while (runFlag) { 63 | Socket socket = null; 64 | Socket remoteHost = null; 65 | try { 66 | socket = server.accept(); 67 | // 接收到请求就把socket扔进map,value为刷新时间 68 | clientList.put(socket, new Date()); 69 | String address = socket.getRemoteSocketAddress().toString(); 70 | log.debug("新连接 : " + address); 71 | // 建立与目标主机的连接 72 | remoteHost = new Socket(this.remoteHostAddr, this.remoteHostPort); 73 | log.debug("连接地址 : " + this.remoteHostAddr + ":" + this.remoteHostPort); 74 | // 端口转发 75 | new Thread(new Switch(socket, remoteHost, remoteHost.getInputStream(), socket.getOutputStream())).start(); 76 | new Thread(new Switch(socket, remoteHost, socket.getInputStream(), remoteHost.getOutputStream())).start(); 77 | } catch (IOException e) { 78 | log.info("连接异常"); 79 | log.info(e.getMessage()); 80 | close(socket); 81 | close(remoteHost); 82 | } 83 | } 84 | log.info("closed..."); 85 | } 86 | 87 | private void close(Socket socket) { 88 | try { 89 | if (socket != null) { 90 | socket.close(); 91 | } 92 | } catch (IOException e) { 93 | e.printStackTrace(); 94 | } 95 | } 96 | 97 | // 用于端口转发 98 | private class Switch implements Runnable { 99 | private Socket host; 100 | private Socket remoteHost; 101 | private InputStream in; 102 | private OutputStream out; 103 | 104 | Switch(Socket host, Socket remoteHost, InputStream in, OutputStream out) { 105 | this.host = host; 106 | this.remoteHost = remoteHost; 107 | this.in = in; 108 | this.out = out; 109 | } 110 | 111 | @Override 112 | public void run() { 113 | int length; 114 | byte[] buffer = new byte[1024]; 115 | try { 116 | while (!host.isClosed() && (length = in.read(buffer)) > -1) { 117 | clientList.put(host, new Date()); 118 | out.write(buffer, 0, length); 119 | } 120 | } catch (IOException e) { 121 | log.debug("连接关闭"); 122 | } finally { 123 | close(host); 124 | close(remoteHost); 125 | } 126 | } 127 | } 128 | 129 | // 用于清除失效连接和超时连接 130 | private class AutoDestroy implements Runnable { 131 | @Override 132 | public void run() { 133 | Timer timer = new Timer(); 134 | timer.schedule(new TimerTask() { 135 | @Override 136 | public void run() { 137 | List list = new LinkedList<>(); 138 | log.info("开始扫描失效与超时连接"); 139 | Date start = new Date(); 140 | for (Socket socket : clientList.keySet()) { 141 | Date lastTime = clientList.get(socket); 142 | long time = new Date().getTime() - lastTime.getTime(); 143 | if (socket.isClosed() || time / 1000 >= TIMEOUT) { 144 | list.add(socket); 145 | } 146 | } 147 | log.info("找到" + list.size() + "个,用时 : " + (new Date().getTime() - start.getTime()) + "毫秒"); 148 | log.info("开始清除失效与超时连接"); 149 | for (Socket socket : list) { 150 | try { 151 | clientList.remove(socket); 152 | socket.close(); 153 | } catch (IOException e) { 154 | e.printStackTrace(); 155 | } 156 | } 157 | log.info("当前连接数 : " + clientList.size()); 158 | } 159 | }, 30 * 1000, 30 * 1000); 160 | } 161 | } 162 | 163 | // private class Terminal implements Runnable { 164 | // private String format = "yyyy-MM-dd HH:mm:ss"; 165 | // private SimpleDateFormat dateFormat = new SimpleDateFormat(format); 166 | // 167 | // @Override 168 | // public void run() { 169 | // while (!server.isClosed()) { 170 | // System.out.print("请输入命令 : "); 171 | // String cmd = reader.readKB(); 172 | // handler(cmd); 173 | // } 174 | // } 175 | // 176 | // private void handler(String cmd) { 177 | // switch (cmd) { 178 | // case "status": 179 | // log.info("当前时间 : " + dateFormat.format(new Date())); 180 | // log.info("总连接数 : " + clientList.size()); 181 | // for (Socket socket : clientList.keySet()) { 182 | // long time = new Date().getTime() - clientList.get(socket).getTime(); 183 | // log.info("<" + socket.getRemoteSocketAddress().toString() + "> " + time / 1000); 184 | // } 185 | // break; 186 | // } 187 | // } 188 | // } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/TokenUtil.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import javax.servlet.http.Cookie; 6 | import javax.servlet.http.HttpServletRequest; 7 | 8 | @Slf4j 9 | public class TokenUtil { 10 | 11 | private TokenUtil() { 12 | } 13 | 14 | private static final String TOKEN_KEY = "123456abc"; 15 | 16 | /** 17 | * @param userName 用户名 18 | * @param password 密码 19 | * @return 20 | */ 21 | public static String makeToken(String userName, String password) { 22 | Long now = System.currentTimeMillis(); 23 | return DigestUtils.Md5(userName, password, now); 24 | } 25 | 26 | 27 | /** 28 | * get token from request 29 | * 30 | * @param request 31 | * @return 32 | */ 33 | public static String getToken(HttpServletRequest request) { 34 | return request.getHeader("X-Token"); 35 | } 36 | 37 | private static String getTokenFromCookies(HttpServletRequest request) { 38 | Cookie[] cookies = request.getCookies(); 39 | if(cookies==null||cookies.length==0){ 40 | return null; 41 | } 42 | for (Cookie cookie : cookies) { 43 | if ("token".equals(cookie.getName())) { 44 | return cookie.getValue(); 45 | } 46 | } 47 | return null; 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/remotessh/KeyLoginUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util.remotessh; 2 | 3 | import com.jcraft.jsch.UIKeyboardInteractive; 4 | import com.jcraft.jsch.UserInfo; 5 | 6 | public class KeyLoginUserInfo implements UserInfo, UIKeyboardInteractive { 7 | 8 | @Override 9 | 10 | public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt, boolean[] echo) { 11 | return null; 12 | } 13 | 14 | @Override 15 | public String getPassphrase() { 16 | return null; 17 | } 18 | 19 | 20 | @Override 21 | 22 | public String getPassword() { 23 | 24 | 25 | return null; 26 | 27 | } 28 | 29 | 30 | @Override 31 | 32 | public boolean promptPassword(String message) { 33 | 34 | // TODO Auto-generated method stub 35 | 36 | return true; 37 | 38 | } 39 | 40 | 41 | @Override 42 | 43 | public boolean promptPassphrase(String message) { 44 | 45 | // TODO Auto-generated method stub 46 | 47 | return true; 48 | 49 | } 50 | 51 | 52 | @Override 53 | 54 | public boolean promptYesNo(String message) { 55 | return true; 56 | } 57 | 58 | 59 | @Override 60 | 61 | public void showMessage(String message) { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/remotessh/MyUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util.remotessh; 2 | 3 | import com.jcraft.jsch.UserInfo; 4 | 5 | /** 6 | * This class provide interface to feedback information to the user. 7 | */ 8 | public class MyUserInfo implements UserInfo { 9 | private String password; 10 | 11 | private String passphrase; 12 | 13 | @Override 14 | public String getPassphrase() { 15 | System.out.println("MyUserInfo.getPassphrase()"); 16 | return null; 17 | } 18 | 19 | @Override 20 | public String getPassword() { 21 | System.out.println("MyUserInfo.getPassword()"); 22 | return null; 23 | } 24 | 25 | @Override 26 | public boolean promptPassphrase(final String arg0) { 27 | System.out.println("MyUserInfo.promptPassphrase()"); 28 | System.out.println(arg0); 29 | return false; 30 | } 31 | 32 | @Override 33 | public boolean promptPassword(final String arg0) { 34 | System.out.println("MyUserInfo.promptPassword()"); 35 | System.out.println(arg0); 36 | return false; 37 | } 38 | 39 | @Override 40 | public boolean promptYesNo(final String arg0) { 41 | System.out.println("MyUserInfo.promptYesNo()"); 42 | System.out.println(arg0); 43 | if (arg0.contains("The authenticity of host")) { 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | @Override 50 | public void showMessage(final String arg0) { 51 | System.out.println("MyUserInfo.showMessage()"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/common/util/remotessh/SSHCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.common.util.remotessh; 2 | 3 | import com.jcraft.jsch.*; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Server; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.ClassPathUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.core.io.ClassPathResource; 9 | import org.springframework.util.CollectionUtils; 10 | import org.springframework.util.ResourceUtils; 11 | import org.springframework.web.servlet.resource.PathResourceResolver; 12 | 13 | import java.io.*; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Properties; 17 | import java.util.Vector; 18 | 19 | /** 20 | * This class provide interface to execute command on remote Linux. 21 | */ 22 | @Slf4j 23 | public class SSHCommandExecutor { 24 | private String ipAddress; 25 | 26 | private String username; 27 | 28 | private String password; 29 | 30 | private String privateKeyPath; 31 | 32 | public int port = 34204; 33 | 34 | private Vector stdout; 35 | 36 | public String getIpAddress() { 37 | return ipAddress; 38 | } 39 | 40 | public int getPort() { 41 | return port; 42 | } 43 | 44 | public SSHCommandExecutor(final String ipAddress, final String username, final String password) { 45 | this.ipAddress = ipAddress; 46 | this.username = username; 47 | this.password = password; 48 | stdout = new Vector<>(); 49 | } 50 | 51 | public SSHCommandExecutor(Server server) { 52 | this.ipAddress = server.getHost(); 53 | this.port = server.getPort(); 54 | this.username = server.getUsername(); 55 | // this.privateKeyPath = server.getPassword(); 56 | this.password = server.getPassword(); 57 | stdout = new Vector<>(); 58 | } 59 | 60 | 61 | public SSHCommandExecutor(Server server, final String username, final String privateKeyPath) { 62 | this.ipAddress = server.getHost(); 63 | this.port = server.getPort(); 64 | this.username = username; 65 | this.privateKeyPath = privateKeyPath; 66 | stdout = new Vector<>(); 67 | } 68 | 69 | public int execute(final String... commandList) { 70 | stdout.clear(); 71 | int returnCode = 0; 72 | JSch jsch = new JSch(); 73 | MyUserInfo userInfo = new MyUserInfo(); 74 | try { 75 | Session session = jsch.getSession(username, ipAddress, port); 76 | session.setPassword(password); 77 | session.setUserInfo(userInfo); 78 | session.connect(); 79 | for (String command : commandList) { 80 | // Create and connect channel. 81 | Channel channel = session.openChannel("exec"); 82 | ((ChannelExec) channel).setCommand(command); 83 | channel.setInputStream(null); 84 | BufferedReader input = new BufferedReader(new InputStreamReader(channel 85 | .getInputStream())); 86 | channel.connect(); 87 | log.info("The remote command is: {}", command); 88 | 89 | // Get the output of remote command. 90 | String line; 91 | while ((line = input.readLine()) != null) { 92 | stdout.add(line); 93 | } 94 | input.close(); 95 | // Get the return code only after the channel is closed. 96 | if (channel.isClosed()) { 97 | returnCode = channel.getExitStatus(); 98 | } 99 | // Disconnect the channel and session. 100 | channel.disconnect(); 101 | } 102 | 103 | session.disconnect(); 104 | } catch (Exception e) { 105 | log.error("执行shell失败", e); 106 | } 107 | log.info("shell result: {}", StringUtils.join(stdout)); 108 | return returnCode; 109 | } 110 | 111 | /** 112 | * 执行脚本 113 | * @param script 114 | * @return 115 | */ 116 | public void executeScript(final String script) { 117 | try { 118 | List commandList = new ArrayList<>(); 119 | ClassPathResource resource = new ClassPathResource("scripts/" + script); 120 | BufferedReader br = new BufferedReader(new InputStreamReader(resource.getInputStream())); 121 | String str; 122 | while ((str = br.readLine()) != null) { 123 | commandList.add(str); 124 | } 125 | execute(commandList.toArray(new String[]{})); 126 | } catch (Exception e) { 127 | e.printStackTrace(); 128 | } 129 | } 130 | 131 | public Vector getResultSet() { 132 | return stdout; 133 | } 134 | 135 | public String getResult() { 136 | return CollectionUtils.isEmpty(stdout) ? "" : stdout.get(0); 137 | } 138 | 139 | public static void main(final String[] args) { 140 | SSHCommandExecutor sshExecutor = new SSHCommandExecutor("120.241.154.4", "root", "28L8CegNk9"); 141 | long stsart = System.currentTimeMillis(); 142 | sshExecutor.execute("netstat -tunlp", "netstat -tunlp", "netstat -tunlp", "netstat -tunlp"); 143 | Vector stdout = sshExecutor.getResultSet(); 144 | for (String str : stdout) { 145 | System.out.println(str); 146 | } 147 | System.out.println(System.currentTimeMillis() - stsart); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/ConfigController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.leeroy.forwordpanel.forwordpanel.common.annotation.NoLogin; 5 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 6 | import com.leeroy.forwordpanel.forwordpanel.dto.PageRequest; 7 | import com.leeroy.forwordpanel.forwordpanel.model.Clash; 8 | import com.leeroy.forwordpanel.forwordpanel.service.ConfigService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | import java.io.OutputStream; 16 | 17 | @RequestMapping("config") 18 | @Controller 19 | public class ConfigController { 20 | 21 | @Autowired 22 | private ConfigService configService; 23 | 24 | 25 | @ResponseBody 26 | @PostMapping("getPage") 27 | public ApiResponse getPage(@RequestBody PageRequest pageRequest) { 28 | return ApiResponse.ok(configService.findClashList(pageRequest)); 29 | } 30 | 31 | @ResponseBody 32 | @PostMapping("save") 33 | public ApiResponse save(@RequestBody Clash clash) { 34 | return configService.save(clash); 35 | } 36 | 37 | @ResponseBody 38 | @GetMapping("delete") 39 | public ApiResponse delete(String id) { 40 | configService.delClash(id); 41 | return ApiResponse.ok(); 42 | } 43 | 44 | @GetMapping("getNodeList") 45 | @ResponseBody 46 | public ApiResponse getNodeList(String id){ 47 | return configService.getNodeList(id); 48 | } 49 | 50 | @PostMapping("saveNodeList") 51 | @ResponseBody 52 | public ApiResponse saveNodeList(@RequestBody JSONArray nodeList, @RequestParam String id){ 53 | return configService.saveNodeList(nodeList, id); 54 | } 55 | 56 | 57 | @NoLogin 58 | @ResponseBody 59 | @GetMapping("/{id}") 60 | public void getClashFileById(@PathVariable String id, HttpServletResponse response) { 61 | Clash clash = configService.findClashById(id); 62 | try { 63 | String text = clash.getText(); 64 | byte[] bytes = text.getBytes("UTF-8"); 65 | response.setContentType("application/octet-stream;charset=UTF-8"); 66 | response.setHeader("Content-Disposition", "attachment; filename=\"" + id + ".yml\""); 67 | response.addHeader("Content-Length", "" + bytes.length); 68 | OutputStream stream = null; 69 | stream = response.getOutputStream(); 70 | stream.write(bytes); 71 | stream.flush(); 72 | stream.close(); 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.ui.Model; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | /** 12 | * @Title: LoginController 13 | * @Description: 14 | * @author: youqing 15 | * @version: 1.0 16 | * @date: 2018/11/20 11:39 17 | */ 18 | @Controller 19 | public class IndexController { 20 | 21 | private Logger logger = LoggerFactory.getLogger(this.getClass()); 22 | 23 | 24 | @RequestMapping("login") 25 | public String tologin(){ 26 | logger.info("定向登陆页"); 27 | return "login"; 28 | } 29 | 30 | 31 | @RequestMapping("home") 32 | public String home(Model model){ 33 | logger.info("定向主页"); 34 | model.addAttribute("username", WebCurrentData.getUserName()); 35 | return "home"; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.annotation.NoAuth; 4 | import com.leeroy.forwordpanel.forwordpanel.common.annotation.NoLogin; 5 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 6 | import com.leeroy.forwordpanel.forwordpanel.common.util.TokenUtil; 7 | import com.leeroy.forwordpanel.forwordpanel.dto.LoginDTO; 8 | import com.leeroy.forwordpanel.forwordpanel.service.LoginService; 9 | import com.leeroy.forwordpanel.forwordpanel.service.UserTokenService; 10 | import io.swagger.annotations.Api; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.validation.annotation.Validated; 14 | import org.springframework.web.bind.annotation.*; 15 | import org.springframework.web.servlet.ModelAndView; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | 19 | @Slf4j 20 | @Api(value = "登录") 21 | @RestController 22 | @Validated 23 | public class LoginController { 24 | 25 | @Autowired 26 | private LoginService loginService; 27 | 28 | @Autowired 29 | private UserTokenService userTokenService; 30 | 31 | @NoLogin 32 | @NoAuth 33 | @PostMapping("/login") 34 | @ResponseBody 35 | public ApiResponse login(@RequestBody LoginDTO loginDTO) { 36 | return loginService.login(loginDTO); 37 | } 38 | 39 | @NoLogin 40 | @NoAuth 41 | @GetMapping("/authToken") 42 | @ResponseBody 43 | public ApiResponse authToken(String token){ 44 | return ApiResponse.ok(userTokenService.getUserByToken(token)); 45 | } 46 | 47 | @RequestMapping("/logout") 48 | @NoLogin 49 | public ApiResponse logout(HttpServletRequest request) { 50 | String token = TokenUtil.getToken(request); 51 | userTokenService.delToken(token); 52 | ModelAndView modelAndView = new ModelAndView(); 53 | modelAndView.setViewName("login"); 54 | return ApiResponse.ok(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/PortController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 4 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 5 | import com.leeroy.forwordpanel.forwordpanel.dto.PageRequest; 6 | import com.leeroy.forwordpanel.forwordpanel.dto.PortAddDTO; 7 | import com.leeroy.forwordpanel.forwordpanel.dto.PortPageRequest; 8 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 9 | import com.leeroy.forwordpanel.forwordpanel.service.PortService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | @RequestMapping("port") 15 | @Controller 16 | public class PortController { 17 | 18 | @Autowired 19 | private PortService portService; 20 | 21 | @ResponseBody 22 | @PostMapping("getPortList") 23 | public ApiResponse getPortList(@RequestBody PortPageRequest pageRequest) { 24 | return ApiResponse.ok(portService.findListByServer(pageRequest)); 25 | } 26 | 27 | @ResponseBody 28 | @PostMapping("getPage") 29 | public ApiResponse getUserPortList(@RequestBody PageRequest pageRequest) { 30 | return ApiResponse.ok(portService.findList(pageRequest)); 31 | } 32 | 33 | @ResponseBody 34 | @GetMapping("getFreePortList") 35 | public ApiResponse getFreePortList(Integer serverId) { 36 | return ApiResponse.ok(portService.findFreePortList(serverId)); 37 | } 38 | 39 | @ResponseBody 40 | @GetMapping("getFreePortPage") 41 | public ApiResponse getFreePortPage(PortPageRequest portPageRequest) { 42 | return ApiResponse.ok(portService.findFreePortPage(portPageRequest)); 43 | } 44 | 45 | 46 | @ResponseBody 47 | @RequestMapping(value = "save", method = RequestMethod.POST) 48 | public ApiResponse saveUserPort(@RequestBody Port port) { 49 | if(WebCurrentData.getUser().getUserType()>0){ 50 | return ApiResponse.error("403", "您没有权限执行此操作"); 51 | } 52 | return portService.save(port); 53 | } 54 | 55 | @ResponseBody 56 | @RequestMapping(value = "batchSave", method = RequestMethod.POST) 57 | public ApiResponse saveUserPort(@RequestBody PortAddDTO port) { 58 | if(WebCurrentData.getUser().getUserType()>0){ 59 | return ApiResponse.error("403", "您没有权限执行此操作"); 60 | } 61 | return portService.save(port); 62 | } 63 | 64 | @ResponseBody 65 | @RequestMapping(value = "batchDelete", method = RequestMethod.POST) 66 | public ApiResponse batchDeleteUserPort(@RequestBody PortAddDTO port) { 67 | if(WebCurrentData.getUser().getUserType()>0){ 68 | return ApiResponse.error("403", "您没有权限执行此操作"); 69 | } 70 | return portService.batchDelete(port); 71 | } 72 | 73 | @ResponseBody 74 | @GetMapping("delete") 75 | public ApiResponse delete(Integer id) { 76 | if(WebCurrentData.getUser().getUserType()>0){ 77 | return ApiResponse.error("403", "您没有权限执行此操作"); 78 | } 79 | return portService.delete(id); 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/ResourceController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 4 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 5 | import com.leeroy.forwordpanel.forwordpanel.model.Resource; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * 资源管理 17 | */ 18 | @RestController 19 | public class ResourceController { 20 | 21 | private static Map> USER_TYPE_RESOURCE = new HashMap<>(); 22 | 23 | /** 24 | * 初始化权限 25 | */ 26 | static { 27 | List adminResourceList = new ArrayList<>(); 28 | adminResourceList.add(new Resource(1, "看板", "/dashboard", "dashboard")); 29 | adminResourceList.add(new Resource(2, "服务器管理", "/server", "el-icon-cpu")); 30 | // adminResourceList.add(new Resource(3, "端口管理", "/port", "el-icon-s-help")); 31 | adminResourceList.add(new Resource(4, "账号管理", "/user", "el-icon-user-solid")); 32 | adminResourceList.add(new Resource(5, "中转管理", "/forward", "el-icon-attract")); 33 | adminResourceList.add(new Resource(6, "配置文件", "/config", "el-icon-tickets")); 34 | adminResourceList.add(new Resource(7, "系统设置", "/system", "el-icon-s-tools")); 35 | // adminResourceList.add(new Resource(7, "更新记录", "/system", "el-icon-notebook-2")); 36 | adminResourceList.add(new Resource(8, "打赏", "/reward", "el-icon-sugar")); 37 | 38 | 39 | USER_TYPE_RESOURCE.put(0, adminResourceList); 40 | List userResourceList = new ArrayList<>(); 41 | userResourceList.add(new Resource(1, "看板", "/dashboard", "dashboard")); 42 | userResourceList.add(new Resource(5, "中转管理", "/forward", "el-icon-attract")); 43 | userResourceList.add(new Resource(6, "配置文件", "/config", "el-icon-tickets")); 44 | // userResourceList.add(new Resource(6, "更新记录", "/config", "el-icon-notebook-2")); 45 | userResourceList.add(new Resource(8, "打赏", "/reward", "el-icon-sugar")); 46 | USER_TYPE_RESOURCE.put(1, userResourceList); 47 | } 48 | 49 | /** 50 | * 获取资源 51 | * 52 | * @return 53 | */ 54 | @GetMapping("/getMenus") 55 | public ApiResponse getResource() { 56 | Integer userType = WebCurrentData.getUser().getUserType(); 57 | return ApiResponse.ok(USER_TYPE_RESOURCE.get(userType)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/ServerController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 4 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 5 | import com.leeroy.forwordpanel.forwordpanel.dto.PageRequest; 6 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 7 | import com.leeroy.forwordpanel.forwordpanel.model.Server; 8 | import com.leeroy.forwordpanel.forwordpanel.service.PortService; 9 | import com.leeroy.forwordpanel.forwordpanel.service.ServerService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | @RequestMapping("server") 15 | @Controller 16 | public class ServerController { 17 | 18 | @Autowired 19 | private ServerService serverService; 20 | 21 | 22 | @ResponseBody 23 | @PostMapping("getPage") 24 | public ApiResponse getPage(@RequestBody PageRequest pageRequest) { 25 | return ApiResponse.ok(serverService.getServerPage(pageRequest)); 26 | } 27 | 28 | 29 | @ResponseBody 30 | @GetMapping("getList") 31 | public ApiResponse getList() { 32 | return ApiResponse.ok(serverService.findList()); 33 | } 34 | 35 | 36 | @ResponseBody 37 | @GetMapping("getForwardServerList") 38 | public ApiResponse getForwardServerList(Integer userId) { 39 | return ApiResponse.ok(serverService.getForwardServerList(userId)); 40 | } 41 | 42 | @ResponseBody 43 | @RequestMapping(value = "save", method = RequestMethod.POST) 44 | public ApiResponse saveUserPort(@RequestBody Server server) { 45 | if(WebCurrentData.getUser().getUserType()>0){ 46 | return ApiResponse.error("403", "您没有权限执行此操作"); 47 | } 48 | return serverService.save(server); 49 | } 50 | 51 | @ResponseBody 52 | @GetMapping("delete") 53 | public ApiResponse delete(Integer id) { 54 | if(WebCurrentData.getUser().getUserType()>0){ 55 | return ApiResponse.error("403", "您没有权限执行此操作"); 56 | } 57 | return serverService.delete(id); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/SysConfigController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 4 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 5 | import com.leeroy.forwordpanel.forwordpanel.dto.PageRequest; 6 | import com.leeroy.forwordpanel.forwordpanel.dto.PortAddDTO; 7 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 8 | import com.leeroy.forwordpanel.forwordpanel.model.SysConfig; 9 | import com.leeroy.forwordpanel.forwordpanel.service.PortService; 10 | import com.leeroy.forwordpanel.forwordpanel.service.SysConfigService; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | @RequestMapping("sysConfig") 16 | @Controller 17 | public class SysConfigController { 18 | 19 | @Autowired 20 | private SysConfigService sysConfigService; 21 | 22 | 23 | @ResponseBody 24 | @RequestMapping(value = "save", method = RequestMethod.POST) 25 | public ApiResponse saveUserPort(@RequestBody SysConfig sysConfig) { 26 | if (WebCurrentData.getUser().getUserType() > 0) { 27 | return ApiResponse.error("403", "您没有权限执行此操作"); 28 | } 29 | return sysConfigService.save(sysConfig); 30 | } 31 | 32 | @ResponseBody 33 | @GetMapping("getSysConfig") 34 | public ApiResponse delete() { 35 | return sysConfigService.getConfig(); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/SystemController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 4 | import com.leeroy.forwordpanel.forwordpanel.service.DataUpgradeService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | /** 13 | * @program: forwordpanel 14 | * @description: 系统设置 15 | * @author: liruifeng 16 | * @created: 2020/09/20 13:38 17 | */ 18 | @RequestMapping("system") 19 | @RestController 20 | public class SystemController { 21 | 22 | @Autowired 23 | private DataUpgradeService upgradeService; 24 | 25 | /** 26 | * 导出数据库 27 | */ 28 | @GetMapping("exportData") 29 | public ApiResponse exportData(){ 30 | return upgradeService.exportData(); 31 | } 32 | 33 | /** 34 | * 导入数据库 35 | * @param file 36 | * @return 37 | */ 38 | @PostMapping("importData") 39 | public ApiResponse exportDB(MultipartFile file){ 40 | return upgradeService.importData(file); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | 4 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 5 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 6 | import com.leeroy.forwordpanel.forwordpanel.dto.UserPortDTO; 7 | import com.leeroy.forwordpanel.forwordpanel.dto.UserSearchDTO; 8 | import com.leeroy.forwordpanel.forwordpanel.model.User; 9 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortService; 10 | import com.leeroy.forwordpanel.forwordpanel.service.UserService; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.util.CollectionUtils; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * 用户管理 24 | */ 25 | @Controller 26 | @RequestMapping("user") 27 | public class UserController { 28 | 29 | private Logger logger = LoggerFactory.getLogger(this.getClass()); 30 | 31 | @Autowired 32 | private UserService userService; 33 | 34 | @Autowired 35 | private UserPortService userPortService; 36 | 37 | 38 | /** 39 | * 设置密码 40 | * 41 | * @param pwd 42 | * @param isPwd 43 | * @return 44 | */ 45 | @RequestMapping("setPwd") 46 | @ResponseBody 47 | public ApiResponse setPassword(String pwd, String isPwd) { 48 | logger.info("进行密码重置"); 49 | Map data = new HashMap(); 50 | if (!pwd.equals(isPwd)) { 51 | return ApiResponse.error("400", "两次密码不一致"); 52 | } 53 | //获取当前登陆的用户信息 54 | User user = WebCurrentData.getUser(); 55 | userService.updatePwd(user.getUsername(), pwd); 56 | return ApiResponse.ok(); 57 | } 58 | 59 | /** 60 | * 功能描述: 跳到系统用户列表 61 | * 62 | * @param: 63 | * @return: 64 | * @auther: youqing 65 | * @date: 2018/11/21 13:50 66 | */ 67 | @RequestMapping("/userManage") 68 | public String userManage() { 69 | return "user/userManage"; 70 | } 71 | 72 | /** 73 | * 用户分页 74 | * 75 | * @param userSearch 76 | * @return 77 | */ 78 | @RequestMapping(value = "/getPage", method = RequestMethod.POST) 79 | @ResponseBody 80 | public ApiResponse getUserPage(@RequestBody UserSearchDTO userSearch) { 81 | // 获取用户列表 82 | return ApiResponse.ok(userService.getUserPage(userSearch)); 83 | } 84 | 85 | /** 86 | * 用户列表 87 | * 88 | * @param userSearch 89 | * @return 90 | */ 91 | @RequestMapping(value = "/getList", method = RequestMethod.POST) 92 | @ResponseBody 93 | public ApiResponse getUserList(@RequestBody UserSearchDTO userSearch) { 94 | // 获取用户列表 95 | return ApiResponse.ok(userService.getUserList(userSearch)); 96 | } 97 | 98 | 99 | /** 100 | * 新增/更新用户 101 | * 102 | * @param user 103 | * @return 104 | */ 105 | @RequestMapping(value = "/save", method = RequestMethod.POST) 106 | @ResponseBody 107 | public ApiResponse setUser(@RequestBody User user) { 108 | logger.info("设置用户[新增或更新]!user:" + user); 109 | if (user.getId() == null) { 110 | return userService.addUser(user); 111 | } else { 112 | return userService.updateUser(user); 113 | } 114 | } 115 | 116 | 117 | /** 118 | * 禁用用户 119 | * 120 | * @param id 121 | * @return 122 | */ 123 | @RequestMapping(value = "/disable", method = RequestMethod.GET) 124 | @ResponseBody 125 | public ApiResponse disable(@RequestParam("id") Integer id) { 126 | if (WebCurrentData.getUser().getUserType() > 0) { 127 | return ApiResponse.error("403", "您没有权限执行此操作"); 128 | } 129 | return userService.disableUser(id); 130 | } 131 | 132 | /** 133 | * 启用用户 134 | * 135 | * @param id 136 | * @return 137 | */ 138 | @RequestMapping(value = "/enable", method = RequestMethod.GET) 139 | @ResponseBody 140 | public ApiResponse enable(@RequestParam("id") Integer id) { 141 | if (WebCurrentData.getUser().getUserType() > 0) { 142 | return ApiResponse.error("403", "您没有权限执行此操作"); 143 | } 144 | return userService.enableUser(id); 145 | } 146 | 147 | /** 148 | * 获取用户信息 149 | * 150 | * @return 151 | */ 152 | @RequestMapping(value = "/getCurrentUser", method = RequestMethod.GET) 153 | @ResponseBody 154 | public ApiResponse getCurrentUser() { 155 | return ApiResponse.ok(userService.getCurrentUser()); 156 | } 157 | 158 | /** 159 | * 启用用户 160 | * 161 | * @param id 162 | * @return 163 | */ 164 | @GetMapping(value = "/delete") 165 | @ResponseBody 166 | public ApiResponse delete(@RequestParam("id") Integer id) { 167 | if (WebCurrentData.getUser().getUserType() > 0) { 168 | return ApiResponse.error("403", "您没有权限执行此操作"); 169 | } 170 | List userPortList = userPortService.findUserPortList(id); 171 | if (!CollectionUtils.isEmpty(userPortList)) { 172 | return ApiResponse.error("401", "请先删除用户端口"); 173 | } 174 | return userService.delUser(id); 175 | } 176 | 177 | /** 178 | * 启用用户 179 | * 180 | * @param id 181 | * @return 182 | */ 183 | @GetMapping(value = "/resetFlow") 184 | @ResponseBody 185 | public ApiResponse resetFlow(@RequestParam("id") Integer id) { 186 | if (WebCurrentData.getUser().getUserType() > 0) { 187 | return ApiResponse.error("403", "您没有权限执行此操作"); 188 | } 189 | return userService.resetUserFlow(id); 190 | } 191 | 192 | /** 193 | * 获取用户流量信息 194 | * 195 | * @param userId 196 | * @return 197 | */ 198 | @GetMapping(value = "/getUserDetail") 199 | @ResponseBody 200 | public ApiResponse getUserDetail(@RequestParam(value = "userId", required = false) Integer userId) { 201 | return ApiResponse.ok(userService.getUserDetail(userId)); 202 | } 203 | 204 | /** 205 | * 获取用户详细流量信息 206 | * 207 | * @param userId 208 | * @return 209 | */ 210 | @GetMapping(value = "/getForwardFlow") 211 | @ResponseBody 212 | public ApiResponse getForwardFlow(@RequestParam(value = "userId", required = false) Integer userId) { 213 | return ApiResponse.ok(userService.getForwardFlow(userId)); 214 | } 215 | 216 | /** 217 | * 获取用户详细流量信息 218 | * 219 | * @param forwardId 220 | * @return 221 | */ 222 | @GetMapping(value = "/getPortForwardFlow") 223 | @ResponseBody 224 | public ApiResponse getPortForwardFlow(@RequestParam(value = "forwardId", required = false) Integer forwardId) { 225 | return ApiResponse.ok(userService.getPortForwardFlow(forwardId)); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/UserPortController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | 4 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 5 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 6 | import com.leeroy.forwordpanel.forwordpanel.common.response.PageDataResult; 7 | import com.leeroy.forwordpanel.forwordpanel.common.response.ResponseResult; 8 | import com.leeroy.forwordpanel.forwordpanel.dto.UserPortPageRequest; 9 | import com.leeroy.forwordpanel.forwordpanel.model.UserPort; 10 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortService; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * @Title: UserController 20 | * @Description: 系统用户管理 21 | * @author: youqing 22 | * @version: 1.0 23 | * @date: 2018/11/20 15:17 24 | */ 25 | @RestController 26 | @RequestMapping("userport") 27 | public class UserPortController { 28 | 29 | @Autowired 30 | private UserPortService userPortService; 31 | 32 | @PostMapping("getList") 33 | public ApiResponse getUserPortList(@RequestBody UserPortPageRequest pageRequest) { 34 | return ApiResponse.ok(userPortService.findUserPortList(pageRequest)); 35 | } 36 | 37 | @PostMapping("save") 38 | public ApiResponse saveUserPort(@RequestBody List userPortList) { 39 | if(WebCurrentData.getUser().getUserType()>0){ 40 | return ApiResponse.error("403", "您没有权限执行此操作"); 41 | } 42 | return userPortService.save(userPortList); 43 | } 44 | 45 | @GetMapping("delete") 46 | public ApiResponse delete(Integer id) { 47 | if(WebCurrentData.getUser().getUserType()>0){ 48 | return ApiResponse.error("403", "您没有权限执行此操作"); 49 | } 50 | userPortService.delUserPort(id); 51 | return ApiResponse.ok(); 52 | } 53 | 54 | /** 55 | * 禁用用户 56 | * 57 | * @param id 58 | * @return 59 | */ 60 | @RequestMapping(value = "/disable", method = RequestMethod.GET) 61 | @ResponseBody 62 | public ApiResponse disable(@RequestParam("id") Integer id) { 63 | if(WebCurrentData.getUser().getUserType()>0){ 64 | return ApiResponse.error("403", "您没有权限执行此操作"); 65 | } 66 | return userPortService.disablePort(id); 67 | } 68 | 69 | /** 70 | * 启用用户 71 | * 72 | * @param id 73 | * @return 74 | */ 75 | @RequestMapping(value = "/enable", method = RequestMethod.GET) 76 | @ResponseBody 77 | public ApiResponse enable(@RequestParam("id") Integer id) { 78 | if(WebCurrentData.getUser().getUserType()>0){ 79 | return ApiResponse.error("403", "您没有权限执行此操作"); 80 | } 81 | return userPortService.enablePort(id); 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/controller/UserPortForwardController.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.controller; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 4 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 5 | import com.leeroy.forwordpanel.forwordpanel.dto.PageRequest; 6 | import com.leeroy.forwordpanel.forwordpanel.dto.UserPortForwardPageReq; 7 | import com.leeroy.forwordpanel.forwordpanel.model.User; 8 | import com.leeroy.forwordpanel.forwordpanel.model.UserPortForward; 9 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortForwardService; 10 | import com.leeroy.forwordpanel.forwordpanel.service.UserService; 11 | import org.apache.commons.lang3.time.DateFormatUtils; 12 | import org.apache.commons.lang3.time.DateUtils; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | @RequestMapping("forward") 19 | @Controller 20 | public class UserPortForwardController { 21 | 22 | @Autowired 23 | private UserPortForwardService forwardService; 24 | 25 | /** 26 | * 中转列表 27 | * 28 | * @return 29 | */ 30 | @ResponseBody 31 | @PostMapping("getPage") 32 | public ApiResponse getList(@RequestBody UserPortForwardPageReq pageRequest) { 33 | return forwardService.getUserForwardList(pageRequest); 34 | } 35 | 36 | /** 37 | * 开始中转 38 | * 39 | * @param userPortForward 40 | * @return 41 | */ 42 | @ResponseBody 43 | @PostMapping("start") 44 | public ApiResponse startForward(@RequestBody UserPortForward userPortForward) { 45 | userPortForward.setUserId(userPortForward.getUserId()==null?WebCurrentData.getUserId():userPortForward.getUserId()); 46 | return forwardService.startForward(userPortForward, true); 47 | } 48 | 49 | /** 50 | * 停止中转 51 | * 52 | * @param userPortForward 53 | * @return 54 | */ 55 | @ResponseBody 56 | @PostMapping("stop") 57 | public ApiResponse stopFroward(@RequestBody UserPortForward userPortForward) { 58 | userPortForward.setUserId(WebCurrentData.getUserId()); 59 | return forwardService.stopForward(userPortForward); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/ClashDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Clash; 5 | import com.leeroy.forwordpanel.forwordpanel.model.UserPort; 6 | import org.apache.ibatis.annotations.Update; 7 | 8 | public interface ClashDao extends BaseMapper { 9 | 10 | 11 | @Update("update clash set disabled=#{disabled} where id=#{id}") 12 | void updateDisable(Boolean disabled, Integer id); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/ForwardFlowDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.ForwardFlow; 5 | import com.leeroy.forwordpanel.forwordpanel.model.UserPortForward; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Update; 8 | 9 | @Mapper 10 | public interface ForwardFlowDao extends BaseMapper { 11 | 12 | 13 | @Update("update forward_flow set disabled=#{disabled} where id=#{id}") 14 | void updateDisable(Boolean disabled, Integer id); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/PortDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 5 | import org.apache.ibatis.annotations.Select; 6 | import org.apache.ibatis.annotations.Update; 7 | 8 | import java.util.List; 9 | 10 | public interface PortDao extends BaseMapper { 11 | 12 | 13 | @Update("update port set disabled=#{disabled} where id=#{id}") 14 | void updateDisable(Boolean disabled, Integer id); 15 | 16 | @Select("select a.* from port a where not exists (select 1 from user_port up where up.port_id = a.id and up.deleted=0) and a.deleted=0 and a.server_id = #{serverId} order by a.create_time desc") 17 | List selectServerFreePort(Integer serverId); 18 | 19 | @Update("delete from port where local_port>= #{startPort} and local_port <=#{endPort} and server_id = #{serverId}") 20 | void batchDelete(Integer startPort, Integer endPort, Integer serverId); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/ServerDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 5 | import com.leeroy.forwordpanel.forwordpanel.model.Server; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.apache.ibatis.annotations.Update; 8 | 9 | import java.util.List; 10 | 11 | public interface ServerDao extends BaseMapper { 12 | 13 | 14 | @Update("update server set disabled=#{disabled} where id=#{id}") 15 | void updateDisable(Boolean disabled, Integer id); 16 | 17 | 18 | @Select("select s.* from server s, user_server us where s.id = us.server_id and s.deleted = false and us.deleted = false and us.user_id =#{userId} ") 19 | List selectUserServer(Integer userId); 20 | 21 | @Select("select s.* from server s where exists ( select 1 from user_port_forward upf where s.id = upf.server_id and s.deleted = false and upf.deleted = false and upf.user_id =#{userId}) ") 22 | List selectForwardServer(Integer userId); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/SysConfigDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.SysConfig; 5 | 6 | public interface SysConfigDao extends BaseMapper { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.dto.UserSearchDTO; 5 | import com.leeroy.forwordpanel.forwordpanel.model.User; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Select; 8 | import org.apache.ibatis.annotations.Update; 9 | 10 | import java.util.List; 11 | 12 | @Mapper 13 | public interface UserDao extends BaseMapper { 14 | 15 | 16 | @Select("") 37 | List getUsers(UserSearchDTO userSearch); 38 | 39 | @Select("") 46 | User getUserByUserName(String username, Integer id); 47 | 48 | @Update("update sys_user set password=#{password} where username=#{username}") 49 | void updatePwd(String username, String password); 50 | 51 | @Update("update sys_user set disabled=#{disabled} where id=#{id}") 52 | void updateDisable(Boolean disabled, Integer id); 53 | 54 | @Update("update sys_user set deleted=true where id=#{id}") 55 | void deleteUser(Integer id); 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/UserPortDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 5 | import com.leeroy.forwordpanel.forwordpanel.model.UserPort; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.apache.ibatis.annotations.Update; 8 | 9 | import java.util.List; 10 | 11 | public interface UserPortDao extends BaseMapper { 12 | 13 | 14 | @Update("update user_port set disabled=#{disabled} where id=#{id}") 15 | void updateDisable(Boolean disabled, Integer id); 16 | 17 | @Select("select p.* from user_port up, port p where up.port_id = p.id and up.deleted = false and p.deleted = false and p.local_port=#{localPort} and p.server_id =#{serverId}") 18 | List selectByLocalPort(Integer localPort, Integer serverId); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/UserPortForwardDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.UserPortForward; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Update; 7 | 8 | @Mapper 9 | public interface UserPortForwardDao extends BaseMapper { 10 | 11 | 12 | @Update("update user_port_forward set disabled=#{disabled} where id=#{id}") 13 | void updateDisable(Boolean disabled, Integer id); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/UserServerDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Server; 5 | import com.leeroy.forwordpanel.forwordpanel.model.UserServer; 6 | import org.apache.ibatis.annotations.Update; 7 | 8 | public interface UserServerDao extends BaseMapper { 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dao/UserTokenDao.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leeroy.forwordpanel.forwordpanel.model.UserToken; 5 | 6 | public interface UserTokenDao extends BaseMapper { 7 | 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/ClashNodeBase.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @program: forwordpanel 7 | * @description: clash node 8 | * @author: liruifeng 9 | * @created: 2020/09/30 14:01 10 | */ 11 | @Data 12 | public class ClashNodeBase { 13 | 14 | protected String name; 15 | protected String server; 16 | protected String port; 17 | protected String type; 18 | protected String password; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/ClashNodeTrojan.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @program: forwordpanel 7 | * @description: clash node 8 | * @author: liruifeng 9 | * @created: 2020/09/30 14:01 10 | */ 11 | @Data 12 | public class ClashNodeTrojan extends ClashNodeBase { 13 | 14 | private String sni; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/DataFlow.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class DataFlow { 7 | public DataFlow(String tcp, String udp) { 8 | this.tcp = tcp; 9 | this.udp = udp; 10 | } 11 | 12 | private String tcp; 13 | private String udp; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/ForwardFlowDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import lombok.Data; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.Id; 9 | import java.util.Date; 10 | 11 | /** 12 | * @program: forwordpanel 13 | * @description: 用户中转流量记录 14 | * @author: liruifeng 15 | * @created: 2020/09/17 14:06 16 | */ 17 | @Data 18 | public class ForwardFlowDTO { 19 | 20 | @Id 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Integer id; 23 | //中转记录id 24 | private Integer forwardId; 25 | // 中转用户 26 | private Integer userId; 27 | //端口id 28 | private Integer portId; 29 | // 服务id 30 | private Integer serverId; 31 | // 服务名称 32 | private String serverName; 33 | // 本地端口 34 | private Integer localPort; 35 | // 远程端口 36 | private Integer internetPort; 37 | // 目标ip 38 | private String remoteIp; 39 | // 目标主机地址 40 | private String remoteHost; 41 | // 流量使用量 42 | private Long dataUsage; 43 | // 目标主机端口 44 | private Integer remotePort; 45 | // 创建时间 46 | private Date createTime; 47 | //更新时间 48 | private Date updateTime; 49 | //删除表示 50 | private Boolean deleted; 51 | //禁用表示 52 | private Boolean disabled; 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/LoginDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | 4 | 5 | /** 6 | * @Title: UserDTO 7 | * @Description: 8 | * @author: youqing 9 | * @version: 1.0 10 | * @date: 2018/11/21 11:19 11 | */ 12 | public class LoginDTO { 13 | private String username; 14 | 15 | private String password; 16 | 17 | private String rememberMe; 18 | 19 | public String getUsername() { 20 | return username; 21 | } 22 | 23 | public void setUsername(String username) { 24 | this.username = username; 25 | } 26 | 27 | public String getPassword() { 28 | return password; 29 | } 30 | 31 | public void setPassword(String password) { 32 | this.password = password; 33 | } 34 | 35 | public String getRememberMe() { 36 | return rememberMe; 37 | } 38 | 39 | public void setRememberMe(String rememberMe) { 40 | this.rememberMe = rememberMe; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "LoginDTO{" + 46 | "username='" + username + '\'' + 47 | ", password='" + password + '\'' + 48 | ", rememberMe='" + rememberMe + '\'' + 49 | '}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/PageRequest.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | 4 | /** 5 | * @Title: 6 | * @Description: 7 | * @author: chris 8 | * @version: 9 | * @date: 2018/10/23 11:46 10 | */ 11 | public class PageRequest { 12 | /** 默认页面大小 */ 13 | public static Integer DEFAULT_PAGE_SIZE = 10; 14 | 15 | /** 默认页码 */ 16 | public static Integer DEFAULT_PAGE_NUM = 1; 17 | 18 | protected Integer pageNum; 19 | protected Integer pageSize; 20 | 21 | 22 | public Integer getPageNum() { 23 | return pageNum <= 0 ? DEFAULT_PAGE_NUM : pageNum; 24 | } 25 | 26 | public void setPageNum(int pageNum) { 27 | this.pageNum = pageNum; 28 | } 29 | 30 | public Integer getPageSize() { 31 | return pageSize <= 0 ? DEFAULT_PAGE_SIZE : pageSize; 32 | } 33 | 34 | public void setPageSize(int pageSize) { 35 | this.pageSize = pageSize; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/PortAddDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 端口信息 7 | */ 8 | @Data 9 | public class PortAddDTO { 10 | 11 | 12 | /** 13 | * 所属服务器 14 | */ 15 | private Integer serverId; 16 | 17 | /** 18 | * 本地端口 19 | */ 20 | private String localPort; 21 | 22 | /** 23 | * 外网端口 24 | */ 25 | private String internetPort; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/PortDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import lombok.Data; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * 端口信息 13 | */ 14 | @Data 15 | public class PortDTO { 16 | @TableId(value = "id", type = IdType.AUTO) 17 | private Integer id; 18 | 19 | /** 20 | * 所属服务器 21 | */ 22 | private Integer serverId; 23 | 24 | /** 25 | * 服务器名称 26 | */ 27 | private String serverName; 28 | 29 | /** 30 | * 服务起ip 31 | */ 32 | private String serverHost; 33 | 34 | /** 35 | * 本地端口 36 | */ 37 | private Integer localPort; 38 | 39 | /** 40 | * 外网端口 41 | */ 42 | private Integer internetPort; 43 | 44 | @DateTimeFormat(pattern = "yyyy-MM-dd") 45 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 46 | private Date createTime; 47 | 48 | @DateTimeFormat(pattern = "yyyy-MM-dd") 49 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 50 | private Date updateTime; 51 | 52 | private Boolean deleted; 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/PortPageRequest.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @program: forwordpanel 7 | * @description: 端口分页请求 8 | * @author: liruifeng 9 | * @created: 2020/10/14 12:51 10 | */ 11 | @Data 12 | public class PortPageRequest extends PageRequest{ 13 | 14 | private Integer serverId; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | 9 | import java.util.Date; 10 | 11 | 12 | @TableName("sys_user") 13 | @Data 14 | public class UserDTO { 15 | /** 16 | * ID 17 | */ 18 | @TableId(value = "id", type = IdType.AUTO) 19 | private Integer id; 20 | 21 | /** 22 | * 系统用户名称 23 | */ 24 | @TableField("username") 25 | private String username; 26 | 27 | /** 28 | * 系统用户密码 29 | */ 30 | private String password; 31 | 32 | /** 33 | * 手机号 34 | */ 35 | private String userPhone; 36 | 37 | /** 38 | * 手机号 39 | */ 40 | private String telegram; 41 | 42 | 43 | /** 44 | * 登记时间 45 | */ 46 | private Date regTime; 47 | 48 | /** 49 | * 状态(0:无效;1:有效) 50 | */ 51 | private Boolean disabled; 52 | 53 | /** 54 | * 删除表示 55 | */ 56 | private Boolean deleted; 57 | 58 | /** 59 | * 用户流量限制 60 | */ 61 | private Long dataLimit; 62 | 63 | /** 64 | * 流量使用量 65 | */ 66 | private Long dataUsage; 67 | 68 | /** 69 | * 用户类型 70 | */ 71 | private Integer userType; 72 | 73 | /** 74 | * 到期时间 75 | */ 76 | private Date expireTime; 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/UserPortDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import lombok.Data; 8 | import org.springframework.format.annotation.DateTimeFormat; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * 端口 14 | */ 15 | @Data 16 | public class UserPortDTO { 17 | @TableId(value = "id",type = IdType.AUTO) 18 | private Integer id; 19 | private Integer userId; 20 | private Integer portId; 21 | private Integer localPort; 22 | private Boolean disabled; 23 | private Boolean deleted; 24 | /** 25 | * 端口流量限制 26 | */ 27 | private Long dataLimit; 28 | /** 29 | * 到期时间 30 | */ 31 | @DateTimeFormat(pattern = "yyyy-MM-dd") 32 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 33 | private Date expireTime; 34 | 35 | @DateTimeFormat(pattern = "yyyy-MM-dd") 36 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 37 | private Date createTime; 38 | 39 | @DateTimeFormat(pattern = "yyyy-MM-dd") 40 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 41 | private Date updateTime; 42 | 43 | /** 44 | * 所属服务器 45 | */ 46 | private Integer serverId; 47 | 48 | /** 49 | * 服务器名称 50 | */ 51 | private String serverName; 52 | 53 | /** 54 | * 服务起ip 55 | */ 56 | private String serverHost; 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/UserPortForwardDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import lombok.Data; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * 端口转发实体 13 | */ 14 | @Data 15 | public class UserPortForwardDTO { 16 | 17 | @TableId(value = "id", type = IdType.AUTO) 18 | private Integer id; 19 | // 中转用户 20 | private Integer userId; 21 | //端口id 22 | private Integer portId; 23 | private Integer serverId; 24 | private Integer localPort; 25 | private String serverName; 26 | private String serverHost; 27 | private Integer internetPort; 28 | private String username; 29 | // 目标ip 30 | private String remoteIp; 31 | // 目标主机地址 32 | private String remoteHost; 33 | // 流量使用量 34 | private Long dataUsage; 35 | // 目标主机端口 36 | private Integer remotePort; 37 | // 创建时间 38 | @DateTimeFormat(pattern = "yyyy-MM-dd") 39 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 40 | private Date createTime; 41 | //更新时间 42 | @DateTimeFormat(pattern = "yyyy-MM-dd") 43 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 44 | private Date updateTime; 45 | //删除表示 46 | private Boolean deleted; 47 | //禁用表示 48 | private Boolean disabled; 49 | 50 | private Integer state; 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/UserPortForwardPageReq.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @program: forwordpanel 7 | * @description: 中转分页查询 8 | * @author: liruifeng 9 | * @created: 2020/10/13 09:24 10 | */ 11 | @Data 12 | public class UserPortForwardPageReq extends PageRequest { 13 | private Integer userId; 14 | private Integer serverId; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/UserPortPageRequest.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @program: forwordpanel 7 | * @description: 端口分页请求 8 | * @author: liruifeng 9 | * @created: 2020/10/14 12:51 10 | */ 11 | @Data 12 | public class UserPortPageRequest extends PageRequest{ 13 | 14 | private Integer userId; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/dto/UserSearchDTO.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.dto; 2 | 3 | 4 | import lombok.Data; 5 | 6 | /** 7 | * @Title: UserSearchDTO 8 | * @Description: 9 | * @author: youqing 10 | * @version: 1.0 11 | * @date: 2018/11/21 11:19 12 | */ 13 | @Data 14 | public class UserSearchDTO extends PageRequest { 15 | private String username; 16 | 17 | private String userPhone; 18 | 19 | private String telegram; 20 | 21 | private Boolean disabled; 22 | 23 | private Integer id; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/job/CheckDDNSJob.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.job; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.dao.PortDao; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 5 | import com.leeroy.forwordpanel.forwordpanel.model.UserPortForward; 6 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortForwardService; 7 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortService; 8 | import com.leeroy.forwordpanel.forwordpanel.service.UserService; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.annotation.Configurable; 12 | import org.springframework.scheduling.annotation.EnableScheduling; 13 | import org.springframework.scheduling.annotation.Scheduled; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * 检查用户转发ddns变化 20 | */ 21 | @Slf4j 22 | @EnableScheduling 23 | @Configurable 24 | @Component 25 | public class CheckDDNSJob { 26 | 27 | @Autowired 28 | private UserService userService; 29 | 30 | @Autowired 31 | private UserPortService userPortService; 32 | @Autowired 33 | private PortDao portDao; 34 | @Autowired 35 | private UserPortForwardService userPortForwardService; 36 | 37 | @Scheduled(cron = "0 0/3 * * * ?") 38 | public void execute() { 39 | List userPortForwards = userPortForwardService.getUsingForwards(); 40 | for(UserPortForward forward: userPortForwards){ 41 | log.info(">>>>开始执行DDNS检查任务"); 42 | String remoteIp = userPortForwardService.getRemoteIp(forward.getRemoteHost()); 43 | if (remoteIp == null) { 44 | userPortForwardService.stopForward(forward); 45 | } else if (!remoteIp.equals(forward.getRemoteIp())) { 46 | Port port = portDao.selectById(forward.getPortId()); 47 | forward.setRemoteIp(remoteIp); 48 | userPortForwardService.updateForward(forward, port); 49 | } 50 | log.info(">>>>结束执行DDNS检查任务"); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/job/CheckExpireJob.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.job; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.User; 4 | import com.leeroy.forwordpanel.forwordpanel.service.ForwardFlowService; 5 | import com.leeroy.forwordpanel.forwordpanel.service.ForwardService; 6 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortService; 7 | import com.leeroy.forwordpanel.forwordpanel.service.UserService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Configurable; 10 | import org.springframework.scheduling.annotation.EnableScheduling; 11 | import org.springframework.scheduling.annotation.Scheduled; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * 检查用户账户到期的任务 18 | */ 19 | @EnableScheduling 20 | @Configurable 21 | @Component 22 | public class CheckExpireJob { 23 | 24 | @Autowired 25 | private UserService userService; 26 | 27 | @Autowired 28 | private UserPortService userPortService; 29 | 30 | @Autowired 31 | private ForwardFlowService forwardFlowService; 32 | 33 | @Scheduled(cron = "0 0/1 * * * ?") 34 | public void execute() { 35 | List expireUserList = userService.findExpireUserList(); 36 | for (User user : expireUserList) { 37 | if (user.getExpireTime() == null||user.getUserType()<=0) { 38 | continue; 39 | } 40 | userPortService.disableUserPort(user.getId()); 41 | userService.disableUserById(user.getId()); 42 | } 43 | //流量用超的用户 44 | List enableUserList = userService.findEnableUserList(); 45 | for (User user : enableUserList) { 46 | if ( user.getDataLimit() == null || user.getDataLimit() <= 0L || user.getUserType()<=0) { 47 | continue; 48 | } 49 | Long total = forwardFlowService.getUserFlowTotal(user.getId()); 50 | if (total >= user.getDataLimit() * 1024 * 1024 * 1024) { 51 | userPortService.disableUserPort(user.getId()); 52 | userService.disableUserById(user.getId()); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/job/DataUsageJob.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.job; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.User; 4 | import com.leeroy.forwordpanel.forwordpanel.model.UserPortForward; 5 | import com.leeroy.forwordpanel.forwordpanel.service.ForwardFlowService; 6 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortForwardService; 7 | import com.leeroy.forwordpanel.forwordpanel.service.UserService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Configurable; 10 | import org.springframework.scheduling.annotation.EnableScheduling; 11 | import org.springframework.scheduling.annotation.Scheduled; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * 统计用户使用的流量 18 | */ 19 | @EnableScheduling 20 | @Configurable 21 | @Component 22 | public class DataUsageJob { 23 | 24 | @Autowired 25 | private UserService userService; 26 | 27 | @Autowired 28 | private UserPortForwardService userPortForwardService; 29 | 30 | @Autowired 31 | private ForwardFlowService forwardFlowService; 32 | 33 | 34 | @Scheduled(cron = "0 0/3 * * * ?") 35 | public void execute() { 36 | List expireUserList = userService.findEnableUserList(); 37 | expireUserList.forEach(user -> { 38 | List userForwardList = userPortForwardService.findUserForwardList(user.getId()); 39 | forwardFlowService.collectForwardFlow(userForwardList); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/job/KeepForwardJob.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.job; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.enums.ServerStatusEnum; 4 | import com.leeroy.forwordpanel.forwordpanel.model.Server; 5 | import com.leeroy.forwordpanel.forwordpanel.model.UserPortForward; 6 | import com.leeroy.forwordpanel.forwordpanel.service.RemoteForwardService; 7 | import com.leeroy.forwordpanel.forwordpanel.service.ServerService; 8 | import com.leeroy.forwordpanel.forwordpanel.service.UserPortForwardService; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.annotation.Configurable; 12 | import org.springframework.scheduling.annotation.EnableScheduling; 13 | import org.springframework.scheduling.annotation.Scheduled; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.util.StringUtils; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * @program: forwordpanel 21 | * @description: 保持中转任务 22 | * @author: liruifeng 23 | * @created: 2020/09/24 15:06 24 | */ 25 | @EnableScheduling 26 | @Configurable 27 | @Component 28 | @Slf4j 29 | public class KeepForwardJob { 30 | 31 | @Autowired 32 | ServerService serverService; 33 | 34 | @Autowired 35 | RemoteForwardService remoteForwardService; 36 | 37 | @Autowired 38 | UserPortForwardService userPortForwardService; 39 | 40 | @Scheduled(cron = "0 0/3 * * * ?") 41 | public void execute() { 42 | log.info(">>>>开始执行中转机重启检查任务"); 43 | List serviceList = serverService.findListWithoutLogin(); 44 | for (Server server : serviceList) { 45 | remoteForwardService.checkIPV4Forward(server); 46 | String lastRestart = remoteForwardService.getLastRestart(server); 47 | log.info(">>>>服务器:{} 当前重启时间:{}, 记录重启时间:{}", server.getServerName(), lastRestart, server.getLastRebootTime()); 48 | if(StringUtils.isEmpty(lastRestart)){ 49 | server.setState(ServerStatusEnum.CONNECT_FAIL.getCode()); 50 | serverService.save(server); 51 | continue; 52 | }else { 53 | server.setState(ServerStatusEnum.ONLINE.getCode()); 54 | } 55 | //重启过 56 | if(StringUtils.isEmpty(server.getLastRebootTime()) ||!server.getLastRebootTime().equals(lastRestart)){ 57 | log.info(">>>>服务器:{} 重启过, 开始启动中转", server.getServerName()); 58 | List userForwardList = userPortForwardService.findServerEnabledForwardList(server.getId()); 59 | for (UserPortForward userPortForward : userForwardList) { 60 | userPortForwardService.startForward(userPortForward, false); 61 | } 62 | server.setLastRebootTime(lastRestart); 63 | log.info(">>>>服务器:{} 重启过, 启动中转完成", server.getServerName()); 64 | } 65 | serverService.save(server); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/ForwardFlowRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.ForwardFlow; 4 | import com.leeroy.forwordpanel.forwordpanel.model.UserPort; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface ForwardFlowRepository extends JpaRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/PortRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface PortRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/ServerRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.Server; 4 | import com.leeroy.forwordpanel.forwordpanel.model.UserServer; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface ServerRepository extends JpaRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/SysConfigRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.SysConfig; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface SysConfigRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/UserPortForwardRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.UserPortForward; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface UserPortForwardRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/UserPortRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.Port; 4 | import com.leeroy.forwordpanel.forwordpanel.model.UserPort; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface UserPortRepository extends JpaRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface UserRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/jpa/UserServerRepository.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.jpa; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.model.User; 4 | import com.leeroy.forwordpanel.forwordpanel.model.UserServer; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface UserServerRepository extends JpaRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/Clash.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import lombok.Data; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * clash配置 13 | */ 14 | @Data 15 | public class Clash { 16 | 17 | 18 | @TableId(value = "id", type = IdType.ASSIGN_UUID) 19 | private String id; 20 | 21 | private String configName; 22 | private Integer userId; 23 | private Boolean disabled; 24 | private Boolean deleted; 25 | /** 26 | * 到期时间 27 | */ 28 | @DateTimeFormat(pattern = "yyyy-MM-dd") 29 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 30 | private Date expireTime; 31 | 32 | private Date createTime; 33 | 34 | private Date updateTime; 35 | /** 36 | * 配置字符串 37 | */ 38 | private String text; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/ForwardFlow.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import lombok.Data; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.Id; 9 | import java.util.Date; 10 | 11 | /** 12 | * @program: forwordpanel 13 | * @description: 用户中转流量记录 14 | * @author: liruifeng 15 | * @created: 2020/09/17 14:06 16 | */ 17 | @Entity 18 | @Data 19 | public class ForwardFlow { 20 | 21 | @Id 22 | @TableId(value = "id", type = IdType.AUTO) 23 | private Integer id; 24 | //中转记录id 25 | private Integer forwardId; 26 | // 中转用户 27 | private Integer userId; 28 | //端口id 29 | private Integer portId; 30 | // 服务id 31 | private Integer serverId; 32 | // 目标ip 33 | private String remoteIp; 34 | // 目标主机地址 35 | private String remoteHost; 36 | // 流量使用量 37 | private Long dataUsage; 38 | // 目标主机端口 39 | private Integer remotePort; 40 | // 创建时间 41 | private Date createTime; 42 | //更新时间 43 | private Date updateTime; 44 | //删除表示 45 | private Boolean deleted; 46 | //禁用表示 47 | private Boolean disabled; 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/Port.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import lombok.Data; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import javax.persistence.Entity; 10 | import javax.persistence.Id; 11 | import java.util.Date; 12 | 13 | /** 14 | * 端口信息 15 | */ 16 | @Entity 17 | @Data 18 | public class Port { 19 | 20 | @Id 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Integer id; 23 | 24 | /** 25 | * 所属服务器 26 | */ 27 | private Integer serverId; 28 | 29 | /** 30 | * 本地端口 31 | */ 32 | private Integer localPort; 33 | 34 | /** 35 | * 外网端口 36 | */ 37 | private Integer internetPort; 38 | 39 | private Date createTime; 40 | 41 | private Date updateTime; 42 | 43 | private Boolean deleted; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/Resource.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 资源 9 | */ 10 | @Data 11 | public class Resource { 12 | 13 | private Integer id; 14 | 15 | private String title; 16 | 17 | private String path; 18 | 19 | private boolean meta; 20 | 21 | private boolean hidden; 22 | 23 | private String icon; 24 | 25 | private List children; 26 | 27 | public Resource(Integer id, String title, String path, boolean meta, boolean hidden) { 28 | this.id = id; 29 | this.title = title; 30 | this.path = path; 31 | this.meta = meta; 32 | this.hidden = hidden; 33 | } 34 | 35 | public Resource(Integer id, String title, String path, String icon) { 36 | this.id = id; 37 | this.title = title; 38 | this.path = path; 39 | this.meta = true; 40 | this.hidden = false; 41 | this.icon = icon; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/Server.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import lombok.Data; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import javax.persistence.Entity; 10 | import javax.persistence.Id; 11 | import java.util.Date; 12 | 13 | /** 14 | * 端口信息 15 | */ 16 | @Entity 17 | @Data 18 | public class Server { 19 | 20 | @Id 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Integer id; 23 | 24 | 25 | /** 26 | * 服务器所有者 27 | */ 28 | private Integer ownerId; 29 | 30 | /** 31 | * 服务器名称 32 | */ 33 | private String serverName; 34 | 35 | /** 36 | * 服务器host 37 | */ 38 | private String host; 39 | 40 | /** 41 | * ssh端口 42 | */ 43 | private Integer port; 44 | 45 | /** 46 | * 服务器 key 47 | */ 48 | private String key; 49 | 50 | /** 51 | * 密码 52 | */ 53 | private String username; 54 | 55 | /** 56 | * 用户名 57 | */ 58 | private String password; 59 | 60 | /** 61 | * 服务器状态 62 | */ 63 | private Integer state; 64 | 65 | private Date createTime; 66 | 67 | private Date updateTime; 68 | 69 | private Boolean deleted; 70 | /** 71 | * 最后一次重启时间 72 | */ 73 | private String lastRebootTime; 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/SysConfig.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import lombok.Data; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.Id; 9 | import java.util.Date; 10 | 11 | /** 12 | * 系统配置 13 | */ 14 | @Entity 15 | @Data 16 | public class SysConfig { 17 | 18 | @Id 19 | @TableId(value = "id", type = IdType.INPUT) 20 | private Integer id; 21 | 22 | 23 | /** 24 | * tg 25 | */ 26 | private String telegram; 27 | 28 | /** 29 | * 邮箱 30 | */ 31 | private String email; 32 | 33 | /** 34 | * 邮箱 35 | */ 36 | private String qq; 37 | 38 | /** 39 | * 当前版本 40 | */ 41 | private String version; 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/User.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import com.fasterxml.jackson.annotation.JsonFormat; 8 | import lombok.Data; 9 | import org.springframework.format.annotation.DateTimeFormat; 10 | 11 | import javax.persistence.Entity; 12 | import javax.persistence.Id; 13 | import javax.persistence.Table; 14 | import java.util.Date; 15 | 16 | 17 | @Table(name = "sys_user") 18 | @Entity 19 | @TableName("sys_user") 20 | @Data 21 | public class User { 22 | /** 23 | * ID 24 | */ 25 | @Id 26 | @TableId(value = "id", type = IdType.AUTO) 27 | private Integer id; 28 | 29 | /** 30 | * 系统用户名称 31 | */ 32 | @TableField("username") 33 | private String username; 34 | 35 | /** 36 | * 系统用户密码 37 | */ 38 | private String password; 39 | 40 | /** 41 | * 手机号 42 | */ 43 | private String userPhone; 44 | 45 | /** 46 | * 手机号 47 | */ 48 | private String telegram; 49 | 50 | 51 | /** 52 | * 登记时间 53 | */ 54 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 55 | private Date regTime; 56 | 57 | /** 58 | * 状态(0:无效;1:有效) 59 | */ 60 | private Boolean disabled; 61 | 62 | /** 63 | * 删除表示 64 | */ 65 | private Boolean deleted; 66 | 67 | /** 68 | * 用户流量限制 69 | */ 70 | private Long dataLimit; 71 | 72 | /** 73 | * 流量使用量 74 | */ 75 | private Long dataUsage; 76 | 77 | /** 78 | * 用户类型 79 | */ 80 | private Integer userType; 81 | 82 | /** 83 | * 到期时间 84 | */ 85 | private Date expireTime; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/UserPort.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import lombok.Data; 8 | import org.springframework.format.annotation.DateTimeFormat; 9 | 10 | import javax.persistence.Entity; 11 | import javax.persistence.Id; 12 | import java.util.Date; 13 | 14 | /** 15 | * 端口 16 | */ 17 | @Entity 18 | @Data 19 | public class UserPort { 20 | 21 | @Id 22 | @TableId(value = "id",type = IdType.AUTO) 23 | private Integer id; 24 | private Integer userId; 25 | private Integer portId; 26 | private Integer serverId; 27 | private Boolean disabled; 28 | private Boolean deleted; 29 | /** 30 | * 端口流量限制 31 | */ 32 | private Long dataLimit; 33 | /** 34 | * 到期时间 35 | */ 36 | @DateTimeFormat(pattern = "yyyy-MM-dd") 37 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 38 | private Date expireTime; 39 | 40 | private Date createTime; 41 | 42 | private Date updateTime; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/UserPortForward.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import lombok.Data; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import javax.persistence.Entity; 10 | import javax.persistence.Id; 11 | import java.util.Date; 12 | 13 | /** 14 | * 端口转发实体 15 | */ 16 | @Entity 17 | @Data 18 | public class UserPortForward { 19 | 20 | @Id 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Integer id; 23 | // 中转用户 24 | private Integer userId; 25 | //端口id 26 | private Integer portId; 27 | // 服务id 28 | private Integer serverId; 29 | // 目标ip 30 | private String remoteIp; 31 | // 目标主机地址 32 | private String remoteHost; 33 | // 流量使用量 34 | private Long dataUsage; 35 | // 目标主机端口 36 | private Integer remotePort; 37 | // 创建时间 38 | private Date createTime; 39 | //更新时间 40 | private Date updateTime; 41 | //删除表示 42 | private Boolean deleted; 43 | //禁用表示 44 | private Boolean disabled; 45 | 46 | private Integer state; 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/UserServer.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import lombok.Data; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.Id; 9 | 10 | @Entity 11 | @Data 12 | public class UserServer { 13 | 14 | @Id 15 | @TableId(value = "id",type = IdType.AUTO) 16 | private Integer id; 17 | /** 18 | * 用户id 19 | */ 20 | private Integer userId; 21 | /** 22 | * 服务器id 23 | */ 24 | private Integer serverId; 25 | 26 | private Boolean deleted; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/model/UserToken.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.model; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 用户token 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | public class UserToken implements Serializable { 14 | 15 | 16 | /** 17 | * 用户token 18 | */ 19 | private String token; 20 | 21 | /** 22 | * 用户名 23 | */ 24 | private String username; 25 | 26 | /** 27 | * 用户id 28 | */ 29 | private Integer userId; 30 | 31 | /** 32 | * 超时时间 33 | */ 34 | private Long expireTime; 35 | 36 | /** 37 | * 创建时间 38 | */ 39 | private Long createTime; 40 | 41 | /** 42 | * 更新时间 43 | */ 44 | private Long updateTime; 45 | 46 | public static UserToken getInstance(String token, String username, Integer userId) { 47 | UserToken userToken = new UserToken(); 48 | userToken.setUsername(username); 49 | userToken.setUserId(userId); 50 | userToken.setToken(token); 51 | userToken.setExpireTime(System.currentTimeMillis()+1000*60*30); 52 | return userToken; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/service/ConfigService.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.service; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 6 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 7 | import com.github.pagehelper.Page; 8 | import com.github.pagehelper.PageHelper; 9 | import com.github.pagehelper.PageInfo; 10 | import com.leeroy.forwordpanel.forwordpanel.common.WebCurrentData; 11 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 12 | import com.leeroy.forwordpanel.forwordpanel.dao.ClashDao; 13 | import com.leeroy.forwordpanel.forwordpanel.dto.PageRequest; 14 | import com.leeroy.forwordpanel.forwordpanel.model.Clash; 15 | import org.springframework.beans.BeanUtils; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Service; 18 | import org.springframework.util.StringUtils; 19 | import org.yaml.snakeyaml.DumperOptions; 20 | import org.yaml.snakeyaml.Yaml; 21 | 22 | import java.io.StringWriter; 23 | import java.util.ArrayList; 24 | import java.util.Date; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | 28 | @Service 29 | public class ConfigService { 30 | 31 | @Autowired 32 | private ClashDao clashDao; 33 | 34 | 35 | /** 36 | * 保存clash 37 | */ 38 | public ApiResponse save(Clash clash) { 39 | clash.setUserId(WebCurrentData.getUserId()); 40 | if (StringUtils.isEmpty(clash.getId())) { 41 | clash.setDeleted(false); 42 | clash.setDisabled(false); 43 | clash.setCreateTime(new Date()); 44 | clashDao.insert(clash); 45 | } else { 46 | Clash existClash = clashDao.selectById(clash.getId()); 47 | BeanUtils.copyProperties(clash, existClash); 48 | existClash.setUpdateTime(new Date()); 49 | clashDao.updateById(existClash); 50 | } 51 | return ApiResponse.ok(); 52 | } 53 | 54 | public ApiResponse getNodeList(String id){ 55 | Clash clash = clashDao.selectById(id); 56 | Yaml yaml = new Yaml(); 57 | JSONObject jsonObject = yaml.loadAs(clash.getText(), JSONObject.class); 58 | JSONArray proxies = jsonObject.getJSONArray("proxies"); 59 | return ApiResponse.ok(proxies); 60 | } 61 | 62 | /** 63 | * 保存节点列表 64 | * @param nodeList 65 | * @param id 66 | * @return 67 | */ 68 | public ApiResponse saveNodeList(JSONArray nodeList, String id){ 69 | Clash clash = clashDao.selectById(id); 70 | DumperOptions dumperOptions = new DumperOptions(); 71 | 72 | dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); 73 | dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN); 74 | Yaml yaml = new Yaml(dumperOptions); 75 | JSONObject jsonObject = yaml.loadAs(clash.getText(), JSONObject.class); 76 | parseProxies(nodeList); 77 | jsonObject.put("proxies", nodeList); 78 | List groupProxies = new ArrayList<>(); 79 | for (int i = 0; i < nodeList.size(); i++) { 80 | JSONObject item = nodeList.getJSONObject(i); 81 | groupProxies.add(item.getString("name")); 82 | } 83 | JSONArray proxyGroups = jsonObject.getJSONArray("proxy-groups"); 84 | for (int i = 0; i < proxyGroups.size(); i++) { 85 | JSONObject item = proxyGroups.getJSONObject(i); 86 | JSONArray proxies = item.getJSONArray("proxies"); 87 | Iterator iterator = proxies.iterator(); 88 | while (iterator.hasNext()) { 89 | String name = (String) iterator.next(); 90 | if(name.indexOf("DIRECT")>=0){ 91 | continue; 92 | } 93 | //排除分组j 94 | if (isGroup(proxyGroups, name)){ 95 | continue; 96 | } 97 | 98 | iterator.remove(); 99 | } 100 | proxies.addAll(groupProxies); 101 | item.put("proxies", proxies); 102 | } 103 | StringWriter writer = new StringWriter(); 104 | yaml.dump(jsonObject, writer); 105 | clash.setText(writer.toString()); 106 | clashDao.updateById(clash); 107 | return ApiResponse.ok(); 108 | } 109 | 110 | private void parseProxies(JSONArray nodeList){ 111 | for (int i = 0; i < nodeList.size(); i++) { 112 | JSONObject item = nodeList.getJSONObject(i); 113 | item.put("name", item.getString("name")); 114 | item.put("server", item.getString("server")); 115 | item.put("port", item.getInteger("port")); 116 | item.put("password", item.getString("password")); 117 | item.put("sni", item.getString("sni")); 118 | item.put("type", item.getString("type")); 119 | } 120 | } 121 | 122 | private boolean isGroup(JSONArray proxyGroups, String name){ 123 | //排除分组j 124 | for (int j = 0; j < proxyGroups.size(); j++) { 125 | JSONObject proxy = proxyGroups.getJSONObject(j); 126 | if( proxy.getString("name").equals(name)){ 127 | return true; 128 | } 129 | } 130 | return false; 131 | } 132 | 133 | /** 134 | * 查询clash列表 135 | * 136 | * @return 137 | */ 138 | public PageInfo findClashList(PageRequest pageRequest) { 139 | Integer userId = WebCurrentData.getUserId(); 140 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery().eq(Clash::getUserId, userId).eq(Clash::getDeleted, false); 141 | Page page = PageHelper.startPage(pageRequest.getPageNum(), pageRequest.getPageSize()); 142 | List clashList = clashDao.selectList(queryWrapper); 143 | PageInfo pageInfo = page.toPageInfo(); 144 | pageInfo.setList(clashList); 145 | return pageInfo; 146 | } 147 | 148 | /** 149 | * 删除clash 150 | */ 151 | public void delClash(String id) { 152 | Clash userPort = new Clash(); 153 | userPort.setId(id); 154 | userPort.setDeleted(true); 155 | clashDao.updateById(userPort); 156 | } 157 | 158 | /** 159 | * 查询clash 160 | * @param id 161 | * @return 162 | */ 163 | public Clash findClashById(String id){ 164 | return clashDao.selectById(id); 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/service/ForwardService.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.service; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.leeroy.forwordpanel.forwordpanel.common.util.IpUtil; 5 | import com.leeroy.forwordpanel.forwordpanel.common.util.ShellUtil; 6 | import com.leeroy.forwordpanel.forwordpanel.dto.DataFlow; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.util.StringUtils; 10 | 11 | import java.math.BigDecimal; 12 | 13 | @Slf4j 14 | @Service 15 | public class ForwardService { 16 | 17 | /** 18 | * 添加转发 19 | * 20 | * @param remoteHost 21 | * @param remotePort 22 | * @param localPort 23 | */ 24 | public void addForward(String remoteHost, Integer remotePort, Integer localPort) { 25 | stopForward(remoteHost, remotePort, localPort); 26 | String localIp = IpUtil.getLocalIpByNetcard(); 27 | ShellUtil.execShell(String.format("iptables -t nat -A PREROUTING -p tcp --dport %d -j DNAT --to-destination %s:%d", localPort, remoteHost, remotePort)); 28 | ShellUtil.execShell(String.format("iptables -t nat -A PREROUTING -p udp --dport %d -j DNAT --to-destination %s:%d", localPort, remoteHost, remotePort)); 29 | ShellUtil.execShell(String.format("iptables -t nat -A POSTROUTING -p tcp -d %s --dport %d -j SNAT --to-source %s", remoteHost, remotePort, localIp)); 30 | ShellUtil.execShell(String.format("iptables -t nat -A POSTROUTING -p udp -d %s --dport %d -j SNAT --to-source %s", remoteHost, remotePort, localIp)); 31 | String flow = ShellUtil.execShell(String.format("iptables -n -v -L -t filter -x | grep %s | awk '{print $2}'", remoteHost)); 32 | log.info("fow: {}", flow); 33 | if (StringUtils.isEmpty(flow)) { 34 | ShellUtil.execShell(String.format("iptables -I FORWARD -s %s", remoteHost)); 35 | } 36 | } 37 | 38 | 39 | /** 40 | * 获取转发流量 41 | * @param remoteHost 42 | * @param remotePort 43 | * @return 44 | */ 45 | public String getPortFlow(String remoteHost, Integer remotePort) { 46 | String result = ShellUtil.execShell(String.format("iptables -n -v -L -t filter -x | grep %s | awk '{print $2}'", remoteHost)); 47 | result = result.replaceAll("\n", ""); 48 | log.info("flow:{}", result); 49 | return StringUtils.isEmpty(result)?"0":result; 50 | } 51 | 52 | 53 | /** 54 | * 重置流量 55 | * @param remoteHost 56 | * @param remotePort 57 | */ 58 | public void resetFlowCount(String remoteHost, Integer remotePort){ 59 | ShellUtil.execShell(String.format("iptables -D FORWARD -s %s", remoteHost)); 60 | ShellUtil.execShell(String.format("iptables -I FORWARD -s %s", remoteHost)); 61 | } 62 | 63 | /** 64 | * 重置流量 65 | * @param remoteHost 66 | * @param remotePort 67 | */ 68 | public void deleteFlowCount(String remoteHost, Integer remotePort){ 69 | ShellUtil.execShell(String.format("iptables -D FORWARD -p tcp --dport %s -d %s", remotePort, remoteHost)); 70 | ShellUtil.execShell(String.format("iptables -D FORWARD -p udp --dport %s -d %s", remotePort, remoteHost)); 71 | } 72 | 73 | 74 | /** 75 | * 停止转发 76 | * 77 | * @param remoteHost 78 | * @param remotePort 79 | * @param localPort 80 | */ 81 | public void stopForward(String remoteHost, Integer remotePort, Integer localPort) { 82 | String dnatStr = ShellUtil.execShell(String.format("iptables -L PREROUTING -n -t nat --line-number |grep DNAT|grep \"dpt:%d \"|sort -r|awk '{print $1,$3,$9}'|tr \" \" \":\"|tr \"\\n\" \" \"", localPort)); 83 | log.info(dnatStr); 84 | dnatStr.trim(); 85 | if(StringUtils.isEmpty(dnatStr)){ 86 | return; 87 | } 88 | String[] dnatList = dnatStr.split(" "); 89 | log.info(JSON.toJSONString(dnatList)); 90 | for (String item : dnatList) { 91 | item = item.trim(); 92 | if(StringUtils.isEmpty(item)){ 93 | continue; 94 | } 95 | String[] itemList = item.split(":"); 96 | log.info(JSON.toJSONString(itemList)); 97 | ShellUtil.execShell(String.format("iptables -t nat -D PREROUTING %s", itemList[0])); 98 | String snatStr = ShellUtil.execShell(String.format("iptables -L POSTROUTING -n -t nat --line-number|grep SNAT|grep %s|grep dpt:%s|grep %s|awk '{print $1}'|sort -r|tr \"\\n\" \" \"", itemList[3], itemList[4], itemList[1])); 99 | if(StringUtils.isEmpty(snatStr)){ 100 | continue; 101 | } 102 | snatStr = snatStr.trim(); 103 | if(StringUtils.isEmpty(snatStr)){ 104 | continue; 105 | } 106 | String[] snatList = snatStr.split(" "); 107 | log.info(JSON.toJSONString(snatList)); 108 | for (String s : snatList) { 109 | if(StringUtils.isEmpty(s)){ 110 | continue; 111 | } 112 | ShellUtil.execShell("iptables -t nat -D POSTROUTING "+ s); 113 | } 114 | } 115 | 116 | } 117 | 118 | private String getForwardKey(String remoteHost, Integer remotePort, Integer localPort) { 119 | return remoteHost + remotePort + localPort; 120 | } 121 | 122 | 123 | public static void main(String[] args) { 124 | String a = " "; 125 | System.out.println(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/service/LoginService.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.service; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 4 | import com.leeroy.forwordpanel.forwordpanel.common.util.DigestUtils; 5 | import com.leeroy.forwordpanel.forwordpanel.common.util.TokenUtil; 6 | import com.leeroy.forwordpanel.forwordpanel.dto.LoginDTO; 7 | import com.leeroy.forwordpanel.forwordpanel.model.User; 8 | import com.leeroy.forwordpanel.forwordpanel.model.UserToken; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.util.StringUtils; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | 15 | @Service 16 | public class LoginService { 17 | 18 | @Autowired 19 | private UserService userService; 20 | 21 | @Autowired 22 | private UserTokenService userTokenService; 23 | 24 | /** 25 | * 登录 26 | * 27 | * @param loginDTO 28 | * @return 29 | */ 30 | public ApiResponse login(LoginDTO loginDTO) { 31 | if (StringUtils.isEmpty(loginDTO.getUsername()) || StringUtils.isEmpty(loginDTO.getPassword())) { 32 | return ApiResponse.error("400", "用户名密码不能为空"); 33 | } 34 | User baseAdminUser = userService.findByUserName(loginDTO.getUsername()); 35 | if (baseAdminUser == null) { 36 | return ApiResponse.error("403", "用户名密码错误"); 37 | } 38 | if (!DigestUtils.Md5(loginDTO.getUsername(), loginDTO.getPassword()).equals(baseAdminUser.getPassword())) { 39 | return ApiResponse.error("403", "用户名密码错误"); 40 | } 41 | String token = TokenUtil.makeToken(baseAdminUser.getUsername(), baseAdminUser.getPassword()); 42 | UserToken userToken = UserToken.getInstance(token, baseAdminUser.getUsername(), baseAdminUser.getId()); 43 | userTokenService.saveUserToken(userToken); 44 | return ApiResponse.ok(userToken); 45 | } 46 | 47 | public ApiResponse authToken(String token){ 48 | return ApiResponse.ok(userTokenService.getUserByToken(token)); 49 | } 50 | 51 | /** 52 | * 登出 53 | * 54 | * @return 55 | */ 56 | public void logout(HttpServletRequest request) { 57 | String token = TokenUtil.getToken(request); 58 | userTokenService.delToken(token); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/service/SysConfigService.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.service; 2 | 3 | import com.leeroy.forwordpanel.forwordpanel.common.response.ApiResponse; 4 | import com.leeroy.forwordpanel.forwordpanel.dao.SysConfigDao; 5 | import com.leeroy.forwordpanel.forwordpanel.model.SysConfig; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * @Title: 系统配置 13 | * @Description: 14 | * @author: leeroy 15 | * @version: 1.0 16 | * @date: 2020/11/21 11:04 17 | */ 18 | @Slf4j 19 | @Service 20 | public class SysConfigService { 21 | 22 | @Autowired 23 | SysConfigDao sysConfigDao; 24 | 25 | @Value("${panel.version:test}") 26 | private String version; 27 | 28 | /** 29 | * 保存clash 30 | */ 31 | public ApiResponse save(SysConfig config) { 32 | if (config.getId() != null) { 33 | sysConfigDao.updateById(config); 34 | } else { 35 | config.setId(1); 36 | sysConfigDao.insert(config); 37 | } 38 | return ApiResponse.ok(); 39 | } 40 | 41 | /** 42 | * 获取配置 43 | * 44 | * @return 45 | */ 46 | public ApiResponse getConfig() { 47 | SysConfig sysConfig = sysConfigDao.selectById(1); 48 | 49 | if (sysConfig == null) { 50 | sysConfig = new SysConfig(); 51 | sysConfig.setVersion(version); 52 | sysConfig.setId(1); 53 | sysConfig.setTelegram(""); 54 | sysConfigDao.insert(sysConfig); 55 | } else { 56 | sysConfig.setVersion(version); 57 | } 58 | return ApiResponse.ok(sysConfig); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/leeroy/forwordpanel/forwordpanel/service/UserTokenService.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel.service; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 5 | import com.leeroy.forwordpanel.forwordpanel.dao.UserDao; 6 | import com.leeroy.forwordpanel.forwordpanel.dao.UserTokenDao; 7 | import com.leeroy.forwordpanel.forwordpanel.model.User; 8 | import com.leeroy.forwordpanel.forwordpanel.model.UserToken; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | /** 13 | * 用户token 14 | */ 15 | @Service 16 | public class UserTokenService { 17 | 18 | @Autowired 19 | private UserTokenDao userTokenDao; 20 | 21 | @Autowired 22 | private UserDao userDao; 23 | 24 | /** 25 | * 保存用户token 26 | * @param userToken 27 | */ 28 | public void saveUserToken(UserToken userToken){ 29 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery().eq(UserToken::getToken, userToken) 30 | .gt(UserToken::getExpireTime, System.currentTimeMillis()); 31 | UserToken existToken = userTokenDao.selectOne(queryWrapper); 32 | if(existToken!=null){ 33 | existToken.setUpdateTime(System.currentTimeMillis()); 34 | existToken.setExpireTime(existToken.getExpireTime()+30*60*1000); 35 | userTokenDao.updateById(userToken); 36 | }else { 37 | userToken.setCreateTime(System.currentTimeMillis()); 38 | userTokenDao.insert(userToken); 39 | } 40 | } 41 | 42 | public void updateUserToken(UserToken userToken){ 43 | userTokenDao.update(userToken, Wrappers.lambdaQuery().eq(UserToken::getUserId, userToken.getUserId()).eq(UserToken::getToken, userToken.getToken())); 44 | } 45 | 46 | 47 | /** 48 | * 获取用户token 49 | * @param token 50 | * @return 51 | */ 52 | public UserToken getUserToken(String token){ 53 | if(token==null){ 54 | return null; 55 | } 56 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery().eq(UserToken::getToken, token) 57 | .gt(UserToken::getExpireTime, System.currentTimeMillis()); 58 | return userTokenDao.selectOne(queryWrapper); 59 | } 60 | 61 | /** 62 | * 获取用户token 63 | * @param token 64 | * @return 65 | */ 66 | public User getUserByToken(String token){ 67 | if(token==null){ 68 | return null; 69 | } 70 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery().eq(UserToken::getToken, token) 71 | .gt(UserToken::getExpireTime, System.currentTimeMillis()); 72 | UserToken userToken = userTokenDao.selectOne(queryWrapper); 73 | if(userToken==null){ 74 | return null; 75 | } 76 | return userDao.selectById(userToken.getUserId()); 77 | } 78 | 79 | /** 80 | * 删除token 81 | */ 82 | public void delToken(String token){ 83 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery().eq(UserToken::getToken, token); 84 | userTokenDao.delete(queryWrapper); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #���þ�̬��Դ·��������Զ��ŷָ� 2 | server.port=8080 3 | spring.resources.static-locations= classpath:static/,file:static/ 4 | spring.datasource.url=jdbc:h2:./forward_db;DB_CLOSE_ON_EXIT=FALSE 5 | spring.datasource.driverClassName=org.h2.Driver 6 | spring.datasource.username=sa 7 | spring.datasource.password=*yBHJHud$f45fgf 8 | # H2 9 | spring.h2.console.enabled=true 10 | spring.h2.console.path=/h2 11 | spring.h2.console.settings.web-allow-others=true 12 | spring.jpa.generate-ddl=false 13 | spring.jpa.show-sql=true 14 | spring.jpa.hibernate.ddl-auto=update 15 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 16 | panel.version=test 17 | spring.servlet.multipart.max-file-size=1024MB 18 | spring.servlet.multipart.max-request-size=1024MB 19 | 20 | #mybatis-plus配置控制台打印完整带参数SQL语句 21 | #mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 22 | #mybatis-plus.configuration.map-underscore-to-camel-case=true 23 | panel.default-password=XDZZ@123 24 | ssh.privateKeyPath=D:\\id_rsa_old 25 | -------------------------------------------------------------------------------- /src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE sys_user ( 2 | id int NOT NULL AUTO_INCREMENT , 3 | username varchar(50) NOT NULL, 4 | password varchar(250) DEFAULT NULL , 5 | user_phone varchar(11) , 6 | telegram varchar(11) , 7 | reg_time DATE , 8 | data_limit BIGINT, 9 | expire_time DATE, 10 | data_usage BIGINT, 11 | user_type INTEGER NOT NULL DEFAULT 1, 12 | disabled BOOLEAN , 13 | deleted BOOLEAN 14 | ); 15 | 16 | INSERT INTO sys_user VALUES (1, 'admin', '028974a1894e75000bf41636adc8ed81', '11111111111','leeroy','2020-01-03', 107374182400,null,null, 0, false, false); 17 | 18 | CREATE TABLE user_port ( 19 | id int NOT NULL AUTO_INCREMENT , 20 | local_port int NOT NULL, 21 | user_id int NOT NULL , 22 | port_id int NOT NULL , 23 | data_limit BIGINT, 24 | data_usage BIGINT, 25 | disabled BOOLEAN , 26 | deleted BOOLEAN , 27 | expire_time DATE, 28 | create_time TIMESTAMP , 29 | update_time TIMESTAMP 30 | ); 31 | 32 | 33 | CREATE TABLE user_token ( 34 | id int NOT NULL AUTO_INCREMENT , 35 | token varchar(250) NOT NULL, 36 | username varchar(50) NOT NULL, 37 | user_id INTEGER , 38 | create_time BIGINT , 39 | update_time BIGINT, 40 | expire_time BIGINT 41 | ); 42 | 43 | CREATE TABLE user_port_forward ( 44 | id int NOT NULL AUTO_INCREMENT , 45 | user_id int NOT NULL , 46 | local_port int NOT NULL, 47 | remote_ip varchar(64), 48 | remote_host varchar(64), 49 | remote_port int, 50 | data_limit BIGINT, 51 | data_usage BIGINT, 52 | disabled BOOLEAN , 53 | deleted BOOLEAN , 54 | create_time TIMESTAMP , 55 | update_time TIMESTAMP 56 | ); 57 | 58 | CREATE TABLE clash ( 59 | id varchar(50) NOT NULL, 60 | config_name varchar(50) NOT NULL, 61 | text CLOB NOT NULL, 62 | user_id int NOT NULL , 63 | disabled BOOLEAN , 64 | deleted BOOLEAN , 65 | expire_time DATE, 66 | create_time TIMESTAMP , 67 | update_time TIMESTAMP 68 | ); 69 | 70 | CREATE TABLE port ( 71 | id int NOT NULL AUTO_INCREMENT, 72 | local_port int NOT NULL , 73 | internet_port int NOT NULL , 74 | deleted BOOLEAN, 75 | create_time TIMESTAMP, 76 | update_time TIMESTAMP 77 | ); 78 | 79 | CREATE TABLE server( 80 | id int NOT NULL AUTO_INCREMENT, 81 | server_name varchar(128) NOT NULL , 82 | host varchar(256) NOT NULL , 83 | port int NOT NULL , 84 | state int NOT NULL DEFAULT 1 , 85 | deleted BOOLEAN, 86 | create_time TIMESTAMP, 87 | update_time TIMESTAMP 88 | ); 89 | 90 | 91 | alter table server add column key varchar(256) NOT NULL ; 92 | 93 | 94 | -- 07-22 local_port不再存储 95 | alter table user_port drop local_port; 96 | alter table user_port drop port_id; 97 | alter table user_port add local_port int default NULL; 98 | alter table user_port add port_id int default NULL; 99 | 100 | alter table user_port_forward drop local_port; 101 | alter table user_port_forward add local_port int default NULL; 102 | alter table user_port_forward add port_id int NOT NULL; 103 | 104 | -- 07-28 增加user_server 105 | CREATE TABLE user_server( 106 | id int NOT NULL AUTO_INCREMENT, 107 | user_id int NOT NULL , 108 | server_id int NOT NULL DEFAULT 1 , 109 | deleted BOOLEAN, 110 | create_time TIMESTAMP, 111 | update_time TIMESTAMP 112 | ); 113 | alter table server add column owner_id int; 114 | alter table port add column server_id int; 115 | alter table user_port add server_id int default NULL; 116 | alter table user_port_forward add server_id int default NULL; 117 | 118 | alter table server add column username varchar(128); 119 | alter table server add column password varchar(128); 120 | 121 | CREATE TABLE forward_flow ( 122 | id int NOT NULL AUTO_INCREMENT , 123 | forward_id int not NULL, 124 | user_id int NOT NULL , 125 | local_port int default NULL, 126 | remote_ip varchar(64), 127 | remote_host varchar(64), 128 | port_id int NOT NULL, 129 | remote_port int, 130 | data_limit BIGINT, 131 | data_usage BIGINT, 132 | disabled BOOLEAN , 133 | deleted BOOLEAN , 134 | server_id int default NULL, 135 | create_time TIMESTAMP , 136 | update_time TIMESTAMP 137 | ); 138 | -------------------------------------------------------------------------------- /src/main/resources/scripts/turnOnNat.sh: -------------------------------------------------------------------------------- 1 | sed -n '/^net.ipv4.ip_forward=1/'p /etc/sysctl.conf | grep -q "net.ipv4.ip_forward=1";if [ $? -ne 0 ]; then echo -e "net.ipv4.ip_forward=1" >> /etc/sysctl.conf && sysctl -p;fi 2 | arr1=(`iptables -L FORWARD -n --line-number |grep "REJECT"|grep "0.0.0.0/0"|sort -r|awk '{print $1,$2,$5}'|tr " " ":"|tr "\n" " "`);for cell in ${arr1[@]};do arr2=(`echo $cell|tr ":" " "`);index=${arr2[0]};echo 删除禁止FOWARD的规则$index;iptables -D FORWARD $index;done 3 | iptables --policy FORWARD ACCEPT 4 | -------------------------------------------------------------------------------- /src/main/resources/static/avatar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/avatar.gif -------------------------------------------------------------------------------- /src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/favicon.png -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 闲蛋中转
-------------------------------------------------------------------------------- /src/main/resources/static/reward.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/reward.gif -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-22fe9ae1.d62c05ee.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-258281f4]{margin:30px}.dashboard-text[data-v-258281f4]{font-size:30px;line-height:46px}.drawer-body[data-v-258281f4]{padding:20px}.demo-drawer__footer[data-v-258281f4]{padding-top:20px}.edit-cell[data-v-258281f4]{min-height:35px;cursor:pointer} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-2d423526.e6231e8e.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-dac09a84]{margin:30px}.dashboard-text[data-v-dac09a84]{font-size:30px;line-height:46px} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-480b22f6.a41dcdae.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-009db799]{margin:30px}.dashboard-text[data-v-009db799]{font-size:30px;line-height:46px}.drawer-body[data-v-009db799]{padding:20px}.demo-drawer__footer[data-v-009db799]{padding-top:20px}.edit-cell[data-v-009db799]{min-height:35px;cursor:pointer} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-4c26ca85.31d18062.css: -------------------------------------------------------------------------------- 1 | .el-drawer.rtl{overflow:scroll}.dashboard-container[data-v-2b255493]{margin:30px}.dashboard-text[data-v-2b255493]{font-size:30px;line-height:46px}.top-pannel[data-v-2b255493]{margin:10px 0 10px 0;color:#909399}.top-span[data-v-2b255493]{margin-right:20px;line-height:30px}.top-span.mobile[data-v-2b255493]{display:block}.top-text[data-v-2b255493]{color:#606266}.drawer-body[data-v-2b255493]{padding:20px}.item-box .server-status[data-v-2b255493]{position:absolute;right:15px;top:5px;font-size:12px}.item-box .server-status.online[data-v-2b255493]{color:#adff2f}.item-box .server-status.outline[data-v-2b255493]{color:red} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-632b8606.9ca93816.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-58607407]{margin:30px}.dashboard-text[data-v-58607407]{font-size:30px;line-height:46px} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-76cac56b.fcf8ba06.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-7fb10969]{margin:30px}.dashboard-text[data-v-7fb10969]{font-size:30px;line-height:46px}.drawer-body[data-v-7fb10969]{padding:20px}.demo-drawer__footer[data-v-7fb10969]{padding-top:20px}.state-normal[data-v-7fb10969]{font-size:12px}.state-fail[data-v-7fb10969]{color:#f56c6c;font-size:12px}.state-online[data-v-7fb10969]{color:#67c23a;font-size:12px}.item-box[data-v-7fb10969]{position:relative}.item-box .server-status[data-v-7fb10969]{position:absolute;right:15px;top:5px}.item-box .server-status.online[data-v-7fb10969]{color:#adff2f}.item-box .server-status.outline[data-v-7fb10969]{color:red}.item-box .port-manage[data-v-7fb10969]{line-height:30px;padding:0 5px;float:right;font-size:12px} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-76cea4de.3c7f5ad9.css: -------------------------------------------------------------------------------- 1 | .wscn-http404-container[data-v-c095f994]{-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);position:absolute;top:40%;left:50%}.wscn-http404[data-v-c095f994]{position:relative;width:1200px;padding:0 50px;overflow:hidden}.wscn-http404 .pic-404[data-v-c095f994]{position:relative;float:left;width:600px;overflow:hidden}.wscn-http404 .pic-404__parent[data-v-c095f994]{width:100%}.wscn-http404 .pic-404__child[data-v-c095f994]{position:absolute}.wscn-http404 .pic-404__child.left[data-v-c095f994]{width:80px;top:17px;left:220px;opacity:0;-webkit-animation-name:cloudLeft-data-v-c095f994;animation-name:cloudLeft-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}.wscn-http404 .pic-404__child.mid[data-v-c095f994]{width:46px;top:10px;left:420px;opacity:0;-webkit-animation-name:cloudMid-data-v-c095f994;animation-name:cloudMid-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1.2s;animation-delay:1.2s}.wscn-http404 .pic-404__child.right[data-v-c095f994]{width:62px;top:100px;left:500px;opacity:0;-webkit-animation-name:cloudRight-data-v-c095f994;animation-name:cloudRight-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}@-webkit-keyframes cloudLeft-data-v-c095f994{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@keyframes cloudLeft-data-v-c095f994{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@-webkit-keyframes cloudMid-data-v-c095f994{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@keyframes cloudMid-data-v-c095f994{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@-webkit-keyframes cloudRight-data-v-c095f994{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}@keyframes cloudRight-data-v-c095f994{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}.wscn-http404 .bullshit[data-v-c095f994]{position:relative;float:left;width:300px;padding:30px 0;overflow:hidden}.wscn-http404 .bullshit__oops[data-v-c095f994]{font-size:32px;line-height:40px;color:#1482f0;margin-bottom:20px;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__headline[data-v-c095f994],.wscn-http404 .bullshit__oops[data-v-c095f994]{font-weight:700;opacity:0;-webkit-animation-name:slideUp-data-v-c095f994;animation-name:slideUp-data-v-c095f994;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__headline[data-v-c095f994]{font-size:20px;line-height:24px;color:#222;margin-bottom:10px;-webkit-animation-delay:.1s;animation-delay:.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-c095f994]{font-size:13px;line-height:21px;color:grey;margin-bottom:30px;-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-c095f994],.wscn-http404 .bullshit__return-home[data-v-c095f994]{opacity:0;-webkit-animation-name:slideUp-data-v-c095f994;animation-name:slideUp-data-v-c095f994;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__return-home[data-v-c095f994]{display:block;float:left;width:110px;height:36px;background:#1482f0;border-radius:100px;text-align:center;color:#fff;font-size:14px;line-height:36px;cursor:pointer;-webkit-animation-delay:.3s;animation-delay:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes slideUp-data-v-c095f994{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes slideUp-data-v-c095f994{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-9fdcb8cc.e86064da.css: -------------------------------------------------------------------------------- 1 | @supports(-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-69a56f99]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-69a56f99]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-69a56f99]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-69a56f99]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-69a56f99]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-69a56f99]{position:relative}.login-container .title-container .title[data-v-69a56f99]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-69a56f99]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-a2c7894a.dc547638.css: -------------------------------------------------------------------------------- 1 | .el-drawer.rtl{overflow:scroll}.dashboard-container[data-v-1d9ca9eb]{margin:30px}.dashboard-text[data-v-1d9ca9eb]{font-size:30px;line-height:46px}.drawer-body[data-v-1d9ca9eb]{padding:20px}.drawer-body .drawer-select[data-v-1d9ca9eb]{margin:auto;margin-left:20px}.drawer-body .drawer-select.mobile[data-v-1d9ca9eb]{width:98%;margin:10px auto 0;display:block}.demo-drawer__footer[data-v-1d9ca9eb]{padding-top:20px}.el-drawer__body[data-v-1d9ca9eb]{overflow:auto}.reset[data-v-1d9ca9eb]{line-height:30px;padding:0 10px;float:right;font-size:12px}.item-box .server-status[data-v-1d9ca9eb]{position:absolute;right:15px;top:5px;font-size:12px}.item-box .server-status.online[data-v-1d9ca9eb]{color:#adff2f}.item-box .server-status.outline[data-v-1d9ca9eb]{color:red} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-e732e32c.1a2a53c6.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-70b61e79]{margin:30px}.dashboard-text[data-v-70b61e79]{font-size:30px;line-height:46px} -------------------------------------------------------------------------------- /src/main/resources/static/static/css/chunk-libs.3dfb7769.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}[hidden],template{display:none}#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;-webkit-box-shadow:0 0 10px #29d,0 0 5px #29d;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translateY(-4px);transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;-webkit-box-sizing:border-box;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}} -------------------------------------------------------------------------------- /src/main/resources/static/static/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/static/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /src/main/resources/static/static/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/static/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /src/main/resources/static/static/img/404.a57b6f31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/static/img/404.a57b6f31.png -------------------------------------------------------------------------------- /src/main/resources/static/static/img/404_cloud.0f4bc32b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/static/img/404_cloud.0f4bc32b.png -------------------------------------------------------------------------------- /src/main/resources/static/static/js/chunk-2d423526.31af6728.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d423526"],{4731:function(t,e,r){"use strict";r.d(e,"e",(function(){return n})),r.d(e,"f",(function(){return o})),r.d(e,"d",(function(){return i})),r.d(e,"h",(function(){return s})),r.d(e,"a",(function(){return l})),r.d(e,"b",(function(){return d}));var a=r("b775");function n(t){return Object(a["a"])({url:"/port/getPage",method:"post",data:t})}function o(t){return Object(a["a"])({url:"/port/getPortList",method:"post",data:t})}function i(t){return Object(a["a"])({url:"/port/delete",method:"get",params:t})}function s(t){return Object(a["a"])({url:"/port/save",method:"post",data:t})}function l(t){return Object(a["a"])({url:"/port/batchDelete",method:"post",data:t})}function d(t){return Object(a["a"])({url:"/port/batchSave",method:"post",data:t})}},4942:function(t,e,r){"use strict";var a=r("ced1"),n=r.n(a);n.a},aa22:function(t,e,r){"use strict";r.d(e,"d",(function(){return n})),r.d(e,"c",(function(){return o})),r.d(e,"b",(function(){return i})),r.d(e,"a",(function(){return s})),r.d(e,"e",(function(){return l}));var a=r("b775");function n(t){return Object(a["a"])({url:"/server/getPage",method:"post",data:t})}function o(t){return Object(a["a"])({url:"/server/getList",method:"get",data:t})}function i(t){return Object(a["a"])({url:"/server/getForwardServerList",method:"get",params:t})}function s(t){return Object(a["a"])({url:"/server/delete",method:"get",params:t})}function l(t){return Object(a["a"])({url:"/server/save",method:"post",data:t})}},b2dd:function(t,e,r){"use strict";r.r(e);var a=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"app-container"},[r("div",{staticClass:"searchBody"},[r("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchForm},nativeOn:{submit:function(t){t.preventDefault()}}},[r("el-form-item",[r("el-button",{attrs:{type:"success",size:"mini",icon:"el-icon-plus"},on:{click:t.showAddDialog}},[t._v("添加端口")])],1)],1)],1),r("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,"row-key":"id",border:"","tree-props":{children:"children",hasChildren:"hasChildren"}}},[r("el-table-column",{attrs:{label:"行号",type:"index",width:"50"}}),r("el-table-column",{attrs:{label:"本地端口",prop:"localPort"}}),r("el-table-column",{attrs:{label:"外网端口",prop:"internetPort"}}),r("el-table-column",{attrs:{label:"所属服务器",prop:"serverName"}}),r("el-table-column",{attrs:{label:"服务器IP",prop:"serverHost"}}),r("el-table-column",{attrs:{label:"创建时间",width:"160"},scopedSlots:t._u([{key:"default",fn:function(e){return[r("span",[t._v(t._s(t._f("parseTime")(e.row.createTime,"{y}-{m}-{d} {h}:{i}:{s}")))])]}}])}),r("el-table-column",{attrs:{label:"修改时间",width:"160"},scopedSlots:t._u([{key:"default",fn:function(e){return[r("span",[t._v(t._s(t._f("parseTime")(e.row.updateTime,"{y}-{m}-{d} {h}:{i}:{s}")))])]}}])}),r("el-table-column",{attrs:{label:"操作",fixed:"right",width:"250"},scopedSlots:t._u([{key:"default",fn:function(e){return[r("el-button",{attrs:{type:"primary",size:"mini",title:"编辑"},on:{click:function(r){return t.showEditDialog(e.row)}}},[t._v("编辑")]),r("el-button",{attrs:{type:"primary",size:"mini",title:"复制"},on:{click:function(r){return t.copyRow(e.row)}}},[t._v("复制")]),r("el-button",{attrs:{type:"danger",size:"mini",title:"删除"},on:{click:function(r){return t.deleteData(e.row)}}},[t._v("删除")])]}}])})],1),r("div",{staticClass:"block"},[r("el-pagination",{attrs:{background:"","page-sizes":[10,20,50],"page-size":t.searchForm.pageSize,"current-page":t.searchForm.pageNum,layout:"total, sizes, prev, pager, next, jumper",total:t.dataTotal},on:{"size-change":t.handleSizeChange,"current-change":t.handleCurrentChange}})],1),r("el-dialog",{attrs:{title:"添加端口",visible:t.addDialog,width:"30%"},on:{"update:visible":function(e){t.addDialog=e}}},[r("el-form",{ref:"addForm",attrs:{model:t.addForm,rules:t.addFormRules,"label-width":"120px",size:"small"}},[r("el-form-item",{attrs:{label:"所属服务器",prop:"serverId"}},[r("el-select",{staticStyle:{width:"100%"},attrs:{size:"mini",clearable:""},model:{value:t.addForm.serverId,callback:function(e){t.$set(t.addForm,"serverId",e)},expression:"addForm.serverId"}},t._l(t.serverList,(function(t,e){return r("el-option",{key:t+e,attrs:{label:t.serverName,value:t.id}})})),1)],1),r("el-form-item",{attrs:{label:"本地端口",prop:"localPort",rules:[{required:!0,message:"端口不能为空"},{type:"number",message:"端口必须为数字值"}]}},[r("el-input",{attrs:{size:"mini"},model:{value:t.addForm.localPort,callback:function(e){t.$set(t.addForm,"localPort",t._n(e))},expression:"addForm.localPort"}})],1),r("el-form-item",{attrs:{label:"外网端口",prop:"internetPort",rules:[{required:!0,message:"端口不能为空"},{type:"number",message:"端口必须为数字值"}]}},[r("el-input",{attrs:{size:"mini"},model:{value:t.addForm.internetPort,callback:function(e){t.$set(t.addForm,"internetPort",t._n(e))},expression:"addForm.internetPort"}})],1)],1),r("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[r("el-button",{on:{click:function(e){t.addDialog=!1}}},[t._v("取 消")]),r("el-button",{attrs:{type:"primary"},on:{click:t.confirmAddForm}},[t._v("确 定")])],1)],1)],1)},n=[],o=r("4731"),i=r("aa22"),s={data:function(){return{tableData:[],dataTotal:null,serverList:[],searchForm:{pageSize:10,pageNum:1},addForm:{id:null,username:null,addType:"add",password:null},addFormRules:{serverId:[{required:!0,trigger:"blur",message:"必需项"}],localPort:[{required:!0,trigger:"blur",message:"必需项"}],internetPort:[{required:!0,trigger:"blur",message:"必需项"}]},addDialog:!1}},mounted:function(){this.initServerList(),this.getData()},methods:{getData:function(){var t=this;Object(o["e"])(this.searchForm).then((function(e){t.tableData=e.data.list,t.dataTotal=e.data.total}))},deleteData:function(t){var e=this;this.$confirm("确认删除?",{confirmButtonText:"确认",cancelButtonText:"取消",type:"warning"}).then((function(){Object(o["deleteData"])({id:t.id}).then((function(t){e.$notify({message:"删除成功",type:"success"}),e.getData()}))}))},confirmAddForm:function(){var t=this;this.$refs["addForm"].validate((function(e){e&&Object(o["save"])(t.addForm).then((function(e){t.$notify({message:"保存成功",type:"success"}),t.addDialog=!1,t.getData()}))}))},handleSizeChange:function(t){this.searchForm.pageSize=t,this.getData()},handleCurrentChange:function(t){this.searchForm.pageNum=t,this.getData()},initServerList:function(){var t=this;Object(i["c"])().then((function(e){t.serverList=e.data}))},showAddDialog:function(){this.addDialog=!0,this.addForm.addType="add",this.addForm.id=null},showEditDialog:function(t){this.addDialog=!0,this.addForm=t,this.addForm.addType="edit"},copyRow:function(t){this.addDialog=!0,this.addForm=t,this.addForm.id=null,this.addForm.addType="add"}}},l=s,d=(r("4942"),r("2877")),u=Object(d["a"])(l,a,n,!1,null,"dac09a84",null);e["default"]=u.exports},ced1:function(t,e,r){}}]); -------------------------------------------------------------------------------- /src/main/resources/static/static/js/chunk-480b22f6.4f228d1f.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-480b22f6"],{"08db":function(t,a,e){"use strict";var r=e("2e6f"),s=e.n(r);s.a},"2e6f":function(t,a,e){},"52f9":function(t,a,e){"use strict";e.r(a);var r=function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("div",{staticClass:"app-container"},[e("div",{staticStyle:{"line-height":"50px",color:"#606266"}},[e("el-row",[t._v(" 如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 🍹, 你的支持是我继续更新的动力🙏🙏🙏 ")]),e("el-row",[e("img",{staticClass:"user-avatar",attrs:{src:"reward.gif",width:"200px"}})]),e("el-row",[e("el-col",[e("i",{staticClass:"el-icon-wallet"}),t._v(" "),e("a",{staticStyle:{color:"#1989fa"},attrs:{target:"_blank",href:"https://www.paypal.com/paypalme/MrTreeOLO",hre:""}},[t._v("PayPal打赏")])])],1),e("el-row",[e("i",{staticClass:"el-icon-milk-tea"}),t._v(" USDT打赏: "),e("a",{attrs:{hre:""}},[t._v("TEVJQK8iPr2aWEQJ89NTWtSP6bVc8G1xGu")])]),e("el-row",[e("img",{staticClass:"user-avatar",attrs:{src:"usdt.jpeg",width:"280px"}})])],1)])},s=[],l={data:function(){return{tableData:[],dataTotal:null}},mounted:function(){this.getData()},methods:{}},i=l,c=(e("08db"),e("2877")),n=Object(c["a"])(i,r,s,!1,null,"009db799",null);a["default"]=n.exports}}]); -------------------------------------------------------------------------------- /src/main/resources/static/static/js/chunk-632b8606.5ee39ba4.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-632b8606"],{"20ec":function(a,t,e){},"7a85":function(a,t,e){"use strict";var n=e("20ec"),c=e.n(n);c.a},9406:function(a,t,e){"use strict";e.r(t);var n=function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("div",{staticClass:"dashboard-container"},[e("div",{staticClass:"dashboard-text"},[a._v(a._s(a.name)+", 欢迎使用闲蛋中转")])])},c=[],s=e("5530"),i=e("2f62"),o={name:"Dashboard",computed:Object(s["a"])({},Object(i["b"])(["name"]))},r=o,u=(e("7a85"),e("2877")),d=Object(u["a"])(r,n,c,!1,null,"58607407",null);t["default"]=d.exports}}]); -------------------------------------------------------------------------------- /src/main/resources/static/static/js/chunk-76cea4de.78bea551.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-76cea4de"],{"26fc":function(t,s,e){t.exports=e.p+"static/img/404_cloud.0f4bc32b.png"},"8cdb":function(t,s,e){"use strict";e.r(s);var a=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"wscn-http404-container"},[e("div",{staticClass:"wscn-http404"},[t._m(0),e("div",{staticClass:"bullshit"},[e("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._m(1),e("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),e("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),e("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"pic-404"},[a("img",{staticClass:"pic-404__parent",attrs:{src:e("a36b"),alt:"404"}}),a("img",{staticClass:"pic-404__child left",attrs:{src:e("26fc"),alt:"404"}}),a("img",{staticClass:"pic-404__child mid",attrs:{src:e("26fc"),alt:"404"}}),a("img",{staticClass:"pic-404__child right",attrs:{src:e("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"bullshit__info"},[t._v("All rights reserved "),e("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(e("97ef"),e("2877")),r=Object(n["a"])(l,a,c,!1,null,"c095f994",null);s["default"]=r.exports},"97ef":function(t,s,e){"use strict";var a=e("ed94"),c=e.n(a);c.a},a36b:function(t,s,e){t.exports=e.p+"static/img/404.a57b6f31.png"},ed94:function(t,s,e){}}]); -------------------------------------------------------------------------------- /src/main/resources/static/static/js/chunk-9fdcb8cc.b71478a3.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-9fdcb8cc"],{2017:function(e,s,t){"use strict";var n=t("cafe"),o=t.n(n);o.a},2155:function(e,s,t){"use strict";var n=t("ccec"),o=t.n(n);o.a},"9ed6":function(e,s,t){"use strict";t.r(s);var n=function(){var e=this,s=e.$createElement,t=e._self._c||s;return t("div",{staticClass:"login-container"},[t("el-form",{ref:"loginForm",staticClass:"login-form",attrs:{model:e.loginForm,rules:e.loginRules,"auto-complete":"on","label-position":"left"}},[t("div",{staticClass:"title-container"},[t("h3",{staticClass:"title"},[e._v("闲蛋中转面板")])]),t("el-form-item",{attrs:{prop:"username",rules:[{required:!0,message:"用户名不能为空"}]}},[t("span",{staticClass:"svg-container"},[t("svg-icon",{attrs:{"icon-class":"user"}})],1),t("el-input",{ref:"username",attrs:{placeholder:"用户名",name:"username",type:"text",tabindex:"1","auto-complete":"on"},model:{value:e.loginForm.username,callback:function(s){e.$set(e.loginForm,"username",s)},expression:"loginForm.username"}})],1),t("el-form-item",{attrs:{prop:"password"}},[t("span",{staticClass:"svg-container"},[t("svg-icon",{attrs:{"icon-class":"password"}})],1),t("el-input",{key:e.passwordType,ref:"password",attrs:{type:e.passwordType,placeholder:"密码",name:"password",tabindex:"2","auto-complete":"on"},nativeOn:{keyup:function(s){return!s.type.indexOf("key")&&e._k(s.keyCode,"enter",13,s.key,"Enter")?null:e.handleLogin(s)}},model:{value:e.loginForm.password,callback:function(s){e.$set(e.loginForm,"password",s)},expression:"loginForm.password"}}),t("span",{staticClass:"show-pwd",on:{click:e.showPwd}},[t("svg-icon",{attrs:{"icon-class":"password"===e.passwordType?"eye":"eye-open"}})],1)],1),t("el-button",{staticStyle:{width:"100%","margin-bottom":"30px"},attrs:{loading:e.loading,type:"primary"},nativeOn:{click:function(s){return s.preventDefault(),e.handleLogin(s)}}},[e._v("登录")])],1)],1)},o=[],r={name:"Login",data:function(){var e=function(e,s,t){s.length<4?t(new Error("请输入密码,最少6位")):t()};return{loginForm:{username:"",password:""},loginRules:{password:[{required:!0,trigger:"blur",validator:e}]},loading:!1,passwordType:"password",redirect:void 0}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},methods:{showPwd:function(){var e=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick((function(){e.$refs.password.focus()}))},handleLogin:function(){var e=this;this.$refs.loginForm.validate((function(s){if(!s)return console.log("error submit!!"),!1;e.loading=!0,e.$store.dispatch("user/login",e.loginForm).then((function(){e.$router.push({path:e.redirect||"/"}),e.loading=!1})).catch((function(){e.loading=!1}))}))}}},a=r,i=(t("2017"),t("2155"),t("2877")),l=Object(i["a"])(a,n,o,!1,null,"69a56f99",null);s["default"]=l.exports},cafe:function(e,s,t){},ccec:function(e,s,t){}}]); -------------------------------------------------------------------------------- /src/main/resources/static/static/js/chunk-e732e32c.36ae8557.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-e732e32c"],{"26b0":function(t,e,a){"use strict";a.r(e);var o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("el-form",{ref:"form",staticStyle:{width:"400px","max-width":"98%"},attrs:{model:t.addForm,"label-width":"80px"}},[a("el-form-item",{attrs:{label:"系统升级"}},[a("el-button",{attrs:{size:"mini"},on:{click:t.exportSystemData}},[t._v("导出数据")]),a("el-button",{attrs:{type:"primary",size:"mini"},on:{click:t.importSystemData}},[t._v("导入数据")])],1),a("el-form-item",{attrs:{label:"当前版本:"}},[a("span",[t._v(t._s(t.addForm.version))])]),a("el-form-item",{attrs:{label:"TG:",prop:"telegram"}},[a("el-input",{attrs:{size:"mini",placeholder:"请输入tg链接,租户到期会弹框提示"},model:{value:t.addForm.telegram,callback:function(e){t.$set(t.addForm,"telegram",e)},expression:"addForm.telegram"}})],1),a("el-form-item",{attrs:{label:"QQ:",prop:"qq"}},[a("el-input",{attrs:{size:"mini",placeholder:"请输入qq号,租户到期会弹框提示"},model:{value:t.addForm.qq,callback:function(e){t.$set(t.addForm,"qq",e)},expression:"addForm.qq"}})],1),a("el-form-item",{attrs:{label:"邮箱:",prop:"email"}},[a("el-input",{attrs:{size:"mini",placeholder:"请输入邮箱地址,租户到期会弹框提示"},model:{value:t.addForm.email,callback:function(e){t.$set(t.addForm,"email",e)},expression:"addForm.email"}})],1),a("el-button",{staticStyle:{float:"right"},attrs:{size:"mini",type:"primary"},on:{click:t.saveSysConfig}},[t._v("保存配置")])],1),a("el-dialog",{attrs:{title:"导入数据",visible:t.importDialog,width:"30%"},on:{"update:visible":function(e){t.importDialog=e}}},[a("el-form",{ref:"addForm",attrs:{model:t.addForm,"label-width":"80px",size:"small"}},[a("el-form-item",{attrs:{label:"文件",prop:"addForm"}},[a("el-upload",{ref:"upload",staticClass:"upload-demo",attrs:{accept:".json",action:"/system/importData","http-request":t.customUpload,"auto-upload":!1,"show-file-list":!0,"file-list":t.addForm.fileList}},[a("el-button",{attrs:{size:"small",type:"primary"}},[t._v("选择文件")])],1)],1)],1),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{on:{click:function(e){t.importDialog=!1}}},[t._v("取 消")]),a("el-button",{attrs:{type:"primary"},on:{click:t.confirmImport}},[t._v("确 定")])],1)],1)],1)},i=[],n=(a("d3b7"),a("3ca3"),a("ddb0"),a("2b3d"),a("8593")),r={data:function(){return{tableData:[],dataTotal:null,addForm:{id:null,telegram:null,qq:null,email:null,version:null},importDialog:!1}},mounted:function(){this.getSysConfig()},methods:{saveSysConfig:function(){var t=this;Object(n["d"])(this.addForm).then((function(e){t.$notify({message:"保存成功",type:"success"}),t.getSysConfig()}))},getSysConfig:function(){var t=this;Object(n["b"])({}).then((function(e){t.addForm=e.data}))},exportSystemData:function(){var t=this;Object(n["a"])({}).then((function(e){"0"===e.code?t.fileConversion("system-export-".concat((new Date).getTime(),".json"),JSON.stringify(e.data)):t.$notify({message:e.msg,type:"error"})})).catch((function(e){t.$notify({message:e.message,type:"error"})}))},importSystemData:function(){this.importDialog=!0},customUpload:function(t){var e=this;Object(n["c"])(t).then((function(t){e.importDialog=!1,e.$notify({message:"导入完成",type:"success"})}))},confirmImport:function(){this.$refs.upload.submit()},fileConversion:function(t,e){var a=t,o=new Blob([e],{type:"application/text"});if(window.navigator.msSaveOrOpenBlob)navigator.msSaveBlob(o);else{var i=document.createElement("a");i.download=a,i.style.display="none",i.href=URL.createObjectURL(o),document.body.appendChild(i),i.click(),document.body.removeChild(i)}}}},l=r,s=(a("e152"),a("2877")),m=Object(s["a"])(l,o,i,!1,null,"70b61e79",null);e["default"]=m.exports},8593:function(t,e,a){"use strict";a.d(e,"a",(function(){return r})),a.d(e,"c",(function(){return l})),a.d(e,"d",(function(){return s})),a.d(e,"b",(function(){return m}));var o=a("b775"),i=a("5a30"),n=a.n(i);function r(t){return Object(o["a"])({url:"/system/exportData",method:"get",params:t})}function l(t){var e=new n.a;return e.append("file",t.file),Object(o["a"])({method:"post",url:"/system/importData",headers:{"Content-Type":"multipart/form-data"},data:e})}function s(t){return Object(o["a"])({url:"/sysConfig/save",method:"post",data:t})}function m(t){return Object(o["a"])({url:"/sysConfig/getSysConfig",method:"get",params:t})}},"8fe1":function(t,e,a){},e152:function(t,e,a){"use strict";var o=a("8fe1"),i=a.n(o);i.a}}]); -------------------------------------------------------------------------------- /src/main/resources/static/usdt.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noobcfy/forwordpanel/e12657eb02e35e54b8d66daed60b836488885c18/src/main/resources/static/usdt.jpeg -------------------------------------------------------------------------------- /src/test/java/com/leeroy/forwordpanel/forwordpanel/ForwordpanelApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ForwordpanelApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/com/leeroy/forwordpanel/forwordpanel/MyUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel; 2 | 3 | import com.jcraft.jsch.UserInfo; 4 | 5 | /** 6 | * This class provide interface to feedback information to the user. 7 | */ 8 | public class MyUserInfo implements UserInfo { 9 | private String password; 10 | 11 | private String passphrase; 12 | 13 | @Override 14 | public String getPassphrase() { 15 | System.out.println("MyUserInfo.getPassphrase()"); 16 | return null; 17 | } 18 | 19 | @Override 20 | public String getPassword() { 21 | System.out.println("MyUserInfo.getPassword()"); 22 | return null; 23 | } 24 | 25 | @Override 26 | public boolean promptPassphrase(final String arg0) { 27 | System.out.println("MyUserInfo.promptPassphrase()"); 28 | System.out.println(arg0); 29 | return false; 30 | } 31 | 32 | @Override 33 | public boolean promptPassword(final String arg0) { 34 | System.out.println("MyUserInfo.promptPassword()"); 35 | System.out.println(arg0); 36 | return false; 37 | } 38 | 39 | @Override 40 | public boolean promptYesNo(final String arg0) { 41 | System.out.println("MyUserInfo.promptYesNo()"); 42 | System.out.println(arg0); 43 | if (arg0.contains("The authenticity of host")) { 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | @Override 50 | public void showMessage(final String arg0) { 51 | System.out.println("MyUserInfo.showMessage()"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/com/leeroy/forwordpanel/forwordpanel/SSHCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.leeroy.forwordpanel.forwordpanel; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | import java.util.Vector; 6 | 7 | import com.jcraft.jsch.Channel; 8 | import com.jcraft.jsch.ChannelExec; 9 | import com.jcraft.jsch.JSch; 10 | import com.jcraft.jsch.JSchException; 11 | import com.jcraft.jsch.Session; 12 | 13 | /** 14 | * This class provide interface to execute command on remote Linux. 15 | */ 16 | 17 | public class SSHCommandExecutor { 18 | private String ipAddress; 19 | 20 | private String username; 21 | 22 | private String password; 23 | 24 | public static final int DEFAULT_SSH_PORT = 34204; 25 | 26 | private Vector stdout; 27 | 28 | public SSHCommandExecutor(final String ipAddress, final String username, final String password) { 29 | this.ipAddress = ipAddress; 30 | this.username = username; 31 | this.password = password; 32 | stdout = new Vector<>(); 33 | } 34 | 35 | public int execute(final String command) { 36 | int returnCode = 0; 37 | JSch jsch = new JSch(); 38 | MyUserInfo userInfo = new MyUserInfo(); 39 | 40 | try { 41 | // Create and connect session. 42 | Session session = jsch.getSession(username, ipAddress, DEFAULT_SSH_PORT); 43 | session.setPassword(password); 44 | session.setUserInfo(userInfo); 45 | session.connect(); 46 | 47 | // Create and connect channel. 48 | Channel channel = session.openChannel("exec"); 49 | ((ChannelExec) channel).setCommand(command); 50 | 51 | channel.setInputStream(null); 52 | BufferedReader input = new BufferedReader(new InputStreamReader(channel 53 | .getInputStream())); 54 | 55 | channel.connect(); 56 | System.out.println("The remote command is: " + command); 57 | 58 | // Get the output of remote command. 59 | String line; 60 | while ((line = input.readLine()) != null) { 61 | stdout.add(line); 62 | } 63 | input.close(); 64 | 65 | // Get the return code only after the channel is closed. 66 | if (channel.isClosed()) { 67 | returnCode = channel.getExitStatus(); 68 | } 69 | 70 | // Disconnect the channel and session. 71 | channel.disconnect(); 72 | session.disconnect(); 73 | } catch (JSchException e) { 74 | // TODO Auto-generated catch block 75 | e.printStackTrace(); 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | } 79 | return returnCode; 80 | } 81 | 82 | public Vector getResultSet() { 83 | return stdout; 84 | } 85 | 86 | public String getResult() { 87 | return stdout.get(0); 88 | } 89 | 90 | public static void main(final String [] args) { 91 | SSHCommandExecutor sshExecutor = new SSHCommandExecutor("120.241.154.4", "root", "28L8CegNk9"); 92 | sshExecutor.execute(String.format("iptables -n -v -L -t filter -x | grep %s | awk '{print $2}'", "34.92.251.167")); 93 | System.out.println(sshExecutor.getResult()); 94 | } 95 | } 96 | --------------------------------------------------------------------------------