diff --git a/pom.xml b/pom.xml index 915b99c0..40e19816 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ sop-parent 5.0.0-SNAPSHOT pom - 一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台 + 一个开放平台解决方案项目,基于Dubbo实现,目标是能够让用户快速得搭建起自己的开放平台 doc diff --git a/sop-auth/.gitignore b/sop-auth/.gitignore deleted file mode 100644 index 153c9335..00000000 --- a/sop-auth/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -HELP.md -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -/build/ - -### VS Code ### -.vscode/ diff --git a/sop-auth/pom.xml b/sop-auth/pom.xml deleted file mode 100644 index 9c978920..00000000 --- a/sop-auth/pom.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - com.gitee.sop - sop-parent - 5.0.0-SNAPSHOT - ../pom.xml - - - 4.0.0 - sop-auth - sop-auth - 授权程序 - - - 1.8 - 0.31 - - - - - - - com.gitee.sop - sop-service-common - 5.0.0-SNAPSHOT - - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - - - - com.gitee.sop - sdk-java - 5.0.0-SNAPSHOT - - - - com.squareup.okhttp3 - okhttp - 3.14.0 - - - - net.oschina.durcframework - fastmybatis-spring-boot-starter - - - mysql - mysql-connector-java - runtime - - - - - org.apache.oltu.oauth2 - org.apache.oltu.oauth2.authzserver - ${oltu.version} - - - org.apache.oltu.oauth2 - org.apache.oltu.oauth2.resourceserver - ${oltu.version} - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.springframework.boot - spring-boot-starter-data-redis - - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - com.google.guava - guava - 27.1-jre - compile - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - diff --git a/sop-auth/readme.md b/sop-auth/readme.md deleted file mode 100644 index 6c11e032..00000000 --- a/sop-auth/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -# 应用授权服务 - -- 启动注册中心、网关、本服务(sop-auth) -- 浏览器访问:http://localhost:8087/oauth2/appToAppAuth?app_id=2019032617262200001&redirect_uri=http%3a%2f%2flocalhost%3a8087%2foauth2callback -- 输入用户名密码登录,这里是`zhangsan/123456` - -授权接口在`OAuth2Controller`中,查看回调接口在`CallbackController`中 - -回调接口应该由开发者实现,这里为了演示,写在一起。 - -token的维护,重点关注`OAuth2ManagerRedis.java` diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/SopAuthApplication.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/SopAuthApplication.java deleted file mode 100644 index 29595038..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/SopAuthApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gitee.sop.sopauth; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; - -@EnableDiscoveryClient -@SpringBootApplication -public class SopAuthApplication { - - public static void main(String[] args) { - SpringApplication.run(SopAuthApplication.class, args); - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AccessToken.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AccessToken.java deleted file mode 100644 index dc238bcc..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AccessToken.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -/** - * @author 六如 - */ -public class AccessToken { - /** 过期时间,毫秒 */ - private long expireTimeMillis; - private OpenUser openUser; - - /** - * @param expireIn 有效时长,秒 - * @param openUser - */ - public AccessToken(long expireIn, OpenUser openUser) { - super(); - if(expireIn <= 0) { - throw new IllegalArgumentException("expireIn必须大于0"); - } - this.expireTimeMillis = System.currentTimeMillis() + (expireIn * 1000); - this.openUser = openUser; - } - - public boolean isExpired() { - return expireTimeMillis < System.currentTimeMillis(); - } - - public long getExpireTimeMillis() { - return expireTimeMillis; - } - - public void setExpireTimeMillis(long expireTimeMillis) { - this.expireTimeMillis = expireTimeMillis; - } - - public OpenUser getOpenUser() { - return openUser; - } - - public void setOpenUser(OpenUser openUser) { - this.openUser = openUser; - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AppIdManager.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AppIdManager.java deleted file mode 100644 index 0eac59f9..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AppIdManager.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -/** - * @author 六如 - */ -public interface AppIdManager { - /** - * 是否是合法的appId - * - * @param appId - * @return true:合法 - */ - boolean isValidAppId(String appId); -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenParam.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenParam.java deleted file mode 100644 index 6f42634e..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenParam.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -import lombok.Data; - -import javax.validation.constraints.NotBlank; - -/** - * 获取accessToken参数对象 - * @author 六如 - */ -@Data -public class FetchTokenParam { - /** - * 授权类型。
- * 如果使用app_auth_code换取token,则为authorization_code, - * 如果使用refresh_token换取新的token,则为refresh_token - */ - @NotBlank(message = "授权类型不能为空") - private String grant_type; - - /** - * 授权码.与refresh_token二选一,用户对应用授权后得到,即第一步中开发者获取到的app_auth_code值 - */ - private String code; - - /** - * 刷新令牌.与code二选一,可为空,刷新令牌时使用 - */ - private String refresh_token; -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenResult.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenResult.java deleted file mode 100644 index b99de992..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenResult.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -import lombok.Data; - -/** - * 使用app_auth_code换取app_auth_token返回结果 - * @author 六如 - */ -@Data -public class FetchTokenResult { - /** - * 授权令牌 - */ - private String app_auth_token; - - /** - * 用户id - */ - private String user_id; - - /** - * 令牌有效期. -1:永久有效 - */ - private long expires_in = -1; - - /** - * 刷新令牌有效期. -1:永久有效 - */ - private long re_expires_in = -1; - - /** - * 刷新令牌时使用。 - * 刷新令牌后,我们会保证老的app_auth_token从刷新开始10分钟内可继续使用,请及时替换为最新token - */ - private String app_refresh_token; - - private String error; - private String error_description; -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Config.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Config.java deleted file mode 100644 index 9a01f6a6..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Config.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class OAuth2Config { - - private static OAuth2Config instance = new OAuth2Config(); - - public static OAuth2Config getInstance() { - return instance; - } - - public static void setInstance(OAuth2Config instance) { - OAuth2Config.instance = instance; - } - - /** - * 应用授权的app_auth_code唯一的;app_auth_code使用一次后失效,一天(从生成app_auth_code开始的24小时)未被使用自动过期; - * app_auth_token永久有效。 - */ - private int codeTimeoutSeconds = 86400; - /** - * accessToken有效时间,单位秒 - * -1 永久有效 - */ - private int accessTokenExpiresIn = -1; - - /** - * app_refresh_token有效时间,单位秒,accessToken有效时间的3倍。当小于0,永久有效 - */ - private int refreshTokenExpiresIn = accessTokenExpiresIn * 3; - - /** - * 刷新令牌后,我们会保证老的app_auth_token从刷新开始10分钟内可继续使用,请及时替换为最新token - */ - private int afterRefreshExpiresIn = 60 * 10; - - /** - * 登录视图页面用于,mvc视图,如:loginView - */ - private String oauth2LoginUri = "/oauth2/login"; -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Manager.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Manager.java deleted file mode 100644 index 772b9d27..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Manager.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.gitee.sop.sopauth.auth; - - -import com.gitee.sop.sopauth.auth.exception.LoginErrorException; - -import javax.servlet.http.HttpServletRequest; - -/** - * 认证服务,需要自己实现 - * @author 六如 - * - */ -public interface OAuth2Manager { - - /** - * 添加 auth code - * - * @param authCode - * code值 - * @param authUser - * 用户 - */ - void addAuthCode(String authCode, OpenUser authUser); - - /** - * 添加accessToken - * @param accessToken token值 - * @param refreshToken refreshToken - * @param authUser 用户 - */ - void addAccessToken(String accessToken, String refreshToken, OpenUser authUser); - - - /** - * 删除这个accessToken - * @param accessToken - */ - void removeAccessToken(String accessToken); - - /** - * 删除这个refreshToken - * @param refreshToken - */ - void removeRefreshToken(String refreshToken); - - /** - * 获取RefreshToken - * @param refreshToken - * @return 返回Token信息 - */ - RefreshToken getRefreshToken(String refreshToken); - - /** - * 验证auth code是否有效 - * - * @param authCode - * @return 无效返回false - */ - boolean checkAuthCode(String authCode); - - /** - * 根据auth code获取用户 - * - * @param authCode - * @return 返回用户 - */ - OpenUser getUserByAuthCode(String authCode); - - /** - * 根据access token获取用户 - * - * @param accessToken - * token值 - * @return 返回用户 - */ - OpenUser getUserByAccessToken(String accessToken); - - /** - * 用户登录,需判断是否已经登录 - * @param request - * @return 返回用户对象 - * @throws LoginErrorException 登录失败异常 - */ - OpenUser login(HttpServletRequest request) throws LoginErrorException; -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Service.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Service.java deleted file mode 100644 index 230ab094..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Service.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -import org.apache.oltu.oauth2.common.exception.OAuthSystemException; -import org.apache.oltu.oauth2.common.message.OAuthResponse; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.net.URISyntaxException; - -/** - * @author 六如 - */ -public interface OAuth2Service { - - /** - * oauth2授权,获取code. - *
-     *  1、首先通过如http://localhost:8080/api/authorize?client_id=test&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2callback访问授权页面;
-        2、该控制器首先检查clientId是否正确;如果错误将返回相应的错误信息;
-        3、然后判断用户是否登录了,如果没有登录首先到登录页面登录;
-        4、登录成功后生成相应的code即授权码,然后重定向到客户端地址,如http://localhost:8080/oauth2callback?code=6d250650831fea227749f49a5b49ccad;在重定向到的地址中会带上code参数(授权码),接着客户端可以根据授权码去换取accessToken。
-     * 
- * - * @param request req - * @param response resp - * @param authConfig 配置 - * @return 返回响应内容 - * @throws URISyntaxException - * @throws OAuthSystemException - */ - OAuthResponse authorize(HttpServletRequest request, HttpServletResponse response, OAuth2Config authConfig) - throws URISyntaxException, OAuthSystemException; - - - /** - * 通过code获取accessToken. - * @param fetchTokenParam - * @param authConfig 配置项 - * @return 返回响应内容 - * @throws URISyntaxException - * @throws OAuthSystemException - */ - FetchTokenResult accessToken(FetchTokenParam fetchTokenParam, OAuth2Config authConfig); -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OpenUser.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OpenUser.java deleted file mode 100644 index e4f0b1f6..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OpenUser.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -import java.io.Serializable; - -/** - * 如果要使用oauth2功能,自己的用户类需要实现这个对象 - * - * @author 六如 - */ -public interface OpenUser extends Serializable { - /** - * 返回用户id - * @return - */ - String getUserId(); - /** - * 返回用户名 - * - * @return 返回用户名 - */ - String getUsername(); -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/RefreshToken.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/RefreshToken.java deleted file mode 100644 index 48e98a3f..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/RefreshToken.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -import java.io.Serializable; - -/** - * @author 六如 - */ -public class RefreshToken implements Serializable { - private static final long serialVersionUID = 1L; - - private String accessToken; - private OpenUser openUser; - - public RefreshToken(String accessToken, OpenUser openUser) { - super(); - this.accessToken = accessToken; - this.openUser = openUser; - } - - public RefreshToken() { - super(); - } - - public String getAccessToken() { - return accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public OpenUser getOpenUser() { - return openUser; - } - - public void setOpenUser(OpenUser openUser) { - this.openUser = openUser; - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/TokenPair.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/TokenPair.java deleted file mode 100644 index bcedce2b..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/TokenPair.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gitee.sop.sopauth.auth; - -/** - * 存放accessToken和refreshToken - * - * @author 六如 - * - */ -public class TokenPair { - private String accessToken; - private String refreshToken; - - public String getAccessToken() { - return accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public String getRefreshToken() { - return refreshToken; - } - - public void setRefreshToken(String refreshToken) { - this.refreshToken = refreshToken; - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/exception/LoginErrorException.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/exception/LoginErrorException.java deleted file mode 100644 index 131363ce..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/exception/LoginErrorException.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.sop.sopauth.auth.exception; - -/** - * @author 六如 - */ -public class LoginErrorException extends Exception { - private static final long serialVersionUID = -6721499454527023339L; - - public LoginErrorException() { - super(); - } - - public LoginErrorException(String message, Throwable cause) { - super(message, cause); - } - - public LoginErrorException(String message) { - super(message); - } - - public LoginErrorException(Throwable cause) { - super(cause); - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/AppIdManagerImpl.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/AppIdManagerImpl.java deleted file mode 100644 index 866c99de..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/AppIdManagerImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.sopauth.auth.impl; - -import com.gitee.sop.sopauth.auth.AppIdManager; -import com.gitee.sop.sopauth.entity.IsvInfo; -import com.gitee.sop.sopauth.mapper.IsvInfoMapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - * @author 六如 - */ -@Service -@Slf4j -public class AppIdManagerImpl implements AppIdManager { - - public static final int FORBIDDEN = 2; - @Autowired - private IsvInfoMapper isvInfoMapper; - - @Override - public boolean isValidAppId(String appId) { - IsvInfo isvInfo = isvInfoMapper.getByColumn("app_key", appId); - if (isvInfo == null) { - return false; - } - if (isvInfo.getStatus().intValue() == FORBIDDEN) { - log.error("appId已禁用:{}", appId); - return false; - } - return true; - } -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerCache.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerCache.java deleted file mode 100644 index 4ac46c69..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerCache.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.gitee.sop.sopauth.auth.impl; - -import com.gitee.fastmybatis.core.query.Query; -import com.gitee.sop.sopauth.auth.OAuth2Config; -import com.gitee.sop.sopauth.auth.OAuth2Manager; -import com.gitee.sop.sopauth.auth.OpenUser; -import com.gitee.sop.sopauth.auth.RefreshToken; -import com.gitee.sop.sopauth.auth.exception.LoginErrorException; -import com.gitee.sop.sopauth.entity.UserInfo; -import com.gitee.sop.sopauth.mapper.UserInfoMapper; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import javax.servlet.http.HttpServletRequest; -import java.util.concurrent.TimeUnit; - -/** - * oauth2管理,默认谷歌缓存实现,跟redis实现只能用一个。 - * 这里为了演示,使用本地缓存,正式环境请使用redis保存 - * @see OAuth2ManagerRedis OAuth2ManagerRedis - * @author 六如 - * - */ -@Service -public class OAuth2ManagerCache implements OAuth2Manager { - /** - * 应用授权的app_auth_code唯一的;app_auth_code使用一次后失效,一天(从生成app_auth_code开始的24小时)未被使用自动过期; - * app_auth_token永久有效。 - */ - private int codeTimeoutSeconds = OAuth2Config.getInstance().getCodeTimeoutSeconds(); - /** - * accessToken过期时间 - * -1 永久有效 - */ - private int accessTokenTimeoutSeconds = OAuth2Config.getInstance().getAccessTokenExpiresIn(); - - private int refreshTokenTimeoutSeconds = OAuth2Config.getInstance().getRefreshTokenExpiresIn(); - - private LoadingCache codeCache = buildCache(codeTimeoutSeconds); - private LoadingCache accessTokenCache = buildCache(accessTokenTimeoutSeconds); - private LoadingCache refreshTokenCache = buildCache(refreshTokenTimeoutSeconds); - - @Autowired - private UserInfoMapper userInfoMapper; - - - private static LoadingCache buildCache(int timeout) { - CacheBuilder cacheBuilder = CacheBuilder.newBuilder(); - if (timeout > 0) { - cacheBuilder.expireAfterAccess(timeout, TimeUnit.SECONDS); - } - return cacheBuilder - .build(new CacheLoader() { - @Override - public T load(String key) throws Exception { - return null; - } - }); - } - - @Override - public void addAuthCode(String authCode, OpenUser authUser) { - codeCache.put(authCode, authUser); - } - - - @Override - public void addAccessToken(String accessToken, String refreshToken, OpenUser authUser) { - accessTokenCache.put(accessToken, authUser); - refreshTokenCache.put(refreshToken, new RefreshToken(accessToken, authUser)); - } - - @Override - public void removeAccessToken(String accessToken) { - accessTokenCache.asMap().remove(accessToken); - } - - @Override - public void removeRefreshToken(String refreshToken) { - refreshTokenCache.asMap().remove(refreshToken); - } - - @Override - public RefreshToken getRefreshToken(String refreshToken) { - return refreshTokenCache.getIfPresent(refreshToken); - } - - @Override - public boolean checkAuthCode(String authCode) { - return codeCache.asMap().containsKey(authCode); - } - - @Override - public OpenUser getUserByAuthCode(String authCode) { - return codeCache.getIfPresent(authCode); - } - - @Override - public OpenUser getUserByAccessToken(String accessToken) { - return accessTokenCache.getIfPresent(accessToken); - } - - @Override - public OpenUser login(HttpServletRequest request) throws LoginErrorException { - // 这里应该先检查用户有没有登录,如果登录直接返回openUser - String username = request.getParameter("username"); - String password = request.getParameter("password"); - if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { - throw new LoginErrorException("用户名密码不能为空"); - } - - Query query = new Query(); - query.eq("username", username) - .eq("password", password); - UserInfo userInfo = userInfoMapper.getByQuery(query); - - if(userInfo == null) { - throw new LoginErrorException("用户名密码不正确"); - } - - return userInfo; - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerRedis.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerRedis.java deleted file mode 100644 index 601acd74..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerRedis.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.gitee.sop.sopauth.auth.impl; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.gitee.fastmybatis.core.query.Query; -import com.gitee.sop.sopauth.auth.OAuth2Config; -import com.gitee.sop.sopauth.auth.OAuth2Manager; -import com.gitee.sop.sopauth.auth.OpenUser; -import com.gitee.sop.sopauth.auth.RefreshToken; -import com.gitee.sop.sopauth.auth.exception.LoginErrorException; -import com.gitee.sop.sopauth.entity.UserInfo; -import com.gitee.sop.sopauth.mapper.UserInfoMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.util.StringUtils; - -import javax.servlet.http.HttpServletRequest; -import java.util.concurrent.TimeUnit; - -/** - * - * oauth2管理redis实现,这个类跟OAuth2ManagerCache类只能用一个, - * 如果要用这个类, - * 1、注释掉OAuth2ManagerCache的@Service。 - * 2、在properties中配置redis - * 3、启用这个类的@Service - * - * @author 六如 - */ -//@Service -public class OAuth2ManagerRedis implements OAuth2Manager { - - private static String CODE_PREFIX = "com.gitee.sop.oauth2_code:"; - private static String ACCESS_TOKEN_PREFIX = "com.gitee.sop.oauth2_access_token:"; - private static String REFRESH_TOKEN_PREFIX = "com.gitee.sop.oauth2_refresh_token:"; - - @Autowired - private StringRedisTemplate redisTemplate; - - @Autowired - private UserInfoMapper userInfoMapper; - - public static String getCodeKey(String code) { - return CODE_PREFIX + code; - } - - public static String getAccessTokenKey(String accessToken) { - return ACCESS_TOKEN_PREFIX + accessToken; - } - - public static String getRefreshTokenKey(String refreshToken) { - return REFRESH_TOKEN_PREFIX + refreshToken; - } - - @Override - public void addAuthCode(String authCode, OpenUser authUser) { - long codeTimeoutSeconds = OAuth2Config.getInstance().getCodeTimeoutSeconds(); - redisTemplate.opsForValue().set(getCodeKey(authCode), - JSON.toJSONString(authUser), - codeTimeoutSeconds, - TimeUnit.SECONDS); - } - - - @Override - public void addAccessToken(String accessToken, String refreshToken, OpenUser authUser) { - // 存accessToken - long expiresIn = OAuth2Config.getInstance().getAccessTokenExpiresIn(); - long reExpiresIn = OAuth2Config.getInstance().getRefreshTokenExpiresIn(); - if (expiresIn > 0) { - redisTemplate.opsForValue().set(getAccessTokenKey(accessToken), JSON.toJSONString(authUser), expiresIn, TimeUnit.SECONDS); - } else { - redisTemplate.opsForValue().set(getAccessTokenKey(accessToken), JSON.toJSONString(authUser)); - } - // 存refreshToken - if (reExpiresIn > 0) { - redisTemplate.opsForValue().set( - getRefreshTokenKey(refreshToken), - JSON.toJSONString(new RefreshToken(accessToken, authUser)), - // refreshToken过期时间 - reExpiresIn, - TimeUnit.SECONDS); - } else { - redisTemplate.opsForValue().set( - getRefreshTokenKey(refreshToken), - JSON.toJSONString(new RefreshToken(accessToken, authUser))); - } - - } - - @Override - public void removeAccessToken(String accessToken) { - String accessTokenKey = getAccessTokenKey(accessToken); - int afterRefreshExpiresIn = OAuth2Config.getInstance().getAfterRefreshExpiresIn(); - // 刷新令牌后,保证老的app_auth_token从刷新开始10分钟内可继续使用 - redisTemplate.expire(accessTokenKey, afterRefreshExpiresIn, TimeUnit.SECONDS); - } - - @Override - public void removeRefreshToken(String refreshToken) { - redisTemplate.delete(getRefreshTokenKey(refreshToken)); - } - - @Override - public RefreshToken getRefreshToken(String refreshToken) { - String json = redisTemplate.opsForValue().get(getRefreshTokenKey(refreshToken)); - if(StringUtils.isEmpty(json)) { - return null; - } - JSONObject jsonObj = JSON.parseObject(json); - - String userJson = jsonObj.getString("openUser"); - UserInfo user = JSON.parseObject(userJson, UserInfo.class); - String accessToken = jsonObj.getString("accessToken"); - - return new RefreshToken(accessToken, user); - } - - @Override - public boolean checkAuthCode(String authCode) { - if(StringUtils.isEmpty(authCode)) { - return false; - } - return redisTemplate.hasKey(getCodeKey(authCode)); - } - - @Override - public OpenUser getUserByAuthCode(String authCode) { - String json = redisTemplate.opsForValue().get(getCodeKey(authCode)); - if(StringUtils.isEmpty(json)) { - return null; - } - return JSON.parseObject(json, UserInfo.class); - } - - @Override - public OpenUser getUserByAccessToken(String accessToken) { - String json = redisTemplate.opsForValue().get(getAccessTokenKey(accessToken)); - if(StringUtils.isEmpty(json)) { - return null; - } - return JSON.parseObject(json, UserInfo.class); - } - - - @Override - public OpenUser login(HttpServletRequest request) throws LoginErrorException { - // 这里应该先检查用户有没有登录,如果登录直接返回openUser - - String username = request.getParameter("username"); - String password = request.getParameter("password"); - if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { - throw new LoginErrorException("用户名密码不能为空"); - } - - Query query = new Query(); - query.eq("username", username) - .eq("password", password); - UserInfo userInfo = userInfoMapper.getByQuery(query); - - if(userInfo == null) { - throw new LoginErrorException("用户名密码不正确"); - } - - return userInfo; - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ServiceImpl.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ServiceImpl.java deleted file mode 100644 index c68f171a..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ServiceImpl.java +++ /dev/null @@ -1,262 +0,0 @@ -package com.gitee.sop.sopauth.auth.impl; - -import com.gitee.sop.sopauth.auth.AppIdManager; -import com.gitee.sop.sopauth.auth.FetchTokenParam; -import com.gitee.sop.sopauth.auth.FetchTokenResult; -import com.gitee.sop.sopauth.auth.OAuth2Config; -import com.gitee.sop.sopauth.auth.OAuth2Manager; -import com.gitee.sop.sopauth.auth.OAuth2Service; -import com.gitee.sop.sopauth.auth.OpenUser; -import com.gitee.sop.sopauth.auth.RefreshToken; -import com.gitee.sop.sopauth.auth.TokenPair; -import com.gitee.sop.sopauth.auth.exception.LoginErrorException; -import lombok.extern.slf4j.Slf4j; -import org.apache.oltu.oauth2.as.issuer.MD5Generator; -import org.apache.oltu.oauth2.as.issuer.OAuthIssuer; -import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl; -import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest; -import org.apache.oltu.oauth2.as.response.OAuthASResponse; -import org.apache.oltu.oauth2.common.OAuth; -import org.apache.oltu.oauth2.common.error.OAuthError; -import org.apache.oltu.oauth2.common.exception.OAuthProblemException; -import org.apache.oltu.oauth2.common.exception.OAuthSystemException; -import org.apache.oltu.oauth2.common.message.OAuthResponse; -import org.apache.oltu.oauth2.common.message.types.GrantType; -import org.apache.oltu.oauth2.common.message.types.ResponseType; -import org.apache.oltu.oauth2.common.utils.OAuthUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.net.URISyntaxException; - -/** - * oauth2服务端默认实现 - * - * @author 六如 - */ -@Service -@Slf4j -public class OAuth2ServiceImpl implements OAuth2Service { - - - private static final String TOKEN_TYPE = "Bearer"; - public static final String APP_ID_NAME = "app_id"; - - private OAuthIssuer oauthIssuer = new OAuthIssuerImpl(new MD5Generator()); - - @Autowired - private OAuth2Manager oauth2Manager; - - @Autowired - private AppIdManager appIdManager; - - @Override - public OAuthResponse authorize(HttpServletRequest request, HttpServletResponse resp, OAuth2Config authConfig) - throws URISyntaxException, OAuthSystemException { - try { - // 构建OAuth 授权请求 - OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request); - String clientId = oauthRequest.getClientId(); - // 检查传入的客户端id是否正确 - if (!appIdManager.isValidAppId(clientId)) { - return OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST) - .setError(OAuthError.TokenResponse.INVALID_CLIENT) - .setErrorDescription(OAuthError.TokenResponse.INVALID_CLIENT).buildJSONMessage(); - } - - // 如果用户没有登录,跳转到登陆页面 - OpenUser user = null; - try { - user = oauth2Manager.login(request); - } catch (LoginErrorException e) { - log.error(e.getMessage(), e); - request.setAttribute("error", e.getMessage()); - try { - request.getRequestDispatcher(authConfig.getOauth2LoginUri()).forward(request, resp); - return null; - } catch (Exception e1) { - log.error(e1.getMessage(), e1); - throw new RuntimeException(e1); - } - } - - // 生成授权码 - String authorizationCode = null; - // responseType目前仅支持CODE,另外还有TOKEN - String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE); - if (responseType.equals(ResponseType.CODE.toString())) { - OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator()); - authorizationCode = oauthIssuerImpl.authorizationCode(); - oauth2Manager.addAuthCode(authorizationCode, user); - } - // 进行OAuth响应构建 - OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse.authorizationResponse(request, - HttpServletResponse.SC_FOUND); - // 设置授权码 - builder.setCode(authorizationCode); - builder.setParam(APP_ID_NAME, clientId); - // 得到到客户端重定向地址 - String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI); - - // 构建响应 - return builder.location(redirectURI).buildQueryMessage(); - } catch (OAuthProblemException e) { - // 出错处理 - String redirectUri = e.getRedirectUri(); - if (OAuthUtils.isEmpty(redirectUri)) { - // 告诉客户端没有传入redirectUri直接报错 - String error = "OAuth redirectUri needs to be provided by client!!!"; - return OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND) - .error(OAuthProblemException.error(error)).location(redirectUri).buildQueryMessage(); - } else { - // 返回错误消息(如?error=) - return OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND).error(e).location(redirectUri) - .buildQueryMessage(); - } - - } - } - - - @Override - public FetchTokenResult accessToken(FetchTokenParam fetchTokenParam, OAuth2Config authConfig) { - try { - // 这里需要检查client_id和client_secret,但是已经通过网关接口访问进来了 - // 表示client_id和client_secret是合法的 - - // 检查验证类型,如果是第一次进来用code换取accessToken - String grant_type = fetchTokenParam.getGrant_type(); - if (GrantType.AUTHORIZATION_CODE.toString().equals(grant_type)) { - String authCode = fetchTokenParam.getCode(); - if (!oauth2Manager.checkAuthCode(authCode)) { - FetchTokenResult errorResult = new FetchTokenResult(); - errorResult.setError(OAuthError.CodeResponse.INVALID_REQUEST); - errorResult.setError_description("invalid request"); - return errorResult; - } - // 生成Access Token - OpenUser user = oauth2Manager.getUserByAuthCode(authCode); - if (user == null) { - throw OAuthProblemException.error("Can not found user by code."); - } - - TokenPair tokenPair = this.createNewToken(user); - - oauth2Manager.addAccessToken(tokenPair.getAccessToken(), tokenPair.getRefreshToken(), user); - - // 生成OAuth响应 - return this.buildTokenResult(tokenPair, user); - } else if (GrantType.REFRESH_TOKEN.toString().equals(grant_type)) { - // 用refreshToken来刷新accessToken - String refreshToken = fetchTokenParam.getRefresh_token(); - if (StringUtils.isEmpty(refreshToken)) { - FetchTokenResult errorResult = new FetchTokenResult(); - errorResult.setError(OAuthError.ResourceResponse.EXPIRED_TOKEN); - errorResult.setError_description("expired token"); - return errorResult; - } - - RefreshToken refreshTokenObj = oauth2Manager.getRefreshToken(refreshToken); - if (refreshTokenObj == null) { - FetchTokenResult errorResult = new FetchTokenResult(); - errorResult.setError(OAuthError.ResourceResponse.INVALID_TOKEN); - errorResult.setError_description("invalid token"); - return errorResult; - } - - OpenUser user = refreshTokenObj.getOpenUser(); - // 老的token对 - TokenPair oldTokenPair = new TokenPair(); - oldTokenPair.setAccessToken(refreshTokenObj.getAccessToken()); - oldTokenPair.setRefreshToken(refreshToken); - // 创建一对新的accessToken和refreshToken - TokenPair newTokenPair = this.createRefreshToken(user, oldTokenPair); - - this.afterRefreshToken(oldTokenPair, newTokenPair, user); - - // 返回新的accessToken和refreshToken - return this.buildTokenResult(newTokenPair, user); - } else { - FetchTokenResult errorResult = new FetchTokenResult(); - errorResult.setError(OAuthError.TokenResponse.INVALID_GRANT); - errorResult.setError_description("invalid grant"); - return errorResult; - } - - } catch (OAuthProblemException e) { - log.error(e.getMessage(), e); - FetchTokenResult errorResult = new FetchTokenResult(); - errorResult.setError(e.getMessage()); - errorResult.setError_description("invalid grant"); - return errorResult; - } - } - - /** - * 刷新token后续操作 - * - * @param oldTokenPair 老的token - * @param newTokenPair 新的token - * @param user 用户 - */ - protected void afterRefreshToken(TokenPair oldTokenPair, TokenPair newTokenPair, OpenUser user) { - // 保存token - oauth2Manager.addAccessToken(newTokenPair.getAccessToken(), newTokenPair.getRefreshToken(), user); - - // 删除老的accessToken - oauth2Manager.removeAccessToken(oldTokenPair.getAccessToken()); - // 删除老的refreshToken - oauth2Manager.removeRefreshToken(oldTokenPair.getRefreshToken()); - } - - /** - * 创建新的token - * - * @param user - * @return 返回新token - */ - protected TokenPair createNewToken(OpenUser user) { - return this.createDefaultTokenPair(); - } - - /** - * 返回刷新后token - * - * @param user 用户 - * @param oldTokenPair 旧的token - * @return 返回新的token - */ - protected TokenPair createRefreshToken(OpenUser user, TokenPair oldTokenPair) { - return this.createDefaultTokenPair(); - } - - private TokenPair createDefaultTokenPair() { - TokenPair tokenPair = new TokenPair(); - try { - String accessToken = oauthIssuer.accessToken(); - String refreshToken = oauthIssuer.refreshToken(); - - tokenPair.setAccessToken(accessToken); - tokenPair.setRefreshToken(refreshToken); - - return tokenPair; - } catch (OAuthSystemException e) { - throw new RuntimeException(e); - } - } - - private FetchTokenResult buildTokenResult(TokenPair tokenPair, OpenUser user) { - OAuth2Config auth2Config = OAuth2Config.getInstance(); - FetchTokenResult fetchTokenResult = new FetchTokenResult(); - fetchTokenResult.setApp_auth_token(tokenPair.getAccessToken()); - fetchTokenResult.setApp_refresh_token(tokenPair.getRefreshToken()); - fetchTokenResult.setUser_id(user.getUserId()); - fetchTokenResult.setExpires_in(auth2Config.getAccessTokenExpiresIn()); - fetchTokenResult.setRe_expires_in(auth2Config.getRefreshTokenExpiresIn()); - return fetchTokenResult; - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/config/OpenServiceConfig.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/config/OpenServiceConfig.java deleted file mode 100644 index 4d274491..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/config/OpenServiceConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.gitee.sop.sopauth.config; - -import com.gitee.sop.servercommon.bean.ServiceConfig; -import com.gitee.sop.servercommon.configuration.AlipayServiceConfiguration; -import org.springframework.context.annotation.Configuration; - -/** - * 使用支付宝开放平台功能 - * - * @author 六如 - */ -@Configuration -public class OpenServiceConfig extends AlipayServiceConfiguration { - - - static { - ServiceConfig.getInstance().getI18nModules().add("i18n/isp/goods_error"); - } - -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/CallbackController.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/CallbackController.java deleted file mode 100644 index 4b7f7d8d..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/CallbackController.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.gitee.sop.sopauth.controller; - -import com.gitee.sop.sdk.client.OpenClient; -import com.gitee.sop.sdk.model.OpenAuthTokenAppModel; -import com.gitee.sop.sdk.request.OpenAuthTokenAppRequest; -import com.gitee.sop.sdk.response.OpenAuthTokenAppResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author 六如 - */ -@Controller -@Slf4j -public class CallbackController { - String url = "http://localhost:8081/api"; - String appId = "2019032617262200001"; - // 平台提供的私钥 - String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ="; - - OpenClient openClient = new OpenClient(url, appId, privateKey); - - /** - * 模拟开发者回调,这里需要开发者自己实现.通过code换取token - * @return - */ - @GetMapping("oauth2callback") - @ResponseBody - public OpenAuthTokenAppResponse callback(HttpServletRequest servletRequest, HttpServletResponse servletResponse) { - String app_id = servletRequest.getParameter("app_id"); - String code = servletRequest.getParameter("code"); - log.info("app_id:{}, code:{}", app_id, code); - - OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest(); - OpenAuthTokenAppModel model = new OpenAuthTokenAppModel(); - model.setCode(code); - model.setGrant_type("authorization_code"); - request.setBizModel(model); - - // 根据code获取token - OpenAuthTokenAppResponse response = openClient.execute(request); - if (response.isSuccess()) { - // 成功拿到token,开发者在这里保存token信息 - // 后续使用token进行接口访问 - log.info("授权成功,body:{}", response.getBody()); - } - return response; - } -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/OAuth2Controller.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/OAuth2Controller.java deleted file mode 100644 index 43d7bea0..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/OAuth2Controller.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.gitee.sop.sopauth.controller; - -import com.gitee.sop.servercommon.annotation.Open; -import com.gitee.sop.sopauth.auth.FetchTokenParam; -import com.gitee.sop.sopauth.auth.FetchTokenResult; -import com.gitee.sop.sopauth.auth.OAuth2Config; -import com.gitee.sop.sopauth.auth.OAuth2Service; -import org.apache.oltu.oauth2.common.exception.OAuthSystemException; -import org.apache.oltu.oauth2.common.message.OAuthResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.net.URI; -import java.net.URISyntaxException; - -/** - * 授权认证 - * - * @author 六如 - */ -@Controller -@RequestMapping("oauth2") -public class OAuth2Controller { - - @Autowired - private OAuth2Service oAuth2Service; - - // 第一步:授权URL拼装 - // http://localhost:8087/oauth2/appToAppAuth?app_id=2019032617262200001&redirect_uri=http%3a%2f%2flocalhost%3a8087%2foauth2callback - @GetMapping("appToAppAuth") - public String appToAppAuth(HttpServletRequest request, ModelMap modelMap) { - String app_id = request.getParameter("app_id"); - String redirect_uri = request.getParameter("redirect_uri"); - modelMap.put("response_type", "code"); - modelMap.put("client_id", app_id); - modelMap.put("redirect_uri", redirect_uri); - return "oauth2login"; - } - - /** - * 第二步:点击登录,到这里拿code - * oauth2认证获取code - * @param request - * @param resp - * @return 返回code - * @throws URISyntaxException - * @throws OAuthSystemException - */ - @RequestMapping("authorize") - public Object authorize(HttpServletRequest request, HttpServletResponse resp) throws URISyntaxException, OAuthSystemException { - OAuthResponse response = oAuth2Service.authorize(request, resp, OAuth2Config.getInstance()); - if(response == null) { - return null; - } - HttpHeaders headers = new HttpHeaders(); - headers.setLocation(new URI(response.getLocationUri())); - return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus())); - } - - - /** - * 第三步,通过code获取token - * 或者,通过refresh_token换取token - * @param param - * @return - */ - @Open("open.auth.token.app") - @RequestMapping("fetchToken") - @ResponseBody - public FetchTokenResult fetchToken(FetchTokenParam param) { - FetchTokenResult fetchTokenResult = oAuth2Service.accessToken(param, OAuth2Config.getInstance()); - return fetchTokenResult; - } - - @RequestMapping("login") - public String oauth2login() { - return "oauth2login"; - } -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/IsvInfo.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/IsvInfo.java deleted file mode 100644 index 4c5a1854..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/IsvInfo.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gitee.sop.sopauth.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; - - -/** - * 表名:isv_info - * 备注:isv信息表 - * - * @author 六如 - */ -@Table(name = "isv_info",pk = @Pk(name = "id")) -@Data -public class IsvInfo { - /** 数据库字段:id */ - private Long id; - - /** appKey, 数据库字段:app_key */ - private String appKey; - - /** 1启用,2禁用, 数据库字段:status */ - private Byte status; -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/UserInfo.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/UserInfo.java deleted file mode 100644 index 0892322b..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/UserInfo.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gitee.sop.sopauth.entity; - -import com.alibaba.fastjson.annotation.JSONField; -import com.gitee.sop.sopauth.auth.OpenUser; -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:user_info - * 备注:用户信息表 - * - * @author 六如 - */ -@Table(name = "user_info",pk = @Pk(name = "id")) -@Data -public class UserInfo implements OpenUser { - /** 数据库字段:id */ - private Long id; - - /** 用户名, 数据库字段:username */ - private String username; - - /** 密码, 数据库字段:password */ - @JSONField(serialize = false) - private String password; - - /** 昵称, 数据库字段:nickname */ - private String nickname; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; - - @Override - public String getUserId() { - return String.valueOf(id); - } -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/IsvInfoMapper.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/IsvInfoMapper.java deleted file mode 100644 index 6f57b2a4..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/IsvInfoMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.sopauth.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.sopauth.entity.IsvInfo; - - -/** - * @author 六如 - */ -public interface IsvInfoMapper extends CrudMapper { -} diff --git a/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/UserInfoMapper.java b/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/UserInfoMapper.java deleted file mode 100644 index e47ce3f7..00000000 --- a/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/UserInfoMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.sopauth.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.sopauth.entity.UserInfo; - - -/** - * @author 六如 - */ -public interface UserInfoMapper extends CrudMapper { -} diff --git a/sop-auth/src/main/resources/application-dev.properties b/sop-auth/src/main/resources/application-dev.properties deleted file mode 100644 index 40f34a1b..00000000 --- a/sop-auth/src/main/resources/application-dev.properties +++ /dev/null @@ -1,26 +0,0 @@ -server.port=8087 -spring.application.name=sop-auth - -# ------- 需要改的配置 ------- -# mysql数据库账号 -mysql.host=localhost:3306 -mysql.username=root -mysql.password=root - -# nacos地址 -register.url=127.0.0.1:8848 - -# ------- 需要改的配置end ------- - -# nacos cloud配置 -spring.cloud.nacos.discovery.server-addr=${register.url} - -# 数据库配置 -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai -spring.datasource.username=${mysql.username} -spring.datasource.password=${mysql.password} - -spring.thymeleaf.cache=false - -logging.level.com.gitee=debug diff --git a/sop-auth/src/main/resources/application.properties b/sop-auth/src/main/resources/application.properties deleted file mode 100644 index 257b3064..00000000 --- a/sop-auth/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.profiles.active=dev \ No newline at end of file diff --git a/sop-auth/src/main/resources/templates/oauth2login.html b/sop-auth/src/main/resources/templates/oauth2login.html deleted file mode 100644 index 5c089835..00000000 --- a/sop-auth/src/main/resources/templates/oauth2login.html +++ /dev/null @@ -1,19 +0,0 @@ - - - -用户登录 - - -
-
- - - - 用户名:
- 密码:
- -
- - \ No newline at end of file diff --git a/sop-auth/src/test/java/com/gitee/sop/sopauth/RefreshTokenTest.java b/sop-auth/src/test/java/com/gitee/sop/sopauth/RefreshTokenTest.java deleted file mode 100644 index 4e461ca3..00000000 --- a/sop-auth/src/test/java/com/gitee/sop/sopauth/RefreshTokenTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.sop.sopauth; - -import com.gitee.sop.sdk.client.OpenClient; -import com.gitee.sop.sdk.model.OpenAuthTokenAppModel; -import com.gitee.sop.sdk.request.OpenAuthTokenAppRequest; -import com.gitee.sop.sdk.response.OpenAuthTokenAppResponse; -import junit.framework.TestCase; -import lombok.extern.slf4j.Slf4j; - -/** - * @author 六如 - */ -@Slf4j -public class RefreshTokenTest extends TestCase { - String url = "http://localhost:8081/api"; - String appId = "2019032617262200001"; - // 平台提供的私钥 - String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ="; - - OpenClient openClient = new OpenClient(url, appId, privateKey); - - /** - * 根据refreshToken换取token - */ - public void testRefreshToken() { - OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest(); - OpenAuthTokenAppModel model = new OpenAuthTokenAppModel(); - model.setGrant_type("refresh_token"); - model.setRefresh_token("c9e4003c06fe59066eed73d64ea074ca"); - request.setBizModel(model); - - OpenAuthTokenAppResponse response = openClient.execute(request); - if (response.isSuccess()) { - // 成功拿到token,开发者在这里保存token信息 - // 后续使用token进行接口访问 - log.info("换取token成功,body:{}", response.getBody()); - } - } - -} diff --git a/sop-auth/src/test/java/com/gitee/sop/sopauth/SopAuthApplicationTests.java b/sop-auth/src/test/java/com/gitee/sop/sopauth/SopAuthApplicationTests.java deleted file mode 100644 index b5902b77..00000000 --- a/sop-auth/src/test/java/com/gitee/sop/sopauth/SopAuthApplicationTests.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.gitee.sop.sopauth; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class SopAuthApplicationTests { - - @Test - public void contextLoads() { - } - -} diff --git a/sop-common/pom.xml b/sop-common/pom.xml deleted file mode 100644 index b4a41ac3..00000000 --- a/sop-common/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - 4.0.0 - com.gitee.sop - sop-common - 5.0.0-SNAPSHOT - - - 1.8 - - - - - - org.projectlombok - lombok - 1.18.34 - provided - - - org.springframework - spring-context - 6.1.11 - true - - - diff --git a/sop-common/readme.md b/sop-common/readme.md deleted file mode 100644 index 497bdfd7..00000000 --- a/sop-common/readme.md +++ /dev/null @@ -1,12 +0,0 @@ -# sop-common - -- sop-bridge-nacos:注册中心桥接器,接入nacos -- sop-bridge-eureka:注册中心桥接器,接入eureka -- sop-gateway-common:提供给网关使用 -- sop-service-common:提供给微服务端使用,需要打成jar - -正式开发请将这些模块上传的maven私服 - -- 打包成jar:`mvn clean package` -- 上传到本机仓库:`mvn clean install` -- 上传到maven私服:`mvn clean deploy` \ No newline at end of file diff --git a/sop-common/sop-bridge-eureka/.gitignore b/sop-common/sop-bridge-eureka/.gitignore deleted file mode 100644 index c456c4a3..00000000 --- a/sop-common/sop-bridge-eureka/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -/build/ diff --git a/sop-common/sop-bridge-eureka/pom.xml b/sop-common/sop-bridge-eureka/pom.xml deleted file mode 100644 index 9dc02f3c..00000000 --- a/sop-common/sop-bridge-eureka/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - com.gitee.sop - sop-common - 5.0.0-SNAPSHOT - ../pom.xml - - 4.0.0 - sop-bridge-eureka - - - - com.gitee.sop - sop-gateway-common - ${project.version} - - - - org.springframework.cloud - spring-cloud-starter-netflix-eureka-client - - - - org.springframework.cloud - spring-cloud-starter-netflix-ribbon - 2.2.10.RELEASE - - - - diff --git a/sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java b/sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java deleted file mode 100644 index e0cf816a..00000000 --- a/sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.gitee.sop.bridge; - -import com.gitee.sop.bridge.route.EurekaRegistryListener; -import com.gitee.sop.gatewaycommon.route.RegistryListener; -import org.springframework.cloud.netflix.ribbon.ServerIntrospector; -import org.springframework.cloud.netflix.ribbon.eureka.EurekaServerIntrospector; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @author 六如 - */ -@Configuration -public class SopRegisterAutoConfiguration { - - /** - * 负责获取eureka实例的metadata - * @return - */ - @Bean - ServerIntrospector eurekaServerIntrospector() { - return new EurekaServerIntrospector(); - } - - @Bean - RegistryListener registryListenerEureka() { - return new EurekaRegistryListener(); - } - -} - diff --git a/sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/route/EurekaRegistryListener.java b/sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/route/EurekaRegistryListener.java deleted file mode 100644 index 7d406759..00000000 --- a/sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/route/EurekaRegistryListener.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.gitee.sop.bridge.route; - -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.route.BaseRegistryListener; -import com.gitee.sop.gatewaycommon.route.RegistryEvent; -import com.gitee.sop.gatewaycommon.route.ServiceHolder; -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.netflix.eureka.CloudEurekaClient; -import org.springframework.context.ApplicationEvent; - -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * 加载服务路由,eureka实现 - * - * @author 六如 - */ -public class EurekaRegistryListener extends BaseRegistryListener { - - private Set cacheServices = new HashSet<>(); - - @Autowired(required = false) - private List registryEventList; - - @Override - public void onEvent(ApplicationEvent applicationEvent) { - Object source = applicationEvent.getSource(); - CloudEurekaClient cloudEurekaClient = (CloudEurekaClient) source; - Applications applications = cloudEurekaClient.getApplications(); - List registeredApplications = applications.getRegisteredApplications(); - - List serviceList = registeredApplications - .stream() - .filter(application -> CollectionUtils.isNotEmpty(application.getInstances())) - .map(Application::getInstances) - .map(instanceInfos -> { - // 根据更新时间倒叙 - instanceInfos.sort(Comparator.comparing(InstanceInfo::getLastUpdatedTimestamp).reversed()); - // 获取最新的个服务实例,说明这个服务实例刚刚重启过 - return instanceInfos.get(0); - }) - .map(instanceInfo -> new ServiceHolder(instanceInfo.getAppName(), instanceInfo.getLastUpdatedTimestamp())) - .filter(this::canOperator) - .collect(Collectors.toList()); - - final Set currentServices = new HashSet<>(serviceList); - // 删除现有的,剩下的就是新服务 - currentServices.removeAll(cacheServices); - // 如果有新的服务注册进来 - if (currentServices.size() > 0) { - List newApplications = registeredApplications.stream() - .filter(application -> - containsService(currentServices, application.getName())) - .collect(Collectors.toList()); - - this.doRegister(newApplications); - } - - Set removedServiceIdList = getRemovedServiceId(serviceList); - // 如果有服务下线 - this.doRemove(removedServiceIdList); - - cacheServices = new HashSet<>(serviceList); - } - - /** - * 获取已经下线的serviceId - * - * @param serviceList 最新的serviceId集合 - * @return 返回已下线的serviceId - */ - private Set getRemovedServiceId(List serviceList) { - Set cache = cacheServices.stream() - .map(ServiceHolder::getServiceId) - .collect(Collectors.toSet()); - - Set newList = serviceList.stream() - .map(ServiceHolder::getServiceId) - .collect(Collectors.toSet()); - - cache.removeAll(newList); - return cache; - } - - private static boolean containsService(Set currentServices, String serviceId) { - for (ServiceHolder currentService : currentServices) { - if (currentService.getServiceId().equalsIgnoreCase(serviceId)) { - return true; - } - } - return false; - } - - private void doRegister(List registeredApplications) { - registeredApplications.forEach(application -> { - List instances = application.getInstances(); - if (CollectionUtils.isNotEmpty(instances)) { - instances.sort(Comparator.comparing(InstanceInfo::getLastUpdatedTimestamp).reversed()); - InstanceInfo instanceInfo = instances.get(0); - InstanceDefinition instanceDefinition = new InstanceDefinition(); - instanceDefinition.setInstanceId(instanceInfo.getInstanceId()); - instanceDefinition.setServiceId(instanceInfo.getAppName()); - instanceDefinition.setIp(instanceInfo.getIPAddr()); - instanceDefinition.setPort(instanceInfo.getPort()); - instanceDefinition.setMetadata(instanceInfo.getMetadata()); - pullRoutes(instanceDefinition); - if (registryEventList != null) { - registryEventList.forEach(registryEvent -> registryEvent.onRegistry(instanceDefinition)); - } - } - }); - } - - private void doRemove(Set deletedServices) { - if (deletedServices == null) { - return; - } - deletedServices.forEach(serviceId -> { - this.removeRoutes(serviceId); - if (registryEventList != null) { - registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId)); - } - }); - } - -} diff --git a/sop-common/sop-bridge-nacos/.gitignore b/sop-common/sop-bridge-nacos/.gitignore deleted file mode 100644 index c456c4a3..00000000 --- a/sop-common/sop-bridge-nacos/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -/build/ diff --git a/sop-common/sop-bridge-nacos/pom.xml b/sop-common/sop-bridge-nacos/pom.xml deleted file mode 100644 index cf80c52e..00000000 --- a/sop-common/sop-bridge-nacos/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - com.gitee.sop - sop-common - 5.0.0-SNAPSHOT - ../pom.xml - - 4.0.0 - sop-bridge-nacos - - - - com.gitee.sop - sop-gateway-common - 5.0.0-SNAPSHOT - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - - - - org.projectlombok - lombok - provided - - - diff --git a/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java b/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java deleted file mode 100644 index 905904c9..00000000 --- a/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gitee.sop.bridge; - -import com.gitee.sop.bridge.route.NacosRegistryListener; -import com.gitee.sop.gatewaycommon.route.RegistryListener; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @author 六如 - */ -@Configuration -public class SopRegisterAutoConfiguration { - - /** - * 微服务路由加载 - */ - @Bean - RegistryListener registryListenerNacos() { - return new NacosRegistryListener(); - } -} diff --git a/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosRegistryListener.java b/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosRegistryListener.java deleted file mode 100644 index 7cb92e6a..00000000 --- a/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosRegistryListener.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.gitee.sop.bridge.route; - -import com.alibaba.cloud.nacos.NacosDiscoveryProperties; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ListView; -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.route.BaseRegistryListener; -import com.gitee.sop.gatewaycommon.route.RegistryEvent; -import com.gitee.sop.gatewaycommon.route.ServiceHolder; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.math.NumberUtils; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEvent; -import org.springframework.util.CollectionUtils; - -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * 加载服务路由,nacos实现 - * - * @author 六如 - */ -@Slf4j -public class NacosRegistryListener extends BaseRegistryListener { - - private volatile Set cacheServices = new HashSet<>(); - - @Value("${nacos.discovery.group:${spring.cloud.nacos.discovery.group:DEFAULT_GROUP}}") - private String nacosGroup; - - @Autowired - private NacosDiscoveryProperties nacosDiscoveryProperties; - - @Autowired - private ObjectProvider registryEventList; - - @Override - public synchronized void onEvent(ApplicationEvent applicationEvent) { - List serviceList = this.getServiceList(); - final Set currentServices = new HashSet<>(serviceList); - - // 删除现有的,剩下的就是新服务 - currentServices.removeAll(cacheServices); - // 如果有新的服务注册进来 - if (currentServices.size() > 0) { - currentServices.forEach(nacosServiceHolder -> { - Instance instance = nacosServiceHolder.getInstance(); - InstanceDefinition instanceDefinition = new InstanceDefinition(); - instanceDefinition.setInstanceId(instance.getInstanceId()); - instanceDefinition.setServiceId(nacosServiceHolder.getServiceId()); - instanceDefinition.setIp(instance.getIp()); - instanceDefinition.setPort(instance.getPort()); - instanceDefinition.setMetadata(instance.getMetadata()); - pullRoutes(instanceDefinition); - if (registryEventList != null) { - registryEventList.forEach(registryEvent -> registryEvent.onRegistry(instanceDefinition)); - } - }); - } - - // 如果有服务下线 - Set removedServiceIdList = getRemovedServiceId(serviceList); - // 移除 - this.doRemove(removedServiceIdList); - - cacheServices = new HashSet<>(serviceList); - } - - /** - * 获取建康的服务实例 - * - * @return 没有返回空的list - */ - private List getServiceList() { - NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); - ListView servicesOfServer = null; - try { - servicesOfServer = namingService.getServicesOfServer(1, Integer.MAX_VALUE, nacosGroup); - } catch (NacosException e) { - log.error("namingService.getServicesOfServer()错误", e); - } - if (servicesOfServer == null || CollectionUtils.isEmpty(servicesOfServer.getData())) { - return Collections.emptyList(); - } - return servicesOfServer - .getData() - .stream() - .map(serviceName -> { - List allInstances; - try { - // 获取服务实例 - allInstances = namingService.getAllInstances(serviceName, nacosGroup); - } catch (NacosException e) { - log.error("namingService.getAllInstances(serviceName)错误,serviceName:{}", serviceName, e); - return null; - } - if (CollectionUtils.isEmpty(allInstances)) { - return null; - } - return allInstances.stream() - // 只获取建康实例 - .filter(Instance::isHealthy) - .map(instance -> { - String startupTime = instance.getMetadata().get(SopConstants.METADATA_KEY_TIME_STARTUP); - if (startupTime == null) { - return null; - } - long time = NumberUtils.toLong(startupTime, 0); - return new NacosServiceHolder(serviceName, time, instance); - }) - .filter(Objects::nonNull) - .max(Comparator.comparing(ServiceHolder::getLastUpdatedTimestamp)) - .orElse(null); - - }) - .filter(Objects::nonNull) - .filter(this::canOperator) - .collect(Collectors.toList()); - } - - /** - * 获取已经下线的serviceId - * - * @param serviceList 最新的serviceId集合 - * @return 返回已下线的serviceId - */ - private Set getRemovedServiceId(List serviceList) { - Set cache = cacheServices.stream() - .map(NacosServiceHolder::getServiceId) - .collect(Collectors.toSet()); - - Set newList = serviceList.stream() - .map(NacosServiceHolder::getServiceId) - .collect(Collectors.toSet()); - - cache.removeAll(newList); - return cache; - } - - private void doRemove(Set deletedServices) { - if (deletedServices == null) { - return; - } - deletedServices.forEach(serviceId -> { - this.removeRoutes(serviceId); - if (registryEventList != null) { - registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId)); - } - }); - } - -} diff --git a/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosServiceHolder.java b/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosServiceHolder.java deleted file mode 100644 index 351ebbe2..00000000 --- a/sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosServiceHolder.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gitee.sop.bridge.route; - -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.gitee.sop.gatewaycommon.route.ServiceHolder; - -/** - * @author 六如 - */ -public class NacosServiceHolder extends ServiceHolder { - - private final Instance instance; - - public NacosServiceHolder(String serviceId, long lastUpdatedTimestamp, Instance instance) { - super(serviceId, lastUpdatedTimestamp); - this.instance = instance; - } - - public Instance getInstance() { - return instance; - } -} diff --git a/sop-common/sop-gateway-common/.gitignore b/sop-common/sop-gateway-common/.gitignore deleted file mode 100644 index c456c4a3..00000000 --- a/sop-common/sop-gateway-common/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -/build/ diff --git a/sop-common/sop-gateway-common/pom.xml b/sop-common/sop-gateway-common/pom.xml deleted file mode 100644 index 5c37928d..00000000 --- a/sop-common/sop-gateway-common/pom.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - com.gitee.sop - sop-common - 5.0.0-SNAPSHOT - ../pom.xml - - - 4.0.0 - sop-gateway-common - jar - - - 1.8 - - - - - - com.google.guava - guava - - - - org.apache.commons - commons-lang3 - - - commons-fileupload - commons-fileupload - ${commons-fileupload.version} - - - commons-codec - commons-codec - ${commons-codec.version} - - - commons-collections - commons-collections - - - - com.alibaba - fastjson - - - - org.springframework.cloud - spring-cloud-gateway-webflux - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - true - - - - org.springframework.cloud - spring-cloud-starter-gateway - true - - - org.springframework.boot - spring-boot-starter-webflux - true - - - - org.springframework.data - spring-data-redis - true - - - - - junit - junit - 4.12 - test - - - - org.projectlombok - lombok - provided - - - javax.servlet - javax.servlet-api - provided - - - org.springframework - spring-jdbc - - - org.springframework.cloud - spring-cloud-commons - - - org.springframework.cloud - spring-cloud-loadbalancer - - - org.slf4j - slf4j-api - - - org.springframework.cloud - spring-cloud-starter-netflix-ribbon - 2.2.10.RELEASE - - - - - diff --git a/sop-common/sop-gateway-common/readme.md b/sop-common/sop-gateway-common/readme.md deleted file mode 100644 index 3bcf9b86..00000000 --- a/sop-common/sop-gateway-common/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# sop-gateway-common - -网关通用组件,主要封装网关的一些功能。 diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java deleted file mode 100644 index bbe6df77..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java +++ /dev/null @@ -1,216 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.gitee.sop.gatewaycommon.gateway.result.BizResultHandler; -import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor; -import com.gitee.sop.gatewaycommon.limit.DefaultLimitManager; -import com.gitee.sop.gatewaycommon.limit.LimitManager; -import com.gitee.sop.gatewaycommon.loadbalancer.builder.AppIdGrayUserBuilder; -import com.gitee.sop.gatewaycommon.loadbalancer.builder.GrayUserBuilder; -import com.gitee.sop.gatewaycommon.loadbalancer.builder.IpGrayUserBuilder; -import com.gitee.sop.gatewaycommon.manager.DefaultEnvGrayManager; -import com.gitee.sop.gatewaycommon.manager.DefaultIPBlacklistManager; -import com.gitee.sop.gatewaycommon.manager.DefaultIsvRoutePermissionManager; -import com.gitee.sop.gatewaycommon.manager.DefaultLimitConfigManager; -import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager; -import com.gitee.sop.gatewaycommon.manager.DefaultServiceErrorManager; -import com.gitee.sop.gatewaycommon.manager.EnvGrayManager; -import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager; -import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager; -import com.gitee.sop.gatewaycommon.manager.LimitConfigManager; -import com.gitee.sop.gatewaycommon.manager.RouteConfigManager; -import com.gitee.sop.gatewaycommon.manager.ServiceErrorManager; -import com.gitee.sop.gatewaycommon.monitor.MonitorManager; -import com.gitee.sop.gatewaycommon.param.ParameterFormatter; -import com.gitee.sop.gatewaycommon.result.DataNameBuilder; -import com.gitee.sop.gatewaycommon.result.DefaultDataNameBuilder; -import com.gitee.sop.gatewaycommon.result.ResultAppender; -import com.gitee.sop.gatewaycommon.result.ResultExecutorForGateway; -import com.gitee.sop.gatewaycommon.secret.CacheIsvManager; -import com.gitee.sop.gatewaycommon.secret.IsvManager; -import com.gitee.sop.gatewaycommon.validate.ApiEncrypter; -import com.gitee.sop.gatewaycommon.validate.ApiValidator; -import com.gitee.sop.gatewaycommon.validate.Encrypter; -import com.gitee.sop.gatewaycommon.validate.Signer; -import com.gitee.sop.gatewaycommon.validate.TokenValidator; -import com.gitee.sop.gatewaycommon.validate.Validator; -import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner; -import lombok.Data; -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -/** - * @author 六如 - */ -@Data -public class ApiConfig { - - private static ApiConfig instance = new ApiConfig(); - - private ApiConfig() { - grayUserBuilders = new ArrayList<>(4); - grayUserBuilders.add(new AppIdGrayUserBuilder()); - grayUserBuilders.add(new IpGrayUserBuilder()); - } - - /** - * gateway合并结果处理 - */ - private ResultExecutorForGateway gatewayResultExecutor = new GatewayResultExecutor(); - - /** - * isv管理 - */ - private IsvManager isvManager = new CacheIsvManager(); - - /** - * 加密工具 - */ - private Encrypter encrypter = new ApiEncrypter(); - - /** - * 签名工具 - */ - private Signer signer = new AlipaySigner(); - - /** - * 验证 - */ - private Validator validator = new ApiValidator(); - - /** - * isv路由权限 - */ - private IsvRoutePermissionManager isvRoutePermissionManager = new DefaultIsvRoutePermissionManager(); - - /** - * 路由配置管理 - */ - private RouteConfigManager routeConfigManager = new DefaultRouteConfigManager(); - - /** - * 限流配置 - */ - private LimitConfigManager limitConfigManager = new DefaultLimitConfigManager(); - - /** - * IP黑名单 - */ - private IPBlacklistManager ipBlacklistManager = new DefaultIPBlacklistManager(); - - /** - * 限流管理 - */ - private LimitManager limitManager = new DefaultLimitManager(); - - /** - * 用户key管理 - */ - private EnvGrayManager userKeyManager = new DefaultEnvGrayManager(); - - /** - * 构建数据节点名称 - */ - private DataNameBuilder dataNameBuilder = new DefaultDataNameBuilder(); - - /** - * 追加结果 - */ - private ResultAppender resultAppender; - - /** - * 处理错误信息 - */ - private ServiceErrorManager serviceErrorManager = new DefaultServiceErrorManager(); - - private ParameterFormatter parameterFormatter; - - /** - * 校验token - */ - private TokenValidator tokenValidator = apiParam -> apiParam != null && StringUtils.isNotBlank(apiParam.fetchAccessToken()); - - /** - * 路由拦截器 - */ - private List routeInterceptors = new ArrayList<>(4); - - /** - * 监控管理 - */ - private MonitorManager monitorManager = new MonitorManager(); - - /** - * 业务返回参数处理 - */ - private BizResultHandler bizResultHandler = (serviceData, serviceObj, apiParam, request) -> { - serviceData.putAll(serviceObj); - }; - - // -------- fields --------- - - /** - * 错误模块 - */ - private List i18nModules = new ArrayList<>(); - - - /** - * 忽略验证,设置true,则所有接口不会进行签名校验 - */ - private boolean ignoreValidate; - - /** - * 是否对结果进行合并。
- * 默认情况下是否合并结果由微服务端决定,一旦指定该值,则由该值决定,不管微服务端如何设置。 - */ - private Boolean mergeResult; - - /** - * 超时时间 - */ - private int timeoutSeconds = 60 * 5; - - /** - * 是否开启限流功能 - */ - private boolean openLimit = true; - - /** - * 显示返回sign - */ - private boolean showReturnSign = true; - - /** - * 保存错误信息容器的容量 - */ - private int storeErrorCapacity = 20; - - private boolean useGateway; - - private List grayUserBuilders; - - public void addGrayUserBuilder(GrayUserBuilder grayUserBuilder) { - grayUserBuilders.add(grayUserBuilder); - grayUserBuilders.sort(Comparator.comparing(GrayUserBuilder::order)); - } - - public void addAppSecret(Map appSecretPair) { - for (Map.Entry entry : appSecretPair.entrySet()) { - this.isvManager.update(new IsvDefinition(entry.getKey(), entry.getValue())); - } - } - - public static ApiConfig getInstance() { - return instance; - } - - public static void setInstance(ApiConfig apiConfig) { - instance = apiConfig; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiContext.java deleted file mode 100644 index 5df1ec17..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiContext.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -/** - * 应用上下文,方便获取信息 - * - * @author 六如 - */ -public class ApiContext { - - - public static ApiConfig getApiConfig() { - return ApiConfig.getInstance(); - } - - public static void setApiConfig(ApiConfig apiConfig) { - ApiConfig.setInstance(apiConfig); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiParamAware.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiParamAware.java deleted file mode 100644 index 7d041cae..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiParamAware.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.gitee.sop.gatewaycommon.param.ApiParam; - -/** - * @author 六如 - */ -public interface ApiParamAware { - ApiParam getApiParam(T t); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseErrorLogController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseErrorLogController.java deleted file mode 100644 index f92a8213..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseErrorLogController.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.gitee.sop.gatewaycommon.manager.ServiceErrorManager; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.result.ApiResult; -import com.gitee.sop.gatewaycommon.result.JsonResult; -import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; - -import java.util.Collection; - -/** - * @author 六如 - */ -public abstract class BaseErrorLogController { - - TaobaoSigner signer = new TaobaoSigner(); - - @Value("${sop.secret}") - private String secret; - - protected abstract ApiParam getApiParam(T t); - - @GetMapping("/sop/listErrors") - public ApiResult listErrors(T request) { - try { - this.check(request); - ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); - Collection allErrors = serviceErrorManager.listAllErrors(); - JsonResult apiResult = new JsonResult(); - apiResult.setData(allErrors); - return apiResult; - } catch (Exception e) { - ApiResult apiResult = new ApiResult(); - apiResult.setCode("505050"); - apiResult.setMsg(e.getMessage()); - return apiResult; - } - } - - @GetMapping("/sop/clearErrors") - public ApiResult clearErrors(T request) { - try { - this.check(request); - ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); - serviceErrorManager.clear(); - return new ApiResult(); - } catch (Exception e) { - ApiResult apiResult = new ApiResult(); - apiResult.setCode("505050"); - apiResult.setMsg(e.getMessage()); - return apiResult; - } - } - - protected void check(T request) { - ApiParam apiParam = getApiParam(request); - boolean right = signer.checkSign(apiParam, secret); - if (!right) { - throw new RuntimeException("签名校验失败"); - } - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BeanInitializer.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BeanInitializer.java deleted file mode 100644 index 913dc942..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BeanInitializer.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.gitee.sop.gatewaycommon.manager.ChannelMsgProcessor; - -/** - * @author 六如 - */ -public interface BeanInitializer extends ChannelMsgProcessor { - /** - * 执行加载操作 - */ - void load(); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ChannelMsg.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ChannelMsg.java deleted file mode 100644 index 5e141ca3..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ChannelMsg.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.alibaba.fastjson.JSONObject; -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class ChannelMsg { - private String operation; - private JSONObject data; - - public T toObject(Class clazz) { - return data.toJavaObject(clazz); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ConfigLimitDto.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ConfigLimitDto.java deleted file mode 100644 index 3d195f41..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ConfigLimitDto.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.util.concurrent.RateLimiter; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; - -import java.util.Date; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * @author 六如 - */ -@Setter -@Getter -public class ConfigLimitDto { - - public static final byte LIMIT_STATUS_OPEN = 1; - public static final byte LIMIT_STATUS_CLOSE = 0; - - /** 数据库字段:id */ - private Long id; - - /** 路由id, 数据库字段:route_id */ - private String routeId; - - /** 数据库字段:app_key */ - private String appKey; - - /** 限流ip,多个用英文逗号隔开, 数据库字段:limit_ip */ - private String limitIp; - - /** 服务id, 数据库字段:service_id */ - private String serviceId; - - /** 限流策略,1:窗口策略,2:令牌桶策略, 数据库字段:limit_type */ - private Byte limitType; - - /** 每秒可处理请求数, 数据库字段:exec_count_per_second */ - private Integer execCountPerSecond; - - /** 限流过期时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效), 数据库字段:durationSeconds */ - private Integer durationSeconds; - - /** 返回的错误码, 数据库字段:limit_code */ - private String limitCode; - - /** 返回的错误信息, 数据库字段:limit_msg */ - private String limitMsg; - - /** 令牌桶容量, 数据库字段:token_bucket_count */ - private Integer tokenBucketCount; - - /** 限流开启状态,1:开启,0关闭, 数据库字段:limit_status */ - private Byte limitStatus; - - /** 顺序,值小的优先执行, 数据库字段:order_index */ - private Integer orderIndex; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; - - - /** - * 窗口计数器 - */ - private volatile LoadingCache counter; - - /** - * 获取持续时间,1秒内限制请求,则duration设置2 - * - * @return 返回缓存保存的值。 - */ - public int fetchDuration() { - Integer durationSeconds = this.durationSeconds; - if (durationSeconds == null || durationSeconds < 1) { - durationSeconds = 1; - } - // 1秒内限制请求,则duration设置2 - return durationSeconds + 1; - } - - public LoadingCache getCounter() { - if (counter == null) { - synchronized (this) { - if (counter == null) { - int duration = fetchDuration(); - counter = CacheBuilder.newBuilder() - .expireAfterWrite(duration, TimeUnit.SECONDS) - .build(new CacheLoader() { - @Override - public AtomicLong load(Long seconds) throws Exception { - return new AtomicLong(0); - } - }); - } - } - } - return counter; - } - - /** - * 令牌桶 - */ - @Getter(AccessLevel.PRIVATE) - @Setter(AccessLevel.PRIVATE) - private volatile RateLimiter rateLimiter; - - public synchronized void initRateLimiter() { - rateLimiter = RateLimiter.create(tokenBucketCount); - } - - /** - * 获取令牌桶 - * @return - */ - public RateLimiter fetchRateLimiter() { - if (rateLimiter == null) { - synchronized (this) { - if (rateLimiter == null) { - rateLimiter = RateLimiter.create(tokenBucketCount); - } - } - } - return rateLimiter; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ConfigLimitDto that = (ConfigLimitDto) o; - return id.equals(that.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } - - @Override - public String toString() { - return "ConfigLimitDto{" + - "id=" + id + - ", routeId='" + routeId + '\'' + - ", appKey='" + appKey + '\'' + - ", limitIp='" + limitIp + '\'' + - ", serviceId='" + serviceId + '\'' + - ", limitType=" + limitType + - ", execCountPerSecond=" + execCountPerSecond + - ", durationSeconds=" + durationSeconds + - ", limitCode='" + limitCode + '\'' + - ", limitMsg='" + limitMsg + '\'' + - ", tokenBucketCount=" + tokenBucketCount + - ", limitStatus=" + limitStatus + - ", orderIndex=" + orderIndex + - ", gmtCreate=" + gmtCreate + - ", gmtModified=" + gmtModified + - '}'; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/DefaultRouteInterceptorContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/DefaultRouteInterceptorContext.java deleted file mode 100644 index 1759d99b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/DefaultRouteInterceptorContext.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import org.springframework.cloud.client.ServiceInstance; - -/** - * @author 六如 - */ -public class DefaultRouteInterceptorContext implements RouteInterceptorContext { - - /** 请求参数 */ - private ApiParam apiParam; - /** 错误信息 */ - private String serviceErrorMsg; - /** 微服务返回状态 */ - private int responseStatus; - /** 开始时间 */ - private long beginTimeMillis; - /** 结束时间 */ - private long finishTimeMillis; - /** 请求上下文 */ - private Object requestContext; - /** 微服务返回结果 */ - private String serviceResult; - /** 请求包大小 */ - private long requestDataSize; - /** 返回内容大小 */ - private long responseDataSize; - /** 负载均衡选中的微服务 */ - private ServiceInstance serviceInstance; - - @Override - public ApiParam getApiParam() { - return apiParam; - } - - @Override - public String getServiceResult() { - return serviceResult; - } - - @Override - public int getResponseStatus() { - return responseStatus; - } - - @Override - public long getBeginTimeMillis() { - return beginTimeMillis; - } - - @Override - public long getFinishTimeMillis() { - return finishTimeMillis; - } - - @Override - public Object getRequestContext() { - return requestContext; - } - - public void setApiParam(ApiParam apiParam) { - this.apiParam = apiParam; - } - - public void setServiceResult(String serviceResult) { - this.serviceResult = serviceResult; - } - - public void setResponseStatus(int responseStatus) { - this.responseStatus = responseStatus; - } - - public void setBeginTimeMillis(long beginTimeMillis) { - this.beginTimeMillis = beginTimeMillis; - } - - public void setFinishTimeMillis(long finishTimeMillis) { - this.finishTimeMillis = finishTimeMillis; - } - - public void setRequestContext(Object requestContext) { - this.requestContext = requestContext; - } - - @Override - public String getServiceErrorMsg() { - return serviceErrorMsg; - } - - public void setServiceErrorMsg(String serviceErrorMsg) { - this.serviceErrorMsg = serviceErrorMsg; - } - - @Override - public long getRequestDataSize() { - return requestDataSize; - } - - public void setRequestDataSize(long requestDataSize) { - // spring cloud gateway get请求contentLength返回-1 - if (requestDataSize < 0) { - requestDataSize = 0; - } - this.requestDataSize = requestDataSize; - } - - @Override - public long getResponseDataSize() { - return responseDataSize; - } - - public void setResponseDataSize(long responseDataSize) { - this.responseDataSize = responseDataSize; - } - - @Override - public ServiceInstance getServiceInstance() { - return serviceInstance; - } - - public void setServiceInstance(ServiceInstance serviceInstance) { - this.serviceInstance = serviceInstance; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorDefinition.java deleted file mode 100644 index 40727692..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorDefinition.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class ErrorDefinition { - /** - * 接口名 - */ - private String name; - /** - * 版本号 - */ - private String version; - /** - * 服务名 - */ - private String serviceId; - /** - * 错误内容 - */ - private String errorMsg; - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorEntity.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorEntity.java deleted file mode 100644 index 8928ba25..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorEntity.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -import java.util.Objects; - -/** - * @author 六如 - */ -@Getter -@Setter -@ToString -public class ErrorEntity { - private String id; - private String name; - private String version; - private String serviceId; - private String errorMsg; - private long count; - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ErrorEntity that = (ErrorEntity) o; - return id.equals(that.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayFilterDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayFilterDefinition.java deleted file mode 100644 index 3d6f93cd..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayFilterDefinition.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * @author 六如 - */ -@Data -public class GatewayFilterDefinition { - /** Filter Name */ - private String name; - /** 对应的路由规则 */ - private Map args = new LinkedHashMap<>(); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayPredicateDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayPredicateDefinition.java deleted file mode 100644 index 67e4a005..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayPredicateDefinition.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; -import org.springframework.util.StringUtils; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * @author 六如 - */ -@Data -public class GatewayPredicateDefinition { - public static final String GEN_KEY = "_genkey_"; - /** 断言对应的Name */ - private String name; - /** 配置的断言规则 */ - private Map args = new LinkedHashMap<>(); - - public GatewayPredicateDefinition() { - } - - public GatewayPredicateDefinition(String text) { - int eqIdx = text.indexOf(61); - if (eqIdx <= 0) { - throw new RuntimeException("Unable to parse GatewayPredicateDefinition text '" + text + "', must be of the form name=value"); - } else { - this.setName(text.substring(0, eqIdx)); - String[] params = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ","); - - for(int i = 0; i < params.length; ++i) { - this.args.put(generateName(i), params[i]); - } - - } - } - - public static String generateName(int i) { - return GEN_KEY + i; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayPushDTO.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayPushDTO.java deleted file mode 100644 index 0ed6905e..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayPushDTO.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class GatewayPushDTO { - private String dataId; - private String groupId; - private ChannelMsg channelMsg; - - public GatewayPushDTO() { - } - - public GatewayPushDTO(String dataId, String groupId, ChannelMsg channelMsg) { - this.dataId = dataId; - this.groupId = groupId; - this.channelMsg = channelMsg; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/InstanceDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/InstanceDefinition.java deleted file mode 100644 index 00e54241..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/InstanceDefinition.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -import java.util.Map; - -/** - * @author 六如 - */ -@Data -public class InstanceDefinition { - private String instanceId; - private String serviceId; - private String ip; - private int port; - private Map metadata; -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java deleted file mode 100644 index 13a09987..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -/** - * @author 六如 - */ -public interface Isv { - - /** - * appKey - * @return 返回appKey - */ - String getAppKey(); - - /** - * 秘钥 - * @return 返回秘钥 - */ - String getSecretInfo(); - - /** - * 获取平台的私钥 - * @return 返回私钥 - */ - String getPrivateKeyPlatform(); - - /** - * 0启用,1禁用 - * @return 返回状态 - */ - Byte getStatus(); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/IsvDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/IsvDefinition.java deleted file mode 100644 index c7b8d874..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/IsvDefinition.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Getter; -import lombok.Setter; - -/** - * @author 六如 - */ -@Getter -@Setter -public class IsvDefinition implements Isv { - - public static final int SIGN_TYPE_RSA2 = 1; - - public IsvDefinition() { - } - - public IsvDefinition(String appKey, String secret) { - this.appKey = appKey; - this.secret = secret; - } - - private String appKey; - - /** 秘钥,签名方式为MD5时有用 */ - private String secret; - - /** 开发者生成的公钥, 数据库字段:public_key_isv */ - private String publicKeyIsv; - - /** 平台生成的私钥, 数据库字段:private_key_platform */ - private String privateKeyPlatform; - - /** 0启用,1禁用 */ - private Byte status; - - /** 签名类型:1:RSA2,2:MD5 */ - private Byte signType = 1; - - @Override - public String getSecretInfo() { - return signType == SIGN_TYPE_RSA2 ? publicKeyIsv : secret; - } - - - @Override - public String toString() { - return "IsvDefinition{" + - "appKey='" + appKey + '\'' + - ", secret='" + secret + '\'' + - ", publicKeyIsv='" + publicKeyIsv + '\'' + - ", privateKeyPlatform='" + privateKeyPlatform + '\'' + - ", status=" + status + - ", signType=" + signType + - '}'; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/IsvRoutePermission.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/IsvRoutePermission.java deleted file mode 100644 index 5307db4f..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/IsvRoutePermission.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -import java.util.Collections; -import java.util.List; - -/** - * isv授权过的路由 - * @author 六如 - */ -@Data -public class IsvRoutePermission { - private String appKey; - private List routeIdList = Collections.emptyList(); - private String routeIdListMd5; - private String listenPath; - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/LRUCache.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/LRUCache.java deleted file mode 100644 index 9ea136d7..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/LRUCache.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.Function; - -/** - * LRU缓存 - * LinkedHashMap 本身内部有一个触发条件则自动执行的方法:删除最老元素(最近最少使用的元素) - * 由于最近最少使用元素是 LinkedHashMap 内部处理 - * 故我们不再需要维护 最近访问元素放在链尾,get 时直接访问/ put 时直接存储 - * created by Ethan-Walker on 2019/2/16 - */ -public class LRUCache { - private final Map map; - - public LRUCache(int capacity) { - map = new LinkedHashMap(capacity, 0.75f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - // 容量大于capacity 时就删除 - return size() > capacity; - } - }; - } - public V get(K key) { - return map.get(key); - } - - public V computeIfAbsent(K key, - Function mappingFunction) { - return map.computeIfAbsent(key, mappingFunction); - } - - public V put(K key, V value) { - return map.put(key, value); - } - - public Collection values() { - return map.values(); - } -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/NacosConfigs.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/NacosConfigs.java deleted file mode 100644 index aa5aa15a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/NacosConfigs.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -/** - * @author 六如 - */ -public class NacosConfigs { - - public static final String GROUP_CHANNEL = "sop:channel"; - - public static final String GROUP_ROUTE = "sop:route"; - - public static final String DATA_ID_GRAY = "com.gitee.sop.channel.gray"; - - public static final String DATA_ID_IP_BLACKLIST = "com.gitee.sop.channel.ipblacklist"; - - public static final String DATA_ID_ISV = "com.gitee.sop.channel.isv"; - - public static final String DATA_ID_ROUTE_PERMISSION = "com.gitee.sop.channel.routepermission"; - - public static final String DATA_ID_LIMIT_CONFIG = "com.gitee.sop.channel.limitconfig"; - - public static final String DATA_ID_ROUTE_CONFIG = "com.gitee.sop.channel.routeconfig"; - - private static final String DATA_ID_TPL = "com.gitee.sop.route.%s"; - - public static String getRouteDataId(String serviceId) { - return String.format(DATA_ID_TPL, serviceId); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java deleted file mode 100644 index f353ba6f..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class RouteConfig { - - private static final byte STATUS_ENABLE = RouteStatus.ENABLE.getStatus(); - - /** - * 路由id - */ - private String routeId; - - /** - * 状态,0:待审核,1:启用,2:禁用。默认启用 - */ - private Byte status; - - /** - * 是否启用 - * - * @return true:启用 - */ - public boolean enable() { - return status == STATUS_ENABLE; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteDefinition.java deleted file mode 100644 index 7ffdf2c1..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteDefinition.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -import java.util.Collections; -import java.util.List; - -/** - * @author 六如 - */ -@Data -public class RouteDefinition { - /** - * 路由的Id(接口名+版本号),确保此id全局唯一 - */ - private String id; - - /** - * 接口名 - */ - private String name; - - /** - * 版本号 - */ - private String version; - - /** - * 路由断言集合配置 - */ - private List predicates = Collections.emptyList(); - - /** - * 路由过滤器集合配置 - */ - private List filters = Collections.emptyList(); - - /** - * 路由规则转发的目标uri - */ - private String uri; - - /** - * uri后面跟的path - */ - private String path; - - /** - * 路由执行的顺序 - */ - private int order = 0; - - /** - * 是否忽略验证,业务参数验证除外 - */ - private int ignoreValidate; - - /** - * 状态,0:待审核,1:启用,2:禁用 - */ - private int status = 1; - - /** - * 是否合并结果 - */ - private int mergeResult; - - /** - * 是否需要授权才能访问 - */ - private int permission; - - /** - * 是否需要token - */ - private int needToken; - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteStatus.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteStatus.java deleted file mode 100644 index ade016ea..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteStatus.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -/** - * 路由状态 - * - * @author 六如 - */ -public enum RouteStatus { - /** - * 路由状态,0:待审核 - */ - AUDIT(0, "待审核"), - /** - * 路由状态,1:已启用 - */ - ENABLE(1, "已启用"), - /** - * 路由状态,2:已禁用 - */ - DISABLE(2, "已禁用"), - ; - private byte status; - private String description; - - RouteStatus(Integer status, String description) { - this.status = status.byteValue(); - this.description = description; - } - - public byte getStatus() { - return status; - } - - public String getDescription() { - return description; - }} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceBeanInitializer.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceBeanInitializer.java deleted file mode 100644 index faef418f..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceBeanInitializer.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import com.gitee.sop.gatewaycommon.manager.ChannelMsgProcessor; - -/** - * @author 六如 - */ -public interface ServiceBeanInitializer extends ChannelMsgProcessor { - - void load(String serviceId); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceDefinition.java deleted file mode 100644 index 7e9fbace..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceDefinition.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class ServiceDefinition { - - /** - * 服务名称,对应spring.application.name - */ - private String serviceId; - - public ServiceDefinition(String serviceId) { - this.serviceId = serviceId; - } - - public String fetchServiceIdLowerCase() { - return serviceId.toLowerCase(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceGrayDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceGrayDefinition.java deleted file mode 100644 index 74e024ca..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceGrayDefinition.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class ServiceGrayDefinition { - private String serviceId; - private String instanceId; - private String data; -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteInfo.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteInfo.java deleted file mode 100644 index 33de1fc7..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteInfo.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import lombok.Data; - -import java.util.Date; -import java.util.List; - -/** - * @author 六如 - */ -@Data -public class ServiceRouteInfo { - - /** - * 服务名称,对应spring.application.name - */ - private String serviceId; - - private Date createTime = new Date(); - - private Date updateTime = new Date(); - - private String description; - - private List routeDefinitionList; - - private String md5; - - public String fetchServiceIdLowerCase() { - return serviceId.toLowerCase(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java deleted file mode 100644 index 5e76ede8..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - -/** - * @author 六如 - */ -public class SopConstants { - - private SopConstants() {} - - public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; - public static final String UTF8 = "UTF-8"; - public static final String FORMAT_JSON = "json"; - public static final String DEFAULT_SIGN_METHOD = "md5"; - public static final String EMPTY_JSON = "{}"; - - public static final String METADATA_SERVER_CONTEXT_PATH = "server.servlet.context-path"; - - public static final String METADATA_SERVER_CONTEXT_PATH_COMPATIBILITY = "context-path"; - - /** - * 在拦截器中调用获取参数: - * String cachedBody = (String)exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY); - */ - public static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; - - /** - * 在拦截器中调用获取参数: - * Map params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP); - */ - public static final String CACHE_REQUEST_BODY_FOR_MAP = "cacheRequestBodyForMap"; - - public static final String CACHE_API_PARAM = "cacheApiParam"; - - public static final String CACHE_UPLOAD_REQUEST = "cacheUploadRequest"; - - public static final String X_SERVICE_ERROR_CODE = "x-service-error-code"; - - public static final String X_SERVICE_ERROR_MESSAGE = "x-service-error-message"; - - public static final String X_SERVICE_ERROR_RESPONSE = "x-service-error-response"; - - public static final int BIZ_ERROR_STATUS = 4000; - public static final int UNKNOWN_ERROR_STATUS = 5050; - - public static final String UNKNOWN_SERVICE= "_sop_unknown_service_"; - public static final String UNKNOWN_METHOD = "_sop_unknown_method_"; - public static final String UNKNOWN_VERSION = "_sop_unknown_version_"; - - public static final String METADATA_ENV_KEY = "env"; - public static final String METADATA_ENV_PRE_VALUE = "pre"; - public static final String METADATA_ENV_GRAY_VALUE = "gray"; - - public static final String CACHE_ROUTE_INTERCEPTOR_CONTEXT = "cacheRouteInterceptorContext"; - public static final String TARGET_SERVICE = "sop-target-service"; - public static final String RESTFUL_REQUEST = "sop-restful-request"; - - public static final String METADATA_KEY_TIME_STARTUP = "server.startup-time"; - - public static final String CACHE_ROUTE_INFO = "cacheRouteInfo"; - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SpringContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SpringContext.java deleted file mode 100644 index f0e333de..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SpringContext.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -import org.springframework.context.ApplicationContext; - -/** - * @author 六如 - */ -public class SpringContext { - - private static ApplicationContext ctx; - - public static T getBean(Class clazz) { - return ctx.getBean(clazz); - } - - public static Object getBean(String beanName) { - return ctx.getBean(beanName); - } - - public static void setApplicationContext(ApplicationContext ctx) { - SpringContext.ctx = ctx; - } - - public static ApplicationContext getApplicationContext() { - return ctx; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/TargetRoute.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/TargetRoute.java deleted file mode 100644 index 6f05f524..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/TargetRoute.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gitee.sop.gatewaycommon.bean; - -/** - * @author 六如 - */ -public interface TargetRoute { - - /** - * 返回服务信息 - * - * @return 返回服务信息 - */ - ServiceDefinition getServiceDefinition(); - - /** - * 返回微服务路由对象 - * - * @return 返回微服务路由对象 - */ - RouteDefinition getRouteDefinition(); - - String getFullPath(); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/AbstractConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/AbstractConfiguration.java deleted file mode 100644 index 8509b126..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/AbstractConfiguration.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.gitee.sop.gatewaycommon.config; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.bean.BeanInitializer; -import com.gitee.sop.gatewaycommon.bean.SpringContext; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor; -import com.gitee.sop.gatewaycommon.limit.LimitManager; -import com.gitee.sop.gatewaycommon.manager.*; -import com.gitee.sop.gatewaycommon.message.ErrorFactory; -import com.gitee.sop.gatewaycommon.monitor.MonitorManager; -import com.gitee.sop.gatewaycommon.param.ParameterFormatter; -import com.gitee.sop.gatewaycommon.route.RegistryListener; -import com.gitee.sop.gatewaycommon.route.ServiceListener; -import com.gitee.sop.gatewaycommon.route.ServiceRouteListener; -import com.gitee.sop.gatewaycommon.secret.IsvManager; -import com.gitee.sop.gatewaycommon.sync.SopAsyncConfigurer; -import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil; -import com.gitee.sop.gatewaycommon.validate.SignConfig; -import com.gitee.sop.gatewaycommon.validate.Validator; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.client.discovery.event.HeartbeatEvent; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.annotation.Bean; -import org.springframework.context.event.EventListener; -import org.springframework.core.env.Environment; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.reactive.CorsWebFilter; - -import javax.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * @author 六如 - */ -@Slf4j -public class AbstractConfiguration implements ApplicationContextAware, ApplicationRunner { - - private final Lock lock = new ReentrantLock(); - private final Condition condition = lock.newCondition(); - - private volatile boolean isStartupCompleted; - - @Autowired - protected Environment environment; - - protected ApplicationContext applicationContext; - - @Override - public void setApplicationContext(ApplicationContext ctx) throws BeansException { - applicationContext = ctx; - } - - /** - * nacos事件监听 - * - * @param heartbeatEvent - */ - @EventListener(classes = HeartbeatEvent.class) - public void listenEvent(ApplicationEvent heartbeatEvent) { - // 没有启动完毕先等待 - if (!isStartupCompleted) { - lock.lock(); - try { - condition.await(); - } catch (InterruptedException e) { - log.error("condition.await() error", e); - } finally { - lock.unlock(); - } - } - applicationContext.getBean(RegistryListener.class).onEvent(heartbeatEvent); - } - - @Bean - @ConditionalOnMissingBean - ServiceListener serviceListener() { - return new ServiceRouteListener(); - } - - @Bean - @ConditionalOnMissingBean - Validator validator() { - return ApiConfig.getInstance().getValidator(); - } - - @Bean - @ConditionalOnMissingBean - IsvManager isvManager() { - return ApiConfig.getInstance().getIsvManager(); - } - - @Bean - @ConditionalOnMissingBean - IsvRoutePermissionManager isvRoutePermissionManager() { - return ApiConfig.getInstance().getIsvRoutePermissionManager(); - } - - @Bean - @ConditionalOnMissingBean - RouteConfigManager routeConfigManager() { - return ApiConfig.getInstance().getRouteConfigManager(); - } - - @Bean - @ConditionalOnMissingBean - LimitConfigManager limitConfigManager() { - return ApiConfig.getInstance().getLimitConfigManager(); - } - - @Bean - @ConditionalOnMissingBean - LimitManager limitManager() { - return ApiConfig.getInstance().getLimitManager(); - } - - @Bean - @ConditionalOnMissingBean - IPBlacklistManager ipBlacklistManager() { - return ApiConfig.getInstance().getIpBlacklistManager(); - } - - @Bean - @ConditionalOnMissingBean - EnvGrayManager envGrayManager() { - return ApiConfig.getInstance().getUserKeyManager(); - } - - @Bean - @ConditionalOnMissingBean - ParameterFormatter parameterFormatter() { - return ApiConfig.getInstance().getParameterFormatter(); - } - - @Bean - public SopAsyncConfigurer sopAsyncConfigurer(@Value("${sop.monitor-route-interceptor.thread-pool-size:4}") - int threadPoolSize) { - return new SopAsyncConfigurer("gatewayAsync", threadPoolSize); - } - - @Bean - @ConditionalOnMissingBean - public MonitorManager monitorManager() { - return new MonitorManager(); - } - - /** - * 跨域过滤器,gateway采用react形式,需要使用reactive包下的UrlBasedCorsConfigurationSource - */ - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty("sop.gateway-index-path") - public CorsWebFilter corsWebFilter() { - org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source = new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", createCorsConfiguration()); - return new CorsWebFilter(source); - } - - private CorsConfiguration createCorsConfiguration() { - CorsConfiguration corsConfiguration = new CorsConfiguration(); - corsConfiguration.addAllowedOrigin("*"); - corsConfiguration.addAllowedHeader("*"); - corsConfiguration.addAllowedMethod("*"); - return corsConfiguration; - } - - @Override - public void run(ApplicationArguments args) throws Exception { - this.isStartupCompleted = true; - lock.lock(); - condition.signalAll(); - lock.unlock(); - after(); - } - - @PostConstruct - private void post() { - EnvironmentContext.setEnvironment(environment); - SpringContext.setApplicationContext(applicationContext); - } - - public final void after() { - if (RouteRepositoryContext.getRouteRepository() == null) { - throw new IllegalArgumentException("RouteRepositoryContext.setRouteRepository()方法未使用"); - } - String serverName = EnvironmentKeys.SPRING_APPLICATION_NAME.getValue(); - if (!"sop-gateway".equals(serverName)) { - throw new IllegalArgumentException("spring.application.name必须为sop-gateway"); - } - String urlencode = EnvironmentKeys.SIGN_URLENCODE.getValue(); - if ("true".equals(urlencode)) { - SignConfig.enableUrlencodeMode(); - } - - initMessage(); - initBeanInitializer(); - initRouteInterceptor(); - doAfter(); - } - - protected void initBeanInitializer() { - Map beanInitializerMap = applicationContext.getBeansOfType(BeanInitializer.class); - beanInitializerMap.values().forEach(BeanInitializer::load); - } - - protected void initRouteInterceptor() { - Map routeInterceptorMap = applicationContext.getBeansOfType(RouteInterceptor.class); - Collection routeInterceptors = new ArrayList<>(routeInterceptorMap.values()); - RouteInterceptorUtil.addInterceptors(routeInterceptors); - } - - protected void doAfter() { - - } - - protected void initMessage() { - ErrorFactory.initMessageSource(ApiContext.getApiConfig().getI18nModules()); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/ApiConfigProperties.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/ApiConfigProperties.java deleted file mode 100644 index a35034e3..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/ApiConfigProperties.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gitee.sop.gatewaycommon.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author 六如 - */ -@Data -@ConfigurationProperties(prefix = "sop.api-config") -public class ApiConfigProperties { - - private List i18nModules = new ArrayList<>(); - - /** - * 忽略验证,设置true,则所有接口不会进行签名校验 - */ - private boolean ignoreValidate; - - /** - * 是否对结果进行合并。
- * 默认情况下是否合并结果由微服务端决定,一旦指定该值,则由该值决定,不管微服务端如何设置。 - */ - private Boolean mergeResult; - - /** - * 请求超时时间,默认5分钟,即允许在5分钟内重复请求 - */ - private int timeoutSeconds = 300; - - /** - * 是否开启限流功能 - */ - private boolean openLimit = true; - - /** - * 显示返回sign - */ - private boolean showReturnSign = true; - - /** - * 保存错误信息容器的容量 - */ - private int storeErrorCapacity = 20; -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/BaseGatewayAutoConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/BaseGatewayAutoConfiguration.java deleted file mode 100644 index 8cbfd2c3..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/BaseGatewayAutoConfiguration.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gitee.sop.gatewaycommon.config; - -import com.alibaba.fastjson.JSON; -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.EnableConfigurationProperties; - -import javax.annotation.PostConstruct; - -/** - * @author 六如 - */ -@Slf4j -@EnableConfigurationProperties(ApiConfigProperties.class) -public class BaseGatewayAutoConfiguration { - - @Autowired - private ApiConfigProperties apiConfigProperties; - - @PostConstruct - public void after() { - log.info("网关基本配置:{}", JSON.toJSONString(apiConfigProperties)); - ApiConfig apiConfig = ApiConfig.getInstance(); - BeanUtils.copyProperties(apiConfigProperties, apiConfig); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayAutoConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayAutoConfiguration.java deleted file mode 100644 index b68fdf6c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayAutoConfiguration.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gitee.sop.gatewaycommon.config; - -import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; - -/** - * https://blog.csdn.net/seashouwang/article/details/80299571 - * @author 六如 - */ -@Configuration -@Import(AlipayGatewayConfiguration.class) -public class SopGatewayAutoConfiguration extends BaseGatewayAutoConfiguration { -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayEnvironmentPostProcessor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayEnvironmentPostProcessor.java deleted file mode 100644 index 6161ecb8..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayEnvironmentPostProcessor.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.sop.gatewaycommon.config; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.PropertiesPropertySource; -import org.springframework.core.env.PropertySource; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -import java.io.IOException; -import java.util.Properties; - -/** - * 自定义环境处理,在运行SpringApplication之前加载任意配置文件到Environment环境中 - */ -public class SopGatewayEnvironmentPostProcessor implements EnvironmentPostProcessor { - - private final Properties properties = new Properties(); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - Resource resource = new ClassPathResource("META-INF/gateway.properties"); - // 加载成PropertySource对象,并添加到Environment环境中 - environment.getPropertySources().addLast(loadProfiles(resource)); - } - - private PropertySource loadProfiles(Resource resource) { - if (resource == null || !resource.exists()) { - throw new IllegalArgumentException("资源" + resource + "不存在"); - } - try { - properties.load(resource.getInputStream()); - return new PropertiesPropertySource(resource.getFilename(), properties); - } catch (IOException ex) { - throw new IllegalStateException("加载配置文件失败" + resource, ex); - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/exception/ApiException.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/exception/ApiException.java deleted file mode 100644 index 233a2263..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/exception/ApiException.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gitee.sop.gatewaycommon.exception; - - -import com.gitee.sop.gatewaycommon.message.Error; -import com.gitee.sop.gatewaycommon.message.ErrorFactory; -import com.gitee.sop.gatewaycommon.message.ErrorMeta; - -import java.util.Locale; - -/** - * @author 六如 - */ -public class ApiException extends RuntimeException { - - private transient Error error; - - private transient ErrorMeta errorMeta; - private transient Object[] params; - - public ApiException(ErrorMeta errorMeta, Object... params) { - this.errorMeta = errorMeta; - this.params = params; - } - - public ApiException(Throwable cause, ErrorMeta errorMeta, Object... params) { - super(cause); - this.errorMeta = errorMeta; - this.params = params; - } - - public Error getError(Locale locale) { - if (error == null) { - error = ErrorFactory.getError(this.errorMeta, locale, params); - } - return error; - } - - @Override - public String getMessage() { - String message = super.getMessage(); - return message == null ? errorMeta.toString() : message; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java deleted file mode 100644 index c6aa0409..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java +++ /dev/null @@ -1,328 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.gateway.common.FileUploadHttpServletRequest; -import com.gitee.sop.gatewaycommon.gateway.common.RequestContentDataExtractor; -import com.gitee.sop.gatewaycommon.gateway.common.SopServerHttpRequestDecorator; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.param.FormHttpOutputMessage; -import com.gitee.sop.gatewaycommon.param.ParamNames; -import com.gitee.sop.gatewaycommon.route.ForwardInfo; -import com.gitee.sop.gatewaycommon.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.http.converter.FormHttpMessageConverter; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MultiValueMap; -import org.springframework.web.multipart.MultipartHttpServletRequest; -import org.springframework.web.multipart.commons.CommonsMultipartResolver; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilterChain; -import reactor.core.publisher.Mono; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Consumer; - -/** - * @author 六如 - */ -@Slf4j -public class ServerWebExchangeUtil { - - private static final String THROWABLE_KEY = "sop.throwable"; - private static final String UNKNOWN_PATH = "/sop/unknown"; - - private static final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter(); - - /** - * 重定向 - * - * @param exchange exchange - * @param forwardInfo forwardInfo - * @return 返回新的ServerWebExchange,配合chain.filter(newExchange);使用 - */ - public static ServerWebExchange getForwardExchange(ServerWebExchange exchange, ForwardInfo forwardInfo) { - ServerHttpRequest.Builder builder = exchange.getRequest() - .mutate(); - ServerHttpRequest newRequest = builder - .header(ParamNames.HEADER_VERSION_NAME, forwardInfo.getVersion()) - .path(forwardInfo.getFullPath()).build(); - return exchange.mutate().request(newRequest).build(); - } - - - /** - * 构建一个接受请求体的request - * - * @param exchange exchange - * @param codecConfigurer codecConfigurer - * @return 返回ServerRequest - */ - public static ServerRequest createReadBodyRequest(ServerWebExchange exchange, ServerCodecConfigurer codecConfigurer) { - return ServerRequest.create(exchange, codecConfigurer.getReaders()); - } - - public static String getRestfulPath(String path, String prefix) { - int index = path.indexOf(prefix); - // 取"/rest"的后面部分 - return path.substring(index + prefix.length()); - } - - /** - * 重定向 - * - * @param exchange exchange - * @param forwardPath 重定向path - * @return 返回新的ServerWebExchange,配合chain.filter(newExchange);使用 - */ - public static ServerWebExchange getForwardExchange(ServerWebExchange exchange, String forwardPath) { - ServerHttpRequest newRequest = exchange.getRequest() - .mutate() - .path(forwardPath).build(); - return exchange.mutate().request(newRequest).build(); - } - - public static Mono forwardUnknown(ServerWebExchange exchange, WebFilterChain chain) { - // 非法访问 - ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, UNKNOWN_PATH); - return chain.filter(newExchange); - } - - public static ApiParam getApiParamByQuery(ServerWebExchange exchange, String query) { - ApiParam apiParam = new ApiParam(); - String ip = RequestUtil.getIP(exchange.getRequest()); - apiParam.setIp(ip); - Map params = RequestUtil.parseQueryToMap(query); - apiParam.putAll(params); - setApiParam(exchange, apiParam); - return apiParam; - } - - public static ApiParam getApiParam(ServerWebExchange exchange, byte[] body) { - MediaType contentType = exchange.getRequest().getHeaders().getContentType(); - if (contentType == null) { - contentType = MediaType.APPLICATION_FORM_URLENCODED; - } - ApiParam apiParam = new ApiParam(); - String ip = RequestUtil.getIP(exchange.getRequest()); - apiParam.setIp(ip); - Map params = null; - String contentTypeStr = contentType.toString().toLowerCase(); - // 如果是json方式提交 - if (StringUtils.containsAny(contentTypeStr, "json", "text")) { - JSONObject jsonObject = JSON.parseObject(new String(body, SopConstants.CHARSET_UTF8)); - apiParam.putAll(jsonObject); - } else if (StringUtils.containsIgnoreCase(contentTypeStr, "multipart")) { - // 如果是文件上传请求 - HttpServletRequest fileUploadRequest = getFileUploadRequest(exchange, body); - setFileUploadRequest(exchange, fileUploadRequest); - RequestUtil.UploadInfo uploadInfo = RequestUtil.getUploadInfo(fileUploadRequest); - params = uploadInfo.getUploadParams(); - apiParam.setUploadContext(uploadInfo.getUploadContext()); - } else { - // APPLICATION_FORM_URLENCODED请求 - params = RequestUtil.parseQueryToMap(new String(body, SopConstants.CHARSET_UTF8)); - } - if (params != null) { - apiParam.putAll(params); - } - setApiParam(exchange, apiParam); - return apiParam; - } - - - public static ApiParam getApiParam(ServerWebExchange exchange, Map params) { - ApiParam apiParam = new ApiParam(); - apiParam.putAll(params); - setApiParam(exchange, apiParam); - return apiParam; - } - - public static void setThrowable(ServerWebExchange exchange, Throwable throwable) { - exchange.getAttributes().put(THROWABLE_KEY, throwable); - } - - public static Throwable getThrowable(ServerWebExchange exchange) { - return (Throwable)exchange.getAttribute(THROWABLE_KEY); - } - - /** - * 获取请求参数 - * - * @param exchange ServerWebExchange - * @return 返回请求参数 - */ - public static ApiParam getApiParam(ServerWebExchange exchange) { - return exchange.getAttribute(SopConstants.CACHE_API_PARAM); - } - - /** - * 设置请求参数 - * - * @param exchange ServerWebExchange - * @param apiParam 请求参数 - */ - public static void setApiParam(ServerWebExchange exchange, ApiParam apiParam) { - exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, apiParam); - } - - /** - * 添加header - * - * @param exchange 当前ServerWebExchange - * @param headersConsumer headers - * @return 返回一个新的ServerWebExchange - */ - public static ServerWebExchange addHeaders(ServerWebExchange exchange, Consumer headersConsumer) { - // 创建一个新的request - ServerHttpRequest serverHttpRequestNew = exchange.getRequest() - .mutate() - .headers(headersConsumer) - .build(); - // 创建一个新的exchange对象 - return exchange - .mutate() - .request(serverHttpRequestNew) - .build(); - } - - /** - * 返回header值 - * @param exchange ServerWebExchange - * @param name header名 - * @return 返回值,没有返回null - */ - public static Optional getHeaderValue(ServerWebExchange exchange, String name) { - List values = exchange.getResponse().getHeaders().get(name); - if (CollectionUtils.isEmpty(values)) { - return Optional.empty(); - } - return values.stream().findFirst(); - } - - /** - * 移除header - * @param exchange ServerWebExchange - * @param name header名 - */ - public static void removeHeader(ServerWebExchange exchange, String name) { - exchange.getResponse().getHeaders().remove(name); - } - - /** - * 获取一个文件上传request - * - * @param exchange 当前ServerWebExchange - * @param data 上传文件请求体内容 - * @return 返回文件上传request - */ - public static HttpServletRequest getFileUploadRequest(ServerWebExchange exchange, byte[] data) { - return new FileUploadHttpServletRequest(exchange.getRequest(), data); - } - - public static HttpServletRequest getFileUploadRequest(ServerWebExchange exchange) { - return exchange.getAttribute(SopConstants.CACHE_UPLOAD_REQUEST); - } - - public static void setFileUploadRequest(ServerWebExchange exchange, HttpServletRequest request) { - exchange.getAttributes().put(SopConstants.CACHE_UPLOAD_REQUEST, request); - } - - /** - * 修改请求参数。参考自:https://blog.csdn.net/fuck487/article/details/85166162 - * - * @param exchange ServerWebExchange - * @param apiParam 原始请求参数 - * @param paramsConsumer 执行参数更改 - * @param headerConsumer header更改 - * @param 参数类型 - * @return 返回新的ServerWebExchange - */ - public static > ServerWebExchange format( - ServerWebExchange exchange - , T apiParam - , Consumer paramsConsumer - , Consumer headerConsumer - ) { - ServerHttpRequest serverHttpRequest = exchange.getRequest(); - // 新的request - ServerHttpRequest newRequest; - paramsConsumer.accept(apiParam); - if (serverHttpRequest.getMethod() == HttpMethod.GET) { - // 新的查询参数 - String queryString = RequestUtil.convertMapToQueryString(apiParam); - // 创建一个新的request,并使用新的uri - newRequest = new SopServerHttpRequestDecorator(serverHttpRequest, queryString); - } else { - MediaType mediaType = serverHttpRequest.getHeaders().getContentType(); - if (mediaType == null) { - mediaType = MediaType.APPLICATION_FORM_URLENCODED; - } - String contentType = mediaType.toString().toLowerCase(); - // 修改后的请求体 - // 处理json请求(application/json) - if (StringUtils.containsAny(contentType, "json", "text")) { - String bodyStr = JSON.toJSONString(apiParam); - byte[] bodyBytes = bodyStr.getBytes(StandardCharsets.UTF_8); - newRequest = new SopServerHttpRequestDecorator(serverHttpRequest, bodyBytes); - } else if (StringUtils.contains(contentType, "multipart")) { - // 处理文件上传请求 - FormHttpOutputMessage outputMessage = new FormHttpOutputMessage(); - HttpServletRequest request = ServerWebExchangeUtil.getFileUploadRequest(exchange); - try { - // 转成MultipartRequest - if (!(request instanceof MultipartHttpServletRequest)) { - CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(); - request = commonsMultipartResolver.resolveMultipart(request); - } - // 重写新的值 - MultiValueMap builder = RequestContentDataExtractor.extract(request); - for (Map.Entry entry : apiParam.entrySet()) { - Object value = entry.getValue(); - if (value instanceof List) { - builder.put(entry.getKey(), (List) value); - } else { - builder.put(entry.getKey(), Collections.singletonList(String.valueOf(value))); - } - } - // 将字段以及上传文件重写写入到流中 - formHttpMessageConverter.write(builder, mediaType, outputMessage); - // 获取新的上传文件流 - byte[] bodyBytes = outputMessage.getInput(); - newRequest = new SopServerHttpRequestDecorator(serverHttpRequest, bodyBytes); - // 必须要重新指定content-type,因为此时的boundary已经发生改变 - MediaType contentTypeMultipart = outputMessage.getHeaders().getContentType(); - newRequest.getHeaders().setContentType(contentTypeMultipart); - } catch (IOException e) { - log.error("修改上传文件请求参数失败, apiParam:{}", apiParam, e); - throw new RuntimeException(e); - } - } else { - // 否则一律按表单请求处理 - String bodyStr = RequestUtil.convertMapToQueryString(apiParam); - byte[] bodyBytes = bodyStr.getBytes(StandardCharsets.UTF_8); - newRequest = new SopServerHttpRequestDecorator(serverHttpRequest, bodyBytes); - } - } - HttpHeaders headers = newRequest.getHeaders(); - // 自定义header - headerConsumer.accept(headers); - // 创建一个新的exchange - return exchange.mutate().request(newRequest).build(); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/ByteArrayStreamWrapper.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/ByteArrayStreamWrapper.java deleted file mode 100644 index 60be70c9..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/ByteArrayStreamWrapper.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.common; - -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import java.io.IOException; - -/** - * byte数组流包装 - * - * @author 六如 - */ -public class ByteArrayStreamWrapper extends ServletInputStream { - - private byte[] data; - private int idx = 0; - - /** - * Creates a new ByteArrayStreamWrapper instance. - * - * @param data a byte[] value - */ - public ByteArrayStreamWrapper(byte[] data) { - if (data == null) { - data = new byte[0]; - } - this.data = data; - } - - @Override - public int read() throws IOException { - if (idx == data.length) { - return -1; - } - // I have to AND the byte with 0xff in order to ensure that it is returned as an unsigned integer - // the lack of this was causing a weird bug when manually unzipping gzipped request bodies - return data[idx++] & 0xff; - } - - @Override - public boolean isFinished() { - return true; - } - - @Override - public boolean isReady() { - return true; - } - - @Override - public void setReadListener(ReadListener readListener) { - - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/FileUploadHttpServletRequest.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/FileUploadHttpServletRequest.java deleted file mode 100644 index 70a4727b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/FileUploadHttpServletRequest.java +++ /dev/null @@ -1,391 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.common; - -import org.springframework.http.HttpMethod; -import org.springframework.http.server.reactive.ServerHttpRequest; - -import javax.servlet.AsyncContext; -import javax.servlet.DispatcherType; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpUpgradeHandler; -import javax.servlet.http.Part; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.Principal; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Locale; -import java.util.Map; - -/** - * 上传文件的request - * - * @author 六如 - */ -public class FileUploadHttpServletRequest implements HttpServletRequest { - private ServerHttpRequest serverHttpRequest; - private byte[] data; - - public FileUploadHttpServletRequest(ServerHttpRequest serverHttpRequest, byte[] data) { - this.serverHttpRequest = serverHttpRequest; - this.data = data; - } - - @Override - public ServletInputStream getInputStream() throws IOException { - return new ByteArrayStreamWrapper(data); - } - - @Override - public int getContentLength() { - return data.length; - } - - @Override - public long getContentLengthLong() { - return data.length; - } - - /** - * 最关键这个要返回正确 - */ - @Override - public String getContentType() { - return serverHttpRequest.getHeaders().getContentType().toString(); - } - - @Override - public String getAuthType() { - return null; - } - - @Override - public Cookie[] getCookies() { - return new Cookie[0]; - } - - @Override - public long getDateHeader(String s) { - return 0; - } - - @Override - public String getHeader(String s) { - return null; - } - - @Override - public Enumeration getHeaders(String s) { - return null; - } - - @Override - public Enumeration getHeaderNames() { - return null; - } - - @Override - public int getIntHeader(String s) { - return 0; - } - - @Override - public String getMethod() { - return HttpMethod.POST.name(); - } - - @Override - public String getPathInfo() { - return null; - } - - @Override - public String getPathTranslated() { - return null; - } - - @Override - public String getContextPath() { - return "/"; - } - - @Override - public String getQueryString() { - return null; - } - - @Override - public String getRemoteUser() { - return null; - } - - @Override - public boolean isUserInRole(String s) { - return false; - } - - @Override - public Principal getUserPrincipal() { - return null; - } - - @Override - public String getRequestedSessionId() { - return null; - } - - @Override - public String getRequestURI() { - return null; - } - - @Override - public StringBuffer getRequestURL() { - return null; - } - - @Override - public String getServletPath() { - return null; - } - - @Override - public HttpSession getSession(boolean b) { - return null; - } - - @Override - public HttpSession getSession() { - return null; - } - - @Override - public String changeSessionId() { - return null; - } - - @Override - public boolean isRequestedSessionIdValid() { - return false; - } - - @Override - public boolean isRequestedSessionIdFromCookie() { - return false; - } - - @Override - public boolean isRequestedSessionIdFromURL() { - return false; - } - - @Override - public boolean isRequestedSessionIdFromUrl() { - return false; - } - - @Override - public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException { - return false; - } - - @Override - public void login(String s, String s1) throws ServletException { - - } - - @Override - public void logout() throws ServletException { - - } - - @Override - public Collection getParts() throws IOException, ServletException { - return null; - } - - @Override - public Part getPart(String s) throws IOException, ServletException { - return null; - } - - @Override - public T upgrade(Class aClass) throws IOException, ServletException { - return null; - } - - @Override - public Object getAttribute(String s) { - return null; - } - - @Override - public Enumeration getAttributeNames() { - return null; - } - - @Override - public String getCharacterEncoding() { - return null; - } - - @Override - public void setCharacterEncoding(String s) throws UnsupportedEncodingException { - - } - - @Override - public String getParameter(String s) { - return null; - } - - @Override - public Enumeration getParameterNames() { - return Collections.emptyEnumeration(); - } - - @Override - public String[] getParameterValues(String s) { - return new String[0]; - } - - @Override - public Map getParameterMap() { - return null; - } - - @Override - public String getProtocol() { - return null; - } - - @Override - public String getScheme() { - return null; - } - - @Override - public String getServerName() { - return null; - } - - @Override - public int getServerPort() { - return 0; - } - - @Override - public BufferedReader getReader() throws IOException { - return null; - } - - @Override - public String getRemoteAddr() { - return null; - } - - @Override - public String getRemoteHost() { - return null; - } - - @Override - public void setAttribute(String s, Object o) { - - } - - @Override - public void removeAttribute(String s) { - - } - - @Override - public Locale getLocale() { - return null; - } - - @Override - public Enumeration getLocales() { - return null; - } - - @Override - public boolean isSecure() { - return false; - } - - @Override - public RequestDispatcher getRequestDispatcher(String s) { - return null; - } - - @Override - public String getRealPath(String s) { - return null; - } - - @Override - public int getRemotePort() { - return 0; - } - - @Override - public String getLocalName() { - return null; - } - - @Override - public String getLocalAddr() { - return null; - } - - @Override - public int getLocalPort() { - return 0; - } - - @Override - public ServletContext getServletContext() { - return null; - } - - @Override - public AsyncContext startAsync() throws IllegalStateException { - return null; - } - - @Override - public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { - return null; - } - - @Override - public boolean isAsyncStarted() { - return false; - } - - @Override - public boolean isAsyncSupported() { - return false; - } - - @Override - public AsyncContext getAsyncContext() { - return null; - } - - @Override - public DispatcherType getDispatcherType() { - return null; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/RequestContentDataExtractor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/RequestContentDataExtractor.java deleted file mode 100644 index 1bb39a97..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/RequestContentDataExtractor.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2013-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.gitee.sop.gatewaycommon.gateway.common; - -import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Collectors; - -import static java.util.Arrays.stream; -import static java.util.Collections.emptyMap; -import static org.springframework.util.StringUtils.isEmpty; -import static org.springframework.util.StringUtils.tokenizeToStringArray; -import static org.springframework.util.StringUtils.uriDecode; - -/** - * @author 六如 - */ -public class RequestContentDataExtractor { - public static MultiValueMap extract(HttpServletRequest request) throws IOException { - return (request instanceof MultipartHttpServletRequest) ? - extractFromMultipartRequest((MultipartHttpServletRequest) request) : - extractFromRequest(request); - } - - private static MultiValueMap extractFromRequest(HttpServletRequest request) throws IOException { - MultiValueMap builder = new LinkedMultiValueMap<>(); - Set queryParams = findQueryParams(request); - - for (Entry entry : request.getParameterMap().entrySet()) { - String key = entry.getKey(); - - if (!queryParams.contains(key) && entry.getValue() != null) { - for (String value : entry.getValue()) { - builder.add(key, value); - } - } - } - - return builder; - } - - private static MultiValueMap extractFromMultipartRequest(MultipartHttpServletRequest request) - throws IOException { - MultiValueMap builder = new LinkedMultiValueMap<>(); - Map> queryParamsGroupedByName = findQueryParamsGroupedByName( - request); - Set queryParams = findQueryParams(request); - - for (Entry entry : request.getParameterMap().entrySet()) { - String key = entry.getKey(); - List listOfAllParams = stream(request.getParameterMap().get(key)) - .collect(Collectors.toList()); - List listOfOnlyQueryParams = queryParamsGroupedByName.get(key); - - if(listOfOnlyQueryParams != null) { - listOfOnlyQueryParams = listOfOnlyQueryParams.stream() - .map(param -> uriDecode(param, Charset.defaultCharset())) - .collect(Collectors.toList()); - if (!listOfOnlyQueryParams.containsAll(listOfAllParams)) { - listOfAllParams.removeAll(listOfOnlyQueryParams); - for (String value : listOfAllParams) { - builder.add(key, - new HttpEntity<>(value, newHttpHeaders(request, key))); - } - } - } - - if (!queryParams.contains(key)) { - for (String value : entry.getValue()) { - builder.add(key, - new HttpEntity<>(value, newHttpHeaders(request, key))); - } - } - } - - for (Entry> parts : request.getMultiFileMap().entrySet()) { - for (MultipartFile file : parts.getValue()) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentDispositionFormData(file.getName(), file.getOriginalFilename()); - if (file.getContentType() != null) { - headers.setContentType(MediaType.valueOf(file.getContentType())); - } - - HttpEntity entity = new HttpEntity<>(new InputStreamResource(file.getInputStream()), headers); - builder.add(parts.getKey(), entity); - } - } - - return builder; - } - - private static HttpHeaders newHttpHeaders(MultipartHttpServletRequest request, - String key) { - HttpHeaders headers = new HttpHeaders(); - String type = request.getMultipartContentType(key); - - if (type != null) { - headers.setContentType(MediaType.valueOf(type)); - } - return headers; - } - - private static Set findQueryParams(HttpServletRequest request) { - Set result = new HashSet<>(); - String query = request.getQueryString(); - - if (query != null) { - for (String value : tokenizeToStringArray(query, "&")) { - if (value.contains("=")) { - value = value.substring(0, value.indexOf("=")); - } - result.add(value); - } - } - - return result; - } - - static Map> findQueryParamsGroupedByName( - HttpServletRequest request) { - String query = request.getQueryString(); - if (isEmpty(query)) { - return emptyMap(); - } - return UriComponentsBuilder.fromUriString("?" + query).build().getQueryParams(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/SopServerHttpRequestDecorator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/SopServerHttpRequestDecorator.java deleted file mode 100644 index c73845f3..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/common/SopServerHttpRequestDecorator.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.common; - -import io.netty.buffer.ByteBufAllocator; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.NettyDataBufferFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpRequestDecorator; -import org.springframework.web.util.UriComponentsBuilder; -import reactor.core.publisher.Flux; - -import java.net.URI; - -/** - * @author 六如 - */ -public class SopServerHttpRequestDecorator extends ServerHttpRequestDecorator { - - private Flux bodyDataBuffer; - private HttpHeaders httpHeaders; - private URI uri; - - /** - * ServerHttpRequest包装,作用类似于HttpServletRequestWrapper - * - * @param delegate 老的request - * @param queryString get请求后面的参数 - */ - public SopServerHttpRequestDecorator(ServerHttpRequest delegate, String queryString) { - super(delegate); - if (delegate.getMethod() != HttpMethod.GET) { - throw new IllegalArgumentException("this constructor must be used by GET request."); - } - if (queryString == null) { - throw new IllegalArgumentException("queryString can not be null."); - } - // 默认header是只读的,把它改成可写,方便后面的过滤器使用 - this.httpHeaders = HttpHeaders.writableHttpHeaders(delegate.getHeaders()); - this.uri = UriComponentsBuilder.fromUri(delegate.getURI()) - .replaceQuery(queryString) - .build(true) - .toUri(); - } - - - /** - * ServerHttpRequest包装,作用类似于HttpServletRequestWrapper - * - * @param delegate 老的request - * @param bodyData 请求体内容 - */ - public SopServerHttpRequestDecorator(ServerHttpRequest delegate, byte[] bodyData) { - super(delegate); - if (bodyData == null) { - throw new IllegalArgumentException("bodyData can not be null."); - } - // 默认header是只读的,把它改成可写,方便后面的过滤器使用 - this.httpHeaders = HttpHeaders.writableHttpHeaders(delegate.getHeaders()); - // 由于请求体已改变,这里要重新设置contentLength - int contentLength = bodyData.length; - httpHeaders.setContentLength(contentLength); - if (contentLength <= 0) { - // TODO: this causes a 'HTTP/1.1 411 Length Required' on httpbin.org - httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); - } - this.bodyDataBuffer = stringBuffer(bodyData); - } - - /** - * 字符串转DataBuffer - * - * @param bytes 请求体 - * @return 返回buffer - */ - private static Flux stringBuffer(byte[] bytes) { - NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); - DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); - buffer.write(bytes); - return Flux.just(buffer); - } - - @Override - public URI getURI() { - return uri == null ? super.getURI() : uri; - } - - @Override - public HttpHeaders getHeaders() { - return this.httpHeaders; - } - - @Override - public Flux getBody() { - return bodyDataBuffer == null ? super.getBody() : bodyDataBuffer; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/AlipayGatewayConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/AlipayGatewayConfiguration.java deleted file mode 100644 index ac599ae9..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/AlipayGatewayConfiguration.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.configuration; - -/** - * 具备支付宝开放平台能力配置 https://docs.open.alipay.com/api - * - * @author 六如 - */ -public class AlipayGatewayConfiguration extends BaseGatewayConfiguration { - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java deleted file mode 100644 index dbb398d2..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.configuration; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.config.AbstractConfiguration; -import com.gitee.sop.gatewaycommon.gateway.filter.GatewayModifyResponseGatewayFilter; -import com.gitee.sop.gatewaycommon.gateway.filter.IndexFilter; -import com.gitee.sop.gatewaycommon.gateway.filter.LimitFilter; -import com.gitee.sop.gatewaycommon.gateway.filter.ParameterFormatterFilter; -import com.gitee.sop.gatewaycommon.gateway.handler.GatewayExceptionHandler; -import com.gitee.sop.gatewaycommon.gateway.route.GatewayForwardChooser; -import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache; -import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository; -import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.web.reactive.result.view.ViewResolver; - -import java.util.Collections; -import java.util.List; - - -/** - * @author 六如 - */ -@Slf4j -public class BaseGatewayConfiguration extends AbstractConfiguration { - - public BaseGatewayConfiguration() { - ApiConfig.getInstance().setUseGateway(true); - } - - @Bean - public IndexFilter indexFilter() { - return new IndexFilter(); - } - - /** - * 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可 - * - * @param viewResolversProvider viewResolversProvider - * @param serverCodecConfigurer serverCodecConfigurer - */ - @Primary - @Bean - @Order(Ordered.HIGHEST_PRECEDENCE) - public ErrorWebExceptionHandler sopErrorWebExceptionHandler(ObjectProvider> viewResolversProvider, - ServerCodecConfigurer serverCodecConfigurer) { - - GatewayExceptionHandler jsonExceptionHandler = new GatewayExceptionHandler(); - jsonExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList)); - jsonExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters()); - jsonExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders()); - return jsonExceptionHandler; - } - - /** - * 处理返回结果 - */ - @Bean - GatewayModifyResponseGatewayFilter gatewayModifyResponseGatewayFilter() { - return new GatewayModifyResponseGatewayFilter(); - } - - @Bean - ParameterFormatterFilter parameterFormatterFilter() { - return new ParameterFormatterFilter(); - } - - @Bean - LimitFilter limitFilter() { - return new LimitFilter(); - } - - @Bean - GatewayRouteCache gatewayRouteCache(GatewayRouteRepository gatewayRouteRepository) { - return new GatewayRouteCache(gatewayRouteRepository); - } - - @Bean - GatewayRouteRepository gatewayRouteRepository() { - GatewayRouteRepository gatewayRouteRepository = new GatewayRouteRepository(); - RouteRepositoryContext.setRouteRepository(gatewayRouteRepository); - return gatewayRouteRepository; - } - - @Bean - GatewayForwardChooser gatewayForwardChooser() { - return new GatewayForwardChooser(); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/TaobaoGatewayConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/TaobaoGatewayConfiguration.java deleted file mode 100644 index cc943032..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/TaobaoGatewayConfiguration.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.configuration; - -import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.param.ParamNames; -import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; - -/** - * 具备淘宝开放平台能力配置 - * 淘宝开放平台:http://open.taobao.com/doc.htm - * @author 六如 - */ -public class TaobaoGatewayConfiguration extends BaseGatewayConfiguration { - - static { - ParamNames.APP_KEY_NAME = "app_key"; - ParamNames.SIGN_TYPE_NAME = "sign_method"; - ParamNames.VERSION_NAME = "v"; - ParamNames.APP_AUTH_TOKEN_NAME = "session"; - - ApiContext.getApiConfig().setSigner(new TaobaoSigner()); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ConfigChannelController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ConfigChannelController.java deleted file mode 100644 index 61849168..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ConfigChannelController.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.controller; - -import com.alibaba.fastjson.JSON; -import com.gitee.sop.gatewaycommon.bean.GatewayPushDTO; -import com.gitee.sop.gatewaycommon.bean.NacosConfigs; -import com.gitee.sop.gatewaycommon.bean.SpringContext; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.manager.ChannelMsgProcessor; -import com.gitee.sop.gatewaycommon.manager.EnvGrayManager; -import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager; -import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager; -import com.gitee.sop.gatewaycommon.manager.LimitConfigManager; -import com.gitee.sop.gatewaycommon.manager.RouteConfigManager; -import com.gitee.sop.gatewaycommon.secret.IsvManager; -import com.gitee.sop.gatewaycommon.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author 六如 - */ -@Slf4j -@RestController -public class ConfigChannelController { - - private static Map> processorMap = new HashMap<>(16); - - static { - processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_GRAY, EnvGrayManager.class); - processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_IP_BLACKLIST, IPBlacklistManager.class); - processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ISV, IsvManager.class); - processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_PERMISSION, IsvRoutePermissionManager.class); - processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_LIMIT_CONFIG, LimitConfigManager.class); - processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_CONFIG, RouteConfigManager.class); - } - - @Value("${sop.secret}") - private String secret; - - @Autowired - private ServerCodecConfigurer codecConfigurer; - - @PostMapping("/sop/configChannelMsg") - public Mono configChannel(ServerWebExchange exchange) { - ServerRequest serverRequest = ServerWebExchangeUtil.createReadBodyRequest(exchange, codecConfigurer); - // 读取请求体中的内容 - return serverRequest.bodyToMono(String.class) - .flatMap(requestJson -> { - String sign = exchange.getRequest().getHeaders().getFirst("sign"); - try { - // 签名验证 - RequestUtil.checkResponseBody(requestJson, sign, secret); - } catch (Exception e) { - log.error("configChannelMsg错误", e); - return Mono.just(e.getMessage()); - } - GatewayPushDTO gatewayPushDTO = JSON.parseObject(requestJson, GatewayPushDTO.class); - ChannelMsgProcessor channelMsgProcessor = getChannelMsgProcessor(gatewayPushDTO); - channelMsgProcessor.process(gatewayPushDTO.getChannelMsg()); - return Mono.just("ok"); - }); - } - - private ChannelMsgProcessor getChannelMsgProcessor(GatewayPushDTO gatewayPushDTO) { - String key = gatewayPushDTO.getGroupId() + gatewayPushDTO.getDataId(); - Class aClass = processorMap.get(key); - return SpringContext.getBean(aClass); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/GatewayController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/GatewayController.java deleted file mode 100644 index 71f759ab..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/GatewayController.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.controller; - -import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.exception.ApiException; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.result.ResultExecutor; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -/** - * @author 六如 - */ -@RestController -public class GatewayController { - - /** - * 处理签名错误返回 - * - * @param exchange exchange - * @return 返回最终结果 - */ - @RequestMapping("/sop/validateError") - public Mono validateError(ServerWebExchange exchange) { - Throwable throwable = ServerWebExchangeUtil.getThrowable(exchange); - // 合并微服务传递过来的结果,变成最终结果 - ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); - String gatewayResult = resultExecutor.buildErrorResult(exchange, throwable); - exchange.getResponse().getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - return Mono.just(gatewayResult); - } - - @RequestMapping("/sop/unknown") - public Mono unknown(ServerWebExchange exchange) { - ApiException exception = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException(); - ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); - String gatewayResult = resultExecutor.buildErrorResult(exchange, exception); - exchange.getResponse().getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - return Mono.just(gatewayResult); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/RestfulController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/RestfulController.java deleted file mode 100644 index ba760d51..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/RestfulController.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.controller; - -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.gateway.webflux.ProxyExchange; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -import java.util.List; -import java.util.Random; - -/** - * 处理restful请求 - * @author 六如 - */ -@Controller -public class RestfulController { - - @Value("${sop.restful.path:/rest}") - private String prefix; - @Autowired - private DiscoveryClient discoveryClient; - - @RequestMapping("${sop.restful.path:/rest}/**") - public Mono> proxy(ProxyExchange proxy, ServerWebExchange exchange) { - String path = proxy.path(); - String serviceId = getServiceId(path); - String targetPath = getTargetPath(serviceId, path); - String rawQuery = exchange.getRequest().getURI().getRawQuery(); - if (StringUtils.hasLength(rawQuery)) { - targetPath = targetPath + "?" + rawQuery; - } - // 负载均衡 - List instances = discoveryClient.getInstances(serviceId); - if (CollectionUtils.isEmpty(instances)) { - return Mono.error(new RuntimeException("serviceId: " + serviceId + " not found")); - } - ServiceInstance serviceInstance = instances.stream() - .skip(new Random().nextInt(instances.size())) - .findFirst() - .orElse(null); - String uri = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + targetPath; - return proxy.uri(uri).forward(); - } - - /** - * 从path中解析出serviceId - * @param path 格式:/rest/ - * @return 返回serviceId - */ - private String getServiceId(String path) { - int length = prefix.length() + 1; - path = path.substring(length); - int index = path.indexOf('/'); - path = path.substring(0, index); - return path; - } - - private String getTargetPath(String serviceId, String path) { - int length = prefix.length() + 1; - int len = length + serviceId.length(); - return path.substring(len); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/EnvGrayFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/EnvGrayFilter.java deleted file mode 100644 index 6d67805c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/EnvGrayFilter.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import com.gitee.sop.gatewaycommon.bean.TargetRoute; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.manager.EnvGrayManager; -import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.param.ParamNames; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.core.Ordered; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -/** - * @author 六如 - * @deprecated - * @see com.gitee.sop.gatewaycommon.gateway.route.GatewayForwardChooser - */ -@Deprecated -public class EnvGrayFilter implements GlobalFilter, Ordered { - - @Autowired - private EnvGrayManager envGrayManager; - - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); - String nameVersion = apiParam.fetchNameVersion(); - TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(nameVersion); - if (targetRoute == null) { - return chain.filter(exchange); - } - String serviceId = targetRoute.getServiceDefinition().fetchServiceIdLowerCase(); - // 如果服务在灰度阶段,返回一个灰度版本号 - String version = envGrayManager.getVersion(serviceId, nameVersion); - if (version != null && envGrayManager.containsKey(serviceId, apiParam.fetchAppKey())) { - ServerWebExchange serverWebExchange = ServerWebExchangeUtil.addHeaders(exchange, httpHeaders -> httpHeaders.set(ParamNames.HEADER_VERSION_NAME, version)); - return chain.filter(serverWebExchange); - } - return chain.filter(exchange); - } - - @Override - public int getOrder() { - return Orders.ENV_GRAY_FILTER_ORDER; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/GatewayModifyResponseGatewayFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/GatewayModifyResponseGatewayFilter.java deleted file mode 100644 index bcea7b0a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/GatewayModifyResponseGatewayFilter.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.result.ResultExecutor; -import org.apache.commons.lang3.StringUtils; -import org.reactivestreams.Publisher; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter; -import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage; -import org.springframework.cloud.gateway.filter.factory.rewrite.MessageBodyEncoder; -import org.springframework.cloud.gateway.support.BodyInserterContext; -import org.springframework.core.Ordered; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferFactory; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseCookie; -import org.springframework.http.client.reactive.ClientHttpResponse; -import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.http.server.reactive.ServerHttpResponseDecorator; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserter; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; - -import javax.annotation.PostConstruct; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import static java.util.function.Function.identity; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR; - -/** - * 修改返回结果 - * - * @author 六如 - */ -public class GatewayModifyResponseGatewayFilter implements GlobalFilter, Ordered { - - @Autowired - private ServerCodecConfigurer codecConfigurer; - @Autowired - private Set bodyEncoders; - - private Map messageBodyEncoders; - - @Override - @SuppressWarnings("unchecked") - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - - ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) { - - @Override - public Mono writeWith(Publisher body) { - String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR); - // 如果是下载文件,直接放行,不合并结果 - if (StringUtils.containsIgnoreCase(originalResponseContentType, MediaType.APPLICATION_OCTET_STREAM_VALUE)) { - return chain.filter(exchange); - } - // rest请求,直接放行 - if (exchange.getAttribute(SopConstants.RESTFUL_REQUEST) != null) { - return chain.filter(exchange); - } - Class inClass = String.class; - Class outClass = String.class; - - HttpHeaders httpHeaders = new HttpHeaders(); - // explicitly add it in this way instead of - // 'httpHeaders.setContentType(originalResponseContentType)' - // this will prevent exception in case of using non-standard media - // types like "Content-Type: image" - httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType); - ClientResponse clientResponse = prepareClientResponse(exchange, body, httpHeaders); - - //TODO: flux or mono - Mono modifiedBody = clientResponse.bodyToMono(inClass) - // 修复微服务接口返回void网关不会返回code和msg问题 - .switchIfEmpty(Mono.just("")) - .flatMap(originalBody -> { - // 合并微服务传递过来的结果,变成最终结果 - ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); - String ret = resultExecutor.mergeResult(exchange, String.valueOf(originalBody)); - return Mono.just(ret); - }); - - BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, - outClass); - CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, - exchange.getResponse().getHeaders()); - return bodyInserter.insert(outputMessage, new BodyInserterContext()) - .then(Mono.defer(() -> { - Mono messageBody = writeBody(getDelegate(), - outputMessage, outClass); - HttpHeaders headers = getDelegate().getHeaders(); - if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING) - || headers.containsKey(HttpHeaders.CONTENT_LENGTH)) { - messageBody = messageBody.doOnNext(data -> headers - .setContentLength(data.readableByteCount())); - } - // TODO: fail if isStreamingMediaType? - return getDelegate().writeWith(messageBody); - })); - } - - @Override - public Mono writeAndFlushWith(Publisher> body) { - return writeWith(Flux.from(body) - .flatMapSequential(p -> p)); - } - }; - - return chain.filter(exchange.mutate().response(responseDecorator).build()); - } - - private ClientResponse prepareClientResponse( - ServerWebExchange exchange, - Publisher body, - HttpHeaders httpHeaders - ) { - ClientResponse.Builder builder; - builder = ClientResponse.create(exchange.getResponse().getStatusCode(), codecConfigurer.getReaders()); - return builder.headers(headers -> headers.putAll(httpHeaders)) - .body(Flux.from(body)).build(); - } - - private Mono writeBody(ServerHttpResponse httpResponse, - CachedBodyOutputMessage message, Class outClass) { - Mono response = DataBufferUtils.join(message.getBody()); - if (byte[].class.isAssignableFrom(outClass)) { - return response; - } - - List encodingHeaders = httpResponse.getHeaders() - .getOrEmpty(HttpHeaders.CONTENT_ENCODING); - for (String encoding : encodingHeaders) { - MessageBodyEncoder encoder = messageBodyEncoders.get(encoding); - if (encoder != null) { - DataBufferFactory dataBufferFactory = httpResponse.bufferFactory(); - response = response.publishOn(Schedulers.parallel()).map(buffer -> { - byte[] encodedResponse = encoder.encode(buffer); - DataBufferUtils.release(buffer); - return encodedResponse; - }).map(dataBufferFactory::wrap); - break; - } - } - - return response; - } - - @Override - public int getOrder() { - return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1; - } - - public class ResponseAdapter implements ClientHttpResponse { - - private final Flux flux; - private final HttpHeaders headers; - - public ResponseAdapter(Publisher body, HttpHeaders headers) { - this.headers = headers; - if (body instanceof Flux) { - flux = (Flux) body; - } else { - flux = ((Mono) body).flux(); - } - } - - @Override - public Flux getBody() { - return flux; - } - - @Override - public HttpHeaders getHeaders() { - return headers; - } - - @Override - public HttpStatus getStatusCode() { - return null; - } - - @Override - public int getRawStatusCode() { - return 0; - } - - @Override - public MultiValueMap getCookies() { - return null; - } - } - - @PostConstruct - public void after() { - this.messageBodyEncoders = bodyEncoders == null ? Collections.emptyMap() : bodyEncoders.stream() - .collect(Collectors.toMap(MessageBodyEncoder::encodingType, identity())); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java deleted file mode 100644 index a820d1b5..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.exception.ApiException; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.gateway.route.GatewayForwardChooser; -import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.route.ForwardInfo; -import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil; -import com.gitee.sop.gatewaycommon.validate.Validator; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage; -import org.springframework.cloud.gateway.support.BodyInserterContext; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpRequestDecorator; -import org.springframework.web.reactive.function.BodyInserter; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilter; -import org.springframework.web.server.WebFilterChain; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.net.URI; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -/** - * 入口 - * - * @author 六如 - */ -@Slf4j -@Order(Ordered.HIGHEST_PRECEDENCE) -public class IndexFilter implements WebFilter { - - @Autowired - private ServerCodecConfigurer codecConfigurer; - - /** 路径白名单 */ - private static final List PATH_WHITE_LIST = Arrays.asList( - "/sop", "/actuator" - ); - - @Value("${sop.gateway-index-path:/}") - private String indexPath; - - /** sop.restful.enable=true ,开启restful请求,默认开启 */ - @Value("${sop.restful.enable:true}") - private boolean enableRestful; - - @Autowired - private Validator validator; - - @Autowired - private GatewayForwardChooser gatewayForwardChooser; - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - ServerHttpRequest request = exchange.getRequest(); - String path = request.getURI().getPath(); - // 路径是否在白名单中,直接放行 - if (this.isPathInWhiteList(path)) { - return chain.filter(exchange); - } - // 如果是restful请求,直接转发 - // see:com.gitee.sop.gatewaycommon.gateway.controller.RestfulController - if (enableRestful && path.startsWith(EnvironmentKeys.SOP_RESTFUL_PATH.getValue())) { - return chain.filter(exchange); - } - if (Objects.equals(path, indexPath) || "".equals(path)) { - if (request.getMethod() == HttpMethod.POST) { - ServerRequest serverRequest = ServerWebExchangeUtil.createReadBodyRequest(exchange, codecConfigurer); - // 读取请求体中的内容 - Mono modifiedBody = serverRequest.bodyToMono(byte[].class) - .switchIfEmpty(Mono.just("".getBytes())) - .flatMap(data -> { - // 构建ApiParam - ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange, data); - // 签名验证 - doValidate(exchange, apiParam); - return Mono.just(data); - }); - BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, (Class)byte[].class); - HttpHeaders headers = new HttpHeaders(); - headers.putAll(exchange.getRequest().getHeaders()); - - // the new content type will be computed by bodyInserter - // and then set in the request decorator - headers.remove(HttpHeaders.CONTENT_LENGTH); - - CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage( - exchange, headers); - return bodyInserter.insert(outputMessage, new BodyInserterContext()) - .then(Mono.defer(() -> { - ForwardInfo forwardInfo = gatewayForwardChooser.getForwardInfo(exchange); - ServerHttpRequest decorator = decorate(exchange, headers, outputMessage); - ServerWebExchange newExchange = exchange.mutate().request(decorator).build(); - ServerWebExchange forwardExchange = ServerWebExchangeUtil.getForwardExchange(newExchange, forwardInfo); - return chain.filter(forwardExchange); - })); - - } else { - URI uri = exchange.getRequest().getURI(); - // 原始参数 - String originalQuery = uri.getRawQuery(); - // 构建ApiParam - ApiParam apiParam = ServerWebExchangeUtil.getApiParamByQuery(exchange, originalQuery); - // 签名验证 - doValidate(exchange, apiParam); - - ForwardInfo forwardInfo = gatewayForwardChooser.getForwardInfo(exchange); - ServerWebExchange forwardExchange = ServerWebExchangeUtil.getForwardExchange(exchange, forwardInfo); - return chain.filter(forwardExchange); - } - } else { - return ServerWebExchangeUtil.forwardUnknown(exchange, chain); - } - } - - private boolean isPathInWhiteList(String path) { - for (String whitePath : PATH_WHITE_LIST) { - if (path.startsWith(whitePath)) { - return true; - } - } - return false; - } - - private void doValidate(ServerWebExchange exchange, ApiParam apiParam) { - try { - validator.validate(apiParam); - this.afterValidate(exchange, apiParam); - } catch (ApiException e) { - log.error("验证失败, errorMsg:{},url:{}, ip:{}, params:{}", - e.getMessage(), - exchange.getRequest().getURI().toString(), - apiParam.fetchIp(), apiParam.toJSONString()); - ServerWebExchangeUtil.setThrowable(exchange, e); - } - } - - private void afterValidate(ServerWebExchange exchange, ApiParam param) { - RouteInterceptorUtil.runPreRoute(exchange, param, context -> { - DefaultRouteInterceptorContext defaultRouteInterceptorContext = (DefaultRouteInterceptorContext) context; - defaultRouteInterceptorContext.setRequestDataSize(exchange.getRequest().getHeaders().getContentLength()); - exchange.getAttributes().put(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT, context); - }); - } - - private ServerHttpRequestDecorator decorate( - ServerWebExchange exchange - , HttpHeaders headers - , CachedBodyOutputMessage outputMessage - ) { - return new ServerHttpRequestDecorator(exchange.getRequest()) { - @Override - public HttpHeaders getHeaders() { - long contentLength = headers.getContentLength(); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.putAll(super.getHeaders()); - if (contentLength > 0) { - httpHeaders.setContentLength(contentLength); - } else { - httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); - } - return httpHeaders; - } - - @Override - public Flux getBody() { - return outputMessage.getBody(); - } - }; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java deleted file mode 100644 index 9bb11546..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.exception.ApiException; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.limit.LimitManager; -import com.gitee.sop.gatewaycommon.limit.LimitType; -import com.gitee.sop.gatewaycommon.manager.LimitConfigManager; -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.message.ErrorMeta; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.core.Ordered; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -/** - * spring cloud gateway限流过滤器 - * @author 六如 - */ -@Slf4j -public class LimitFilter implements GlobalFilter, Ordered { - - private static final ErrorMeta LIMIT_ERROR_META = ErrorEnum.ISV_REQUEST_LIMIT.getErrorMeta(); - - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - if (exchange.getAttribute(SopConstants.RESTFUL_REQUEST) != null) { - return chain.filter(exchange); - } - ApiConfig apiConfig = ApiConfig.getInstance(); - // 限流功能未开启,直接返回 - if (!apiConfig.isOpenLimit()) { - return chain.filter(exchange); - } - ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); - if (apiParam == null) { - return chain.filter(exchange); - } - ConfigLimitDto configLimitDto = this.findConfigLimitDto(apiConfig, apiParam, exchange); - if (configLimitDto == null) { - return chain.filter(exchange); - } - // 单个限流功能未开启 - if (configLimitDto.getLimitStatus() == ConfigLimitDto.LIMIT_STATUS_CLOSE) { - return chain.filter(exchange); - } - Byte limitType = configLimitDto.getLimitType(); - LimitManager limitManager = ApiConfig.getInstance().getLimitManager(); - // 如果是窗口策略 - if (limitType == LimitType.LEAKY_BUCKET.getType()) { - boolean acquire = limitManager.acquire(configLimitDto); - // 被限流,返回错误信息 - if (!acquire) { - throw new ApiException(LIMIT_ERROR_META); - } - } else if (limitType == LimitType.TOKEN_BUCKET.getType()) { - limitManager.acquireToken(configLimitDto); - } - return chain.filter(exchange); - } - - @Override - public int getOrder() { - return Orders.LIMIT_FILTER_ORDER; - } - - protected ConfigLimitDto findConfigLimitDto(ApiConfig apiConfig, ApiParam apiParam, ServerWebExchange exchange) { - LimitConfigManager limitConfigManager = apiConfig.getLimitConfigManager(); - - String routeId = apiParam.fetchNameVersion(); - String appKey = apiParam.fetchAppKey(); - String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress(); - - String[] limitKeys = new String[]{ - routeId, - appKey, - routeId + appKey, - - ip, - ip + routeId, - ip + appKey, - ip + routeId + appKey, - }; - - List limitConfigList = new ArrayList<>(); - for (String limitKey : limitKeys) { - ConfigLimitDto configLimitDto = limitConfigManager.get(limitKey); - if (configLimitDto == null) { - continue; - } - limitConfigList.add(configLimitDto); - } - if (limitConfigList.isEmpty()) { - return null; - } - limitConfigList.sort(Comparator.comparing(ConfigLimitDto::getOrderIndex)); - return limitConfigList.get(0); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java deleted file mode 100644 index f1702430..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import org.springframework.core.Ordered; - -/** - * @author 六如 - */ -public class Orders { - /** 验证拦截器order */ - public static final int VALIDATE_FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 1000; - - /** 参数格式化过滤器 */ - public static final int PARAMETER_FORMATTER_FILTER_ORDER = VALIDATE_FILTER_ORDER + 1; - - /** 权限验证过滤 */ - public static final int PRE_ROUTE_PERMISSION_FILTER_ORDER = PARAMETER_FORMATTER_FILTER_ORDER + 100; - - /** 验证拦截器order */ - public static final int LIMIT_FILTER_ORDER = PRE_ROUTE_PERMISSION_FILTER_ORDER + 100; - - /** 灰度发布过滤器 */ - public static final int ENV_GRAY_FILTER_ORDER = LIMIT_FILTER_ORDER + 100; - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ParameterFormatterFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ParameterFormatterFilter.java deleted file mode 100644 index 8703187a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ParameterFormatterFilter.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.param.ParamNames; -import com.gitee.sop.gatewaycommon.param.ParameterFormatter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.core.Ordered; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -import static com.gitee.sop.gatewaycommon.gateway.filter.Orders.PARAMETER_FORMATTER_FILTER_ORDER; - -/** - * @author 六如 - */ -@Slf4j -public class ParameterFormatterFilter implements GlobalFilter, Ordered { - - @Autowired(required = false) - private ParameterFormatter parameterFormatter; - - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); - if (apiParam == null) { - return chain.filter(exchange); - } - // 校验成功后进行参数转换 - if (parameterFormatter != null) { - ServerWebExchange formatExchange = ServerWebExchangeUtil.format( - exchange - , apiParam - , parameterFormatter::format - , httpHeaders -> httpHeaders.set(ParamNames.HEADER_VERSION_NAME, apiParam.fetchVersion())); - return chain.filter(formatExchange); - } - return chain.filter(exchange); - } - - @Override - public int getOrder() { - return PARAMETER_FORMATTER_FILTER_ORDER; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java deleted file mode 100644 index 49679592..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.handler; - -import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.bean.TargetRoute; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.result.ResultExecutor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.codec.HttpMessageReader; -import org.springframework.http.codec.HttpMessageWriter; -import org.springframework.util.Assert; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import org.springframework.web.reactive.result.view.ViewResolver; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -import java.util.Collections; -import java.util.List; - -/** - * 统一异常处理 - * - * @author 六如 - */ -public class GatewayExceptionHandler implements ErrorWebExceptionHandler { - - private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); - - - /** - * MessageReader - */ - private List> messageReaders = Collections.emptyList(); - - /** - * MessageWriter - */ - private List> messageWriters = Collections.emptyList(); - - /** - * ViewResolvers - */ - private List viewResolvers = Collections.emptyList(); - - @Override - public Mono handle(ServerWebExchange exchange, Throwable ex) { - ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); - String errorResult = resultExecutor.buildErrorResult(exchange, ex); - ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); - // 错误记录 - Object routeDefinition = exchange.getAttribute(SopConstants.CACHE_ROUTE_INFO); - log.error("网关报错,参数:{}, 错误信息:{}, 路由信息:{}", apiParam, ex.getMessage(), routeDefinition, ex); - // 参考AbstractErrorWebExceptionHandler - if (exchange.getResponse().isCommitted()) { - return Mono.error(ex); - } - ServerRequest newRequest = ServerRequest.create(exchange, this.messageReaders); - return RouterFunctions.route(RequestPredicates.all(), (serverRequest) -> this.renderErrorResponse(errorResult)).route(newRequest) - .switchIfEmpty(Mono.error(ex)) - .flatMap((handler) -> handler.handle(newRequest)) - .flatMap((response) -> write(exchange, response)); - - } - - /** - * 参考AbstractErrorWebExceptionHandler - * - * @param messageReaders messageReaders - */ - public void setMessageReaders(List> messageReaders) { - Assert.notNull(messageReaders, "'messageReaders' must not be null"); - this.messageReaders = messageReaders; - } - - /** - * 参考AbstractErrorWebExceptionHandler - * - * @param viewResolvers viewResolvers - */ - public void setViewResolvers(List viewResolvers) { - this.viewResolvers = viewResolvers; - } - - /** - * 参考AbstractErrorWebExceptionHandler - * - * @param messageWriters messageWriters - */ - public void setMessageWriters(List> messageWriters) { - Assert.notNull(messageWriters, "'messageWriters' must not be null"); - this.messageWriters = messageWriters; - } - - - /** - * 参考DefaultErrorWebExceptionHandler - * - * @param result 返回结果 - * @return 返回mono - */ - protected Mono renderErrorResponse(String result) { - return ServerResponse - .status(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .body(BodyInserters.fromObject(result)); - } - - /** - * 参考AbstractErrorWebExceptionHandler - * - * @param exchange exchange - * @param response response - * @return 返回Mono - */ - private Mono write(ServerWebExchange exchange, - ServerResponse response) { - exchange.getResponse().getHeaders() - .setContentType(response.headers().getContentType()); - return response.writeTo(exchange, new ResponseContext()); - } - - /** - * 参考AbstractErrorWebExceptionHandler - */ - private class ResponseContext implements ServerResponse.Context { - - @Override - public List> messageWriters() { - return GatewayExceptionHandler.this.messageWriters; - } - - @Override - public List viewResolvers() { - return GatewayExceptionHandler.this.viewResolvers; - } - - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/BizResultHandler.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/BizResultHandler.java deleted file mode 100644 index 655772dd..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/BizResultHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.result; - -import com.alibaba.fastjson.JSONObject; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import org.springframework.web.server.ServerWebExchange; - -import java.util.Map; - -/** - * 处理业务返回数据 - * @author thc - */ -public interface BizResultHandler { - - /** - * 处理业务参数 - * @param serviceData 外层service数据 - * @param serviceObj 业务数据 - * @param apiParam apiParam - * @param request ServerWebExchange - * @author thc - * @date 2021/7/7 10:41 - */ - void handle(Map serviceData, JSONObject serviceObj, ApiParam apiParam, ServerWebExchange request); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResult.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResult.java deleted file mode 100644 index a0b9382d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResult.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.result; - -import lombok.Data; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; - -/** - * @author 六如 - */ -@Data -public class GatewayResult { - private HttpStatus httpStatus; - private MediaType contentType; - private String body; - - public GatewayResult(HttpStatus httpStatus, MediaType contentType, String body) { - this.httpStatus = httpStatus; - this.contentType = contentType; - this.body = body; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java deleted file mode 100644 index 47c42749..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.result; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.exception.ApiException; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext; -import com.gitee.sop.gatewaycommon.message.Error; -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter; -import com.gitee.sop.gatewaycommon.result.ResultExecutorForGateway; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.gateway.support.TimeoutException; -import org.springframework.http.HttpStatus; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.util.UriUtils; - -import java.nio.charset.StandardCharsets; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; - - -/** - * @author 六如 - */ -@Slf4j -public class GatewayResultExecutor extends BaseExecutorAdapter - implements ResultExecutorForGateway { - - @Override - public int getResponseStatus(ServerWebExchange exchange) { - HttpStatus statusCode = exchange.getResponse().getStatusCode(); - return ServerWebExchangeUtil.getHeaderValue(exchange, SopConstants.X_SERVICE_ERROR_CODE) - .map(Integer::parseInt) - .orElse(statusCode.value()); - } - - @Override - public Optional getServiceResultForError(ServerWebExchange exchange, int status) { - if (status == HttpStatus.OK.value()) { - return Optional.empty(); - } - return ServerWebExchangeUtil.getHeaderValue(exchange, SopConstants.X_SERVICE_ERROR_RESPONSE); - } - - @Override - public String getResponseErrorMessage(ServerWebExchange exchange) { - return ServerWebExchangeUtil.getHeaderValue(exchange, SopConstants.X_SERVICE_ERROR_MESSAGE) - .map(msg -> UriUtils.decode(msg, StandardCharsets.UTF_8)) - .orElse(null); - } - - @Override - public ApiParam getApiParam(ServerWebExchange exchange) { - return ServerWebExchangeUtil.getApiParam(exchange); - } - - @Override - protected Locale getLocale(ServerWebExchange exchange) { - return exchange.getLocaleContext().getLocale(); - } - - @Override - protected RouteInterceptorContext getRouteInterceptorContext(ServerWebExchange exchange) { - RouteInterceptorContext routeInterceptorContext = exchange.getAttribute(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT); - ServiceInstance serviceInstance = exchange.getAttribute(SopConstants.TARGET_SERVICE); - DefaultRouteInterceptorContext context = (DefaultRouteInterceptorContext) routeInterceptorContext; - context.setServiceInstance(serviceInstance); - return routeInterceptorContext; - } - - @Override - protected void bindRouteInterceptorContextProperties(RouteInterceptorContext routeInterceptorContext, ServerWebExchange requestContext) { - } - - @Override - public String buildErrorResult(ServerWebExchange exchange, Throwable ex) { - Locale locale = getLocale(exchange); - Error error; - if (ex.getCause() instanceof TimeoutException) { - error = ErrorEnum.ISP_GATEWAY_RESPONSE_TIMEOUT.getErrorMeta().getError(locale); - } else if (ex instanceof ApiException) { - ApiException apiException = (ApiException) ex; - error = apiException.getError(locale); - } else { - error = ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getError(locale); - } - JSONObject jsonObject = (JSONObject) JSON.toJSON(error); - return this.merge(exchange, jsonObject); - } - - @Override - protected void handleBizResult(Map serviceData, JSONObject serviceObj, ApiParam apiParam, ServerWebExchange request) { - ApiConfig.getInstance().getBizResultHandler().handle(serviceData, serviceObj, apiParam, request); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayForwardChooser.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayForwardChooser.java deleted file mode 100644 index 26f7809b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayForwardChooser.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.route; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.bean.TargetRoute; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.route.BaseForwardChooser; -import com.gitee.sop.gatewaycommon.route.ForwardInfo; -import org.springframework.web.server.ServerWebExchange; - -/** - * @author 六如 - */ -public class GatewayForwardChooser extends BaseForwardChooser { - - @Override - public ApiParam getApiParam(ServerWebExchange exchange) { - return ServerWebExchangeUtil.getApiParam(exchange); - } - - @Override - public ForwardInfo getForwardInfo(ServerWebExchange exchange) { - // 如果有异常,直接跳转到异常处理 - if (ServerWebExchangeUtil.getThrowable(exchange) != null) { - return ForwardInfo.getErrorForwardInfo(); - } - ForwardInfo forwardInfo = super.getForwardInfo(exchange); - TargetRoute targetRoute = forwardInfo.getTargetRoute(); - exchange.getAttributes().put(SopConstants.CACHE_ROUTE_INFO, targetRoute.getRouteDefinition()); - return forwardInfo; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteCache.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteCache.java deleted file mode 100644 index 4cb9313a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteCache.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.route; - -import com.alibaba.fastjson.JSON; -import com.gitee.sop.gatewaycommon.bean.RouteDefinition; -import com.gitee.sop.gatewaycommon.bean.ServiceDefinition; -import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; -import com.gitee.sop.gatewaycommon.manager.RouteLoader; -import com.gitee.sop.gatewaycommon.manager.RouteRepository; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.DigestUtils; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -/** - * @author 六如 - */ -@Slf4j -public class GatewayRouteCache implements RouteLoader { - - /** - * KEY:serviceId, value: md5 - */ - private Map serviceIdMd5Map = new HashMap<>(); - - private RouteRepository routeRepository; - - public GatewayRouteCache(RouteRepository routeRepository) { - this.routeRepository = routeRepository; - } - - @Override - public void load(ServiceRouteInfo serviceRouteInfo, Consumer callback) { - try { - String serviceId = serviceRouteInfo.getServiceId(); - String newMd5 = serviceRouteInfo.getMd5(); - String oldMd5 = serviceIdMd5Map.get(serviceId); - if (Objects.equals(newMd5, oldMd5)) { - return; - } - serviceIdMd5Map.put(serviceId, newMd5); - - List routeDefinitionList = serviceRouteInfo.getRouteDefinitionList(); - for (RouteDefinition routeDefinition : routeDefinitionList) { - this.add(serviceId, routeDefinition); - if (log.isDebugEnabled()) { - log.debug("新增路由:{}", JSON.toJSONString(routeDefinition)); - } - } - this.refresh(); - callback.accept(null); - } catch (Exception e) { - log.error("加载路由信息失败,serviceRouteInfo:{}", serviceRouteInfo, e); - } - } - - /** - * 添加路由信息到本地缓存,这里添加后Spring Cloud Gateway并不会识别路由,需要调用refresh()方法 - * - * @see #refresh() - * @param serviceId 服务id - * @param routeDefinition 路由信息 - */ - public void add(String serviceId, RouteDefinition routeDefinition) { - GatewayTargetRoute targetRoute = new GatewayTargetRoute(new ServiceDefinition(serviceId), routeDefinition); - routeRepository.add(targetRoute); - } - - /** - * 刷新路由到Spring Cloud Gateway路由管理器当中 - */ - public void refresh() { - this.routeRepository.refresh(); - } - - - @Override - public void remove(String serviceId) { - serviceIdMd5Map.remove(serviceId); - routeRepository.deleteAll(serviceId); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java deleted file mode 100644 index b89b5282..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.route; - -import com.gitee.sop.gatewaycommon.bean.RouteDefinition; -import com.gitee.sop.gatewaycommon.manager.RouteRepository; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.gateway.event.RefreshRoutesEvent; -import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory; -import org.springframework.cloud.gateway.route.Route; -import org.springframework.cloud.gateway.route.RouteLocator; -import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; -import org.springframework.context.ApplicationContext; -import reactor.core.publisher.Flux; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static java.util.Collections.synchronizedMap; - -/** - * 路由存储管理,负责动态更新路由 - * - * @author 六如 - */ -@Slf4j -public class GatewayRouteRepository implements RouteRepository, RouteLocator { - - private static final Map routes = synchronizedMap(new LinkedHashMap<>()); - - @Autowired - private RouteLocatorBuilder routeLocatorBuilder; - - @Autowired - private ApplicationContext applicationContext; - - private volatile RouteLocator routeLocator; - - @Override - public Flux getRoutes() { - if (routeLocator == null) { - return Flux.empty(); - } - return routeLocator.getRoutes(); - } - - public void refresh() { - RouteLocatorBuilder.Builder builder = routeLocatorBuilder.routes(); - routes.values().forEach(gatewayTargetRoute -> { - RouteDefinition routeDefinition = gatewayTargetRoute.getRouteDefinition(); - RewritePathGatewayFilterFactory rewritePathGatewayFilterFactory = new RewritePathGatewayFilterFactory(); - RewritePathGatewayFilterFactory.Config config = new RewritePathGatewayFilterFactory.Config(); - config.setRegexp(gatewayTargetRoute.getFullPath()); - config.setReplacement(routeDefinition.getPath()); - String serviceId = gatewayTargetRoute.getServiceDefinition().getServiceId(); - String path = routeDefinition.getPath(); - if (!path.startsWith("/")) { - path = "/" + path; - } - String finalPath = "/" + serviceId + path; - builder.route(routeDefinition.getId(), - r -> r.path(finalPath) - // path匹配 - .filters(gatewayFilterSpec -> gatewayFilterSpec.filter(rewritePathGatewayFilterFactory.apply(config))) - .uri(routeDefinition.getUri()) - ); - }); - this.routeLocator = builder.build(); - // 触发 - applicationContext.publishEvent(new RefreshRoutesEvent(new Object())); - } - - /** - * 根据ID获取路由 - */ - @Override - public GatewayTargetRoute get(String id) { - if (id == null) { - return null; - } - return routes.get(id); - } - - - @Override - public Collection getAll() { - return routes.values(); - } - - /** - * 增加路由 - */ - @Override - public String add(GatewayTargetRoute targetRoute) { - RouteDefinition routeDefinition = targetRoute.getRouteDefinition(); - routes.put(routeDefinition.getId(), targetRoute); - return "success"; - } - - @Override - public void update(GatewayTargetRoute targetRoute) { - RouteDefinition baseRouteDefinition = targetRoute.getRouteDefinition(); - routes.put(baseRouteDefinition.getId(), targetRoute); - } - - /** - * 删除路由 - */ - @Override - public void delete(String id) { - routes.remove(id); - } - - @Override - public void deleteAll(String serviceId) { - List idList = routes.values().stream() - .filter(zuulTargetRoute -> StringUtils.equalsIgnoreCase(serviceId, zuulTargetRoute.getServiceDefinition().getServiceId())) - .map(zuulTargetRoute -> zuulTargetRoute.getRouteDefinition().getId()) - .collect(Collectors.toList()); - - for (String id : idList) { - this.delete(id); - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayTargetRoute.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayTargetRoute.java deleted file mode 100644 index 466c8d9e..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayTargetRoute.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.route; - -import com.gitee.sop.gatewaycommon.bean.RouteDefinition; -import com.gitee.sop.gatewaycommon.bean.ServiceDefinition; -import com.gitee.sop.gatewaycommon.bean.TargetRoute; -import org.springframework.util.StringUtils; - -/** - * @author 六如 - */ -public class GatewayTargetRoute implements TargetRoute { - - private ServiceDefinition serviceDefinition; - private RouteDefinition routeDefinition; - - - public GatewayTargetRoute(ServiceDefinition serviceDefinition, RouteDefinition routeDefinition) { - this.serviceDefinition = serviceDefinition; - this.routeDefinition = routeDefinition; - } - - @Override - public String getFullPath() { - String serviceId = serviceDefinition.getServiceId(); - String path = StringUtils.trimLeadingCharacter(routeDefinition.getPath(), '/'); - return "/" + serviceId + "/" + path; - } - - @Override - public ServiceDefinition getServiceDefinition() { - return serviceDefinition; - } - - @Override - public RouteDefinition getRouteDefinition() { - return routeDefinition; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/RouteInterceptor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/RouteInterceptor.java deleted file mode 100644 index 99e0dada..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/RouteInterceptor.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.sop.gatewaycommon.interceptor; - -/** - * 路由拦截器 - * - * @author 六如 - */ -public interface RouteInterceptor { - - /** - * 在路由转发前执行,签名校验通过后会立即执行此方法 - * - * @param context context - */ - void preRoute(RouteInterceptorContext context); - - /** - * 微服务返回结果后执行,可能返回成功,也可能返回失败结果, - * 可通过 {@link RouteInterceptorContext#getResponseStatus()} 来判断是否返回正确结果。 - * - * @param context context - */ - void afterRoute(RouteInterceptorContext context); - - /** - * 拦截器执行顺序,值小优先执行,建议从0开始,小于0留给系统使用 - * - * @return 返回顺序 - */ - int getOrder(); - - /** - * 是否匹配,返回true执行拦截器,默认true - * @param context context - * @return 返回true执行拦截器 - */ - default boolean match(RouteInterceptorContext context) { - return true; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/RouteInterceptorContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/RouteInterceptorContext.java deleted file mode 100644 index 4420597c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/interceptor/RouteInterceptorContext.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.gitee.sop.gatewaycommon.interceptor; - -import com.gitee.sop.gatewaycommon.param.ApiParam; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.http.HttpStatus; - -/** - * 拦截器参数 - * - * @author 六如 - */ -public interface RouteInterceptorContext { - - /** - * 返回ApiParam - * - * @return 返回ApiParam - */ - ApiParam getApiParam(); - - /** - * 获取微服务返回的内容 - * - * @return 微服务返回内容 - */ - String getServiceResult(); - - /** - * 获取微服务端的错误信息,status为200时,返回null。 - * - * @return 返回错误信息 - */ - String getServiceErrorMsg(); - - /** - * 获取微服务返回状态码 - * - * @return 返回状态码,正确为200,错误为非200 - */ - int getResponseStatus(); - - /** - * 获取路由开始时间 - * - * @return 返回开始时间 - */ - long getBeginTimeMillis(); - - /** - * 获取路由结束时间 - * - * @return 返回结束时间 - */ - long getFinishTimeMillis(); - - /** - * 获取上下文信息,zuul返回RequestContext,Gateway返回ServerWebExchange - * - * @return 返回上下文对象 - */ - Object getRequestContext(); - - /** - * 获取目标微服务实例,即具体选中哪台实例 - * - * @return 返回目标微服务 - */ - ServiceInstance getServiceInstance(); - - /** - * 获取请求内容大小 - * - * @return 返回请求内容大小 - */ - long getRequestDataSize(); - - /** - * 获取返回结果内容大小 - * @return 返回返回结果内容大小 - */ - long getResponseDataSize(); - - /** - * 是否是成功请求,微服务主动抛出的异常也算作成功,JSR303校验失败也算作成功。 - * 只有微服务返回未知的错误算作失败。 - * @return true:成功请求 - */ - default boolean isSuccessRequest() { - int responseStatus = getResponseStatus(); - return responseStatus == HttpStatus.OK.value(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java deleted file mode 100644 index 33bedf84..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gitee.sop.gatewaycommon.limit; - -import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; -import com.google.common.cache.LoadingCache; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicLong; - -/** - * @author 六如 - */ -@Slf4j -public class DefaultLimitManager implements LimitManager { - - @Override - public double acquireToken(ConfigLimitDto routeConfig) { - if (routeConfig.getLimitStatus() == ConfigLimitDto.LIMIT_STATUS_CLOSE) { - return 0; - } - if (LimitType.LEAKY_BUCKET.getType() == routeConfig.getLimitType().byteValue()) { - return 0; - } - return routeConfig.fetchRateLimiter().acquire(); - } - - - @Override - public boolean acquire(ConfigLimitDto routeConfig) { - if (routeConfig.getLimitStatus() == ConfigLimitDto.LIMIT_STATUS_CLOSE) { - return true; - } - if (LimitType.TOKEN_BUCKET.getType() == routeConfig.getLimitType()) { - return true; - } - int execCountPerSecond = routeConfig.getExecCountPerSecond(); - try { - LoadingCache counter = routeConfig.getCounter(); - // 被限流了 - return counter.get(routeConfig.getId()).incrementAndGet() <= execCountPerSecond; - } catch (ExecutionException e) { - log.error("窗口限流出错,routeConfig:{}", routeConfig, e); - return false; - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java deleted file mode 100644 index 35f83ff8..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gitee.sop.gatewaycommon.limit; - -import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; - -/** - * 限流管理 - * @author 六如 - */ -public interface LimitManager { - - /** - * 从令牌桶中获取令牌,如果使用{@link LimitType#TOKEN_BUCKET - * RateType.TOKEN_BUCKET}限流策略,则该方法生效 - * - * @param routeConfig 路由配置 - * @return 返回耗时时间,秒 - */ - double acquireToken(ConfigLimitDto routeConfig); - - /** - * 是否需要限流,如果使用{@link LimitType#LEAKY_BUCKET - * RateType.LIMIT}限流策略,则该方法生效 - * - * @param routeConfig 路由配置 - * @return 如果返回true,表示可以执行业务代码,返回false则需要限流 - */ - boolean acquire(ConfigLimitDto routeConfig); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java deleted file mode 100644 index 77821081..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gitee.sop.gatewaycommon.limit; - -/** - * 限流策略 - * - * @author 六如 - */ -public enum LimitType { - /** - * 窗口策略。每秒处理固定数量的请求,超出请求返回错误信息。 - */ - LEAKY_BUCKET(1), - /** - * 令牌桶策略,每秒放置固定数量的令牌数,每个请求进来后先去拿令牌,拿到了令牌才能继续,拿不到则等候令牌重新生成了再拿。 - */ - TOKEN_BUCKET(2); - - private byte type; - - LimitType(int type) { - this.type = (byte)type; - } - - public byte getType() { - return type; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/RedisLimitManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/RedisLimitManager.java deleted file mode 100644 index 63f68010..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/RedisLimitManager.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.gitee.sop.gatewaycommon.limit; - -import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.IOUtils; -import org.springframework.core.io.ClassPathResource; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.core.script.RedisScript; -import org.springframework.util.Assert; - -import java.util.Collections; - -/** - * 基于redis限流管理 - * - * @author 六如 - */ -public class RedisLimitManager extends DefaultLimitManager { - - /** - * 限流脚本 - */ - private static final String DEFAULT_LIMIT_LUA_FILE_PATH = "/sop/limit.lua"; - - private static final Long REDIS_SUCCESS = 1L; - - private StringRedisTemplate redisTemplate; - private String limitScript; - private String limitScriptSha1; - - public RedisLimitManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { - Assert.notNull(redisTemplate, "redisTemplate不能为null"); - this.redisTemplate = new StringRedisTemplate(redisTemplate.getConnectionFactory()); - ClassPathResource limitLua = new ClassPathResource(getLimitLuaFilePath()); - try { - this.limitScript = IOUtils.toString(limitLua.getInputStream(), SopConstants.UTF8); - this.limitScriptSha1 = DigestUtils.sha1Hex(this.limitScript); - } catch (Exception e) { - throw new RuntimeException("读取脚本文件失败,脚本路径:" + getLimitLuaFilePath(), e); - } - } - - public String getLimitLuaFilePath() { - return DEFAULT_LIMIT_LUA_FILE_PATH; - } - - @Override - public boolean acquire(ConfigLimitDto routeConfig) { - String key = "sop:lmt:" + routeConfig.getRouteId(); - int limitCount = routeConfig.getExecCountPerSecond(); - int duration = routeConfig.fetchDuration(); - Object result = redisTemplate.execute( - new RedisScript() { - @Override - public String getSha1() { - return limitScriptSha1; - } - - @Override - public Class getResultType() { - return Long.class; - } - - @Override - public String getScriptAsString() { - return limitScript; - } - }, - // KEYS[1] key - Collections.singletonList(key), - // ARGV[1] limit - String.valueOf(limitCount), - // ARGV[2] expire - String.valueOf(duration) - ); - return REDIS_SUCCESS.equals(result); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/GrayLoadBalancer.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/GrayLoadBalancer.java deleted file mode 100644 index f0e91f76..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/GrayLoadBalancer.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.gitee.sop.gatewaycommon.loadbalancer; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import lombok.Data; -import org.apache.commons.lang3.ArrayUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.*; -import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier; -import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; -import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback; -import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; -import org.springframework.http.HttpHeaders; -import org.springframework.util.StringUtils; -import reactor.core.publisher.Mono; - -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; - -@Data -public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer { - private static final Logger log = LoggerFactory.getLogger(GrayLoadBalancer.class); - private final String serviceId; - private AtomicInteger position; // 位置,下标 - private ObjectProvider serviceInstanceListSupplierProvider; - - public GrayLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider, String serviceId) { - this.serviceId = serviceId; - this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; - this.position = new AtomicInteger(new Random().nextInt(1000)); //随机进行设置一个值 - } - - @Override - public Mono> choose(Request request) { - // 提供备选的服务实例列表 - ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new); - // 选择服务实例 - return supplier.get(request).next().map((serviceInstances) -> this.processInstanceResponse(supplier, serviceInstances, request)); - } - - private Response processInstanceResponse(ServiceInstanceListSupplier supplier, - List serviceInstances, - Request request) { - // 从备选的服务列表中选择一个具体的服务实例 - Response serviceInstanceResponse = this.getInstanceResponse(serviceInstances, - request); - if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { - ((SelectedInstanceCallback) supplier).selectedServiceInstance((ServiceInstance) serviceInstanceResponse.getServer()); - } - return serviceInstanceResponse; - } - - private Response getInstanceResponse(List instances, - Request request) { - // 实例为空 首先进行实例判空 - if (instances.isEmpty()) { - if (log.isWarnEnabled()) { - //判空后 给予警告 - log.warn("No servers available for service: " + this.serviceId); - } - //返回响应 - return new EmptyResponse(); - } else { - // 存放预发服务器 - List preServers = new ArrayList<>(4); - // 存放灰度发布服务器 - List grayServers = new ArrayList<>(4); - // 存放非预发服务器 - List notPreServers = new ArrayList<>(4); - - notPreServers.addAll(grayServers); - - for (ServiceInstance instance : instances) { - // 获取实例metadata - Map metadata = instance.getMetadata(); - // 是否开启了预发模式 - if (this.isPreServer(metadata)) { - preServers.add(instance); - } else if (this.isGrayServer(metadata)) { - grayServers.add(instance); - } else { - notPreServers.add(instance); - } - } - notPreServers.addAll(grayServers); - - RequestDataContext context = (RequestDataContext) request.getContext(); - ApiParam apiParam = new ApiParam(); - apiParam.putAll(context.getClientRequest().getAttributes()); - - // 如果没有开启预发布服务和灰度发布,直接用默认的方式 - if (preServers.isEmpty() && grayServers.isEmpty()) { - int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; - ServiceInstance instance = instances.get(pos % instances.size()); - return new DefaultResponse(instance); - } - // 如果是从预发布域名访问过来,则认为是预发布请求,选出预发服务器 - if (this.isRequestFromPreDomain(context.getClientRequest().getHeaders())) { - int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; - ServiceInstance instance = preServers.get(pos % instances.size()); - return new DefaultResponse(instance); - } - // 如果是灰度请求,则认为是灰度用户,选出灰度服务器 - if (apiParam.fetchGrayRequest()) { - int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; - ServiceInstance instance = grayServers.get(pos % instances.size()); - return new DefaultResponse(instance); - } - return getInstanceResponse(instances); - } - } - - /** - * 是否是预发布服务器 - * - * @param metadata metadata - * @return true:是 - */ - private boolean isPreServer(Map metadata) { - return Objects.equals(metadata.get(SopConstants.METADATA_ENV_KEY), SopConstants.METADATA_ENV_PRE_VALUE); - } - - /** - * 是否是灰度发布服务器 - * - * @param metadata metadata - * @return true:是 - */ - private boolean isGrayServer(Map metadata) { - return Objects.equals(metadata.get(SopConstants.METADATA_ENV_KEY), SopConstants.METADATA_ENV_GRAY_VALUE); - } - - /** - * 通过判断hostname来确定是否是预发布请求 - * - * @param httpHeaders - * @return 返回true:可以进入到预发环境 - */ - boolean isRequestFromPreDomain(HttpHeaders httpHeaders) { - String domain = EnvironmentKeys.PRE_DOMAIN.getValue(); - if (StringUtils.isEmpty(domain)) { - return false; - } - String[] domains = domain.split("\\,"); - if (httpHeaders.getHost() != null) { - String host = httpHeaders.getHost().getHostName(); - return ArrayUtils.contains(domains, host); - } else { - return false; - } - } - - public Response getInstanceResponse(List instances) { - if (instances.isEmpty()) { - if (log.isWarnEnabled()) { - log.warn("No servers available for service: " + serviceId); - } - return new EmptyResponse(); - } - - // Do not move position when there is only 1 instance, especially some suppliers - // have already filtered instances - if (instances.size() == 1) { - return new DefaultResponse(instances.get(0)); - } - - // Ignore the sign bit, this allows pos to loop sequentially from 0 to - // Integer.MAX_VALUE - int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; - - ServiceInstance instance = instances.get(pos % instances.size()); - - return new DefaultResponse(instance); - } -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/GrayLoadBalancerConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/GrayLoadBalancerConfig.java deleted file mode 100644 index 9ce6048c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/GrayLoadBalancerConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.gitee.sop.gatewaycommon.loadbalancer; - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; -import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; -import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; - -@Configuration -public class GrayLoadBalancerConfig { - @Bean - public ReactorLoadBalancer GrayLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { - String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); - return new GrayLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); - } -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/ServiceGrayConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/ServiceGrayConfig.java deleted file mode 100644 index 89356674..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/ServiceGrayConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gitee.sop.gatewaycommon.loadbalancer; - -import lombok.Data; - -import java.util.Map; -import java.util.Set; - -/** - * @author 六如 - */ -@Data -public class ServiceGrayConfig { - - private String serviceId; - - /** - * 用户id - */ - private Set userKeys; - - /** 存放接口隐射关系,key:nameversion,value:newVersion */ - private Map grayNameVersion; - - public boolean containsKey(Object userKey) { - return userKeys.contains(String.valueOf(userKey)); - } - - public String getVersion(String name) { - return grayNameVersion.get(name); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/AppIdGrayUserBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/AppIdGrayUserBuilder.java deleted file mode 100644 index 1a6454d0..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/AppIdGrayUserBuilder.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.gitee.sop.gatewaycommon.loadbalancer.builder; - -import com.gitee.sop.gatewaycommon.param.ApiParam; - -/** - * @author 六如 - */ -public class AppIdGrayUserBuilder implements GrayUserBuilder { - - @Override - public String buildGrayUserKey(ApiParam apiParam) { - return apiParam.fetchAppKey(); - } - - @Override - public int order() { - return 0; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/GrayUserBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/GrayUserBuilder.java deleted file mode 100644 index 63ad293d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/GrayUserBuilder.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gitee.sop.gatewaycommon.loadbalancer.builder; - -import com.gitee.sop.gatewaycommon.param.ApiParam; - -/** - * @author 六如 - */ -public interface GrayUserBuilder { - - /** - * 获取灰度用户key - * - * @param apiParam apiParam - * @return 返回用户key - */ - String buildGrayUserKey(ApiParam apiParam); - - /** - * 优先级,数字小优先 - * - * @return 返回数字 - */ - int order(); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/IpGrayUserBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/IpGrayUserBuilder.java deleted file mode 100644 index 011c437a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/builder/IpGrayUserBuilder.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.gitee.sop.gatewaycommon.loadbalancer.builder; - -import com.gitee.sop.gatewaycommon.param.ApiParam; - -/** - * @author 六如 - */ -public class IpGrayUserBuilder implements GrayUserBuilder { - - @Override - public String buildGrayUserKey(ApiParam apiParam) { - return apiParam.fetchIp(); - } - - @Override - public int order() { - return 1; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ChannelMsgProcessor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ChannelMsgProcessor.java deleted file mode 100644 index 5dce47f7..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ChannelMsgProcessor.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.ChannelMsg; - -/** - * @author 六如 - */ -public interface ChannelMsgProcessor { - default void process(ChannelMsg channelMsg) { - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java deleted file mode 100644 index 1bc19bff..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.loadbalancer.ServiceGrayConfig; -import com.google.common.collect.Maps; - -import java.util.Map; - -/** - * @author 六如 - */ -public class DefaultEnvGrayManager implements EnvGrayManager { - - /** - * key:serviceId,服务对应的灰度配置 - */ - private final Map serviceGrayConfigMap = Maps.newConcurrentMap(); - - /** - * key:instanceId value:serviceId - */ - private final Map instanceIdServiceIdMap = Maps.newConcurrentMap(); - - @Override - public void saveServiceGrayConfig(ServiceGrayConfig serviceGrayConfig) { - serviceGrayConfigMap.put(serviceGrayConfig.getServiceId(), serviceGrayConfig); - } - - @Override - public boolean containsKey(String serviceId, Object userKey) { - if (serviceId == null || userKey == null) { - return false; - } - ServiceGrayConfig grayConfig = this.getGrayConfig(serviceId); - return grayConfig != null && grayConfig.containsKey(userKey); - } - - @Override - public String getVersion(String serviceId, String nameVersion) { - if (serviceId == null || nameVersion == null) { - return null; - } - boolean opened = instanceIdServiceIdMap.containsValue(serviceId); - // 没有开启灰度 - if (!opened) { - return null; - } - ServiceGrayConfig grayConfig = this.getGrayConfig(serviceId); - return grayConfig != null ? grayConfig.getVersion(nameVersion) : null; - } - - private ServiceGrayConfig getGrayConfig(String serviceId) { - if (serviceId == null || !instanceIdServiceIdMap.containsValue(serviceId)) { - return null; - } - return serviceGrayConfigMap.get(serviceId); - } - - @Override - public void openGray(String instanceId, String serviceId) { - instanceIdServiceIdMap.putIfAbsent(instanceId, serviceId); - } - - @Override - public void closeGray(String instanceId) { - instanceIdServiceIdMap.remove(instanceId); - } - - @Override - public void load() { - - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultIPBlacklistManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultIPBlacklistManager.java deleted file mode 100644 index 7d02bce0..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultIPBlacklistManager.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.google.common.collect.Sets; -import org.apache.commons.lang3.StringUtils; - -import java.util.Set; - -/** - * ip黑名单管理 - * @author 六如 - */ -public class DefaultIPBlacklistManager implements IPBlacklistManager { - - private static Set ipList = Sets.newConcurrentHashSet(); - - @Override - public void add(String ip) { - ipList.add(ip); - } - - @Override - public void remove(String ip) { - ipList.remove(ip); - } - - @Override - public boolean contains(String ip) { - if (StringUtils.isBlank(ip)) { - return false; - } - return ipList.contains(ip); - } - - @Override - public void load() { - - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultIsvRoutePermissionManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultIsvRoutePermissionManager.java deleted file mode 100644 index a37b6bb8..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultIsvRoutePermissionManager.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.IsvRoutePermission; -import org.apache.commons.lang3.StringUtils; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author 六如 - */ -public class DefaultIsvRoutePermissionManager implements IsvRoutePermissionManager { - - /** key: appKey */ - protected static Map isvRoutePermissionMap = new ConcurrentHashMap<>(64); - - @Override - public void load() { - - } - - @Override - public void update(IsvRoutePermission isvRoutePermission) { - IsvRoutePermission routePermission = isvRoutePermissionMap.get(isvRoutePermission.getAppKey()); - if (routePermission == null) { - isvRoutePermissionMap.put(isvRoutePermission.getAppKey(), isvRoutePermission); - } else { - if (!StringUtils.equals(isvRoutePermission.getRouteIdListMd5(), routePermission.getRouteIdListMd5())) { - isvRoutePermissionMap.put(isvRoutePermission.getAppKey(), isvRoutePermission); - } - } - } - - @Override - public boolean hasPermission(String appKey, String routeId) { - IsvRoutePermission routePermission = isvRoutePermissionMap.get(appKey); - if (routePermission == null) { - return false; - } - return routePermission.getRouteIdList().contains(routeId); - } - - @Override - public void remove(String appKey) { - isvRoutePermissionMap.remove(appKey); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultLimitConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultLimitConfigManager.java deleted file mode 100644 index 48946147..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultLimitConfigManager.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; -import org.apache.commons.lang3.StringUtils; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author 六如 - */ -public class DefaultLimitConfigManager implements LimitConfigManager { - - public static final int LIMIT_STATUS_CLOSED = 0; - /** - * key: limitKey - */ - protected static Map limitCache = new ConcurrentHashMap<>(); - - protected static Map> idKeyMap = new HashMap<>(); - - @Override - public void load(String serviceId) { - - } - - @Override - public void update(ConfigLimitDto configLimitDto) { - Long id = configLimitDto.getId(); - this.remove(id); - if (configLimitDto.getLimitStatus().intValue() == LIMIT_STATUS_CLOSED) { - return; - } - configLimitDto.initRateLimiter(); - Set keys = this.buildKeys(configLimitDto); - idKeyMap.put(id, keys); - for (String key : keys) { - this.doUpdate(key, configLimitDto); - } - } - - /** - * // 根据路由ID限流 - * routeId, - * // 根据appKey限流 - * appKey, - * // 根据路由ID + appKey限流 - * routeId + appKey, - * - * // 根据ip限流 - * ip, - * // 根据ip+路由id限流 - * ip + routeId, - * // 根据ip+appKey限流 - * ip + appKey, - * // 根据ip+路由id+appKey限流 - * ip + routeId + appKey, - * @param configLimitDto - * @return - */ - protected Set buildKeys(ConfigLimitDto configLimitDto) { - Set keys = new HashSet<>(); - String routeId = Optional.ofNullable(configLimitDto.getRouteId()).orElse(""); - String appKey = Optional.ofNullable(configLimitDto.getAppKey()).orElse(""); - String limitIp = Optional.ofNullable(configLimitDto.getLimitIp()).orElse("").replaceAll("\\s", ""); - - // 根据路由ID限流 - if (StringUtils.isNotBlank(routeId) && StringUtils.isBlank(appKey) && StringUtils.isBlank(limitIp)) { - keys.add(routeId); - } - // 根据appKey限流 - if (StringUtils.isBlank(routeId) && StringUtils.isNotBlank(appKey) && StringUtils.isBlank(limitIp)) { - keys.add(appKey); - } - // 根据路由ID + appKey限流 - if (StringUtils.isNotBlank(routeId) && StringUtils.isNotBlank(appKey) && StringUtils.isBlank(limitIp)) { - keys.add(routeId.trim() + appKey.trim()); - } - // 根据ip限流 - if (StringUtils.isBlank(routeId) && StringUtils.isBlank(appKey) && StringUtils.isNotBlank(limitIp)) { - String[] ips = limitIp.split("\\,|\\,"); - keys.addAll(Arrays.asList(ips)); - } - if (StringUtils.isNotBlank(limitIp)) { - String[] ips = limitIp.split("\\,|\\,"); - for (String ip : ips) { - // 根据ip+路由id限流 - if (StringUtils.isNotBlank(ip) && StringUtils.isNotBlank(routeId) && StringUtils.isBlank(appKey)) { - keys.add(routeId); - } - // 根据ip+appKey限流 - if (StringUtils.isNotBlank(ip) && StringUtils.isBlank(routeId) && StringUtils.isNotBlank(appKey)) { - keys.add(ip + appKey); - } - // 根据ip+路由id+appKey限流 - if (StringUtils.isNotBlank(ip) && StringUtils.isNotBlank(routeId) && StringUtils.isNotBlank(appKey)) { - keys.add(ip + routeId +appKey); - } - } - } - return keys; - } - - protected void doUpdate(String key, ConfigLimitDto configLimitDto) { - if (StringUtils.isBlank(key)) { - return; - } - limitCache.put(key, configLimitDto); - } - - protected void remove(Long id) { - Set list = idKeyMap.getOrDefault(id, Collections.emptySet()); - for (String key : list) { - limitCache.remove(key); - } - } - - @Override - public ConfigLimitDto get(String limitKey) { - if (StringUtils.isBlank(limitKey)) { - return null; - } - return limitCache.get(limitKey); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java deleted file mode 100644 index 5daaddd6..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.RouteConfig; -import com.gitee.sop.gatewaycommon.bean.RouteStatus; -import com.gitee.sop.gatewaycommon.util.MyBeanUtil; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author 六如 - */ -public class DefaultRouteConfigManager implements RouteConfigManager { - /** - * key: routeId - */ - protected static Map routeConfigMap = new ConcurrentHashMap<>(64); - - private static final RouteConfig DEFAULT_ROUTE_CONFIG; - - static { - DEFAULT_ROUTE_CONFIG = new RouteConfig(); - DEFAULT_ROUTE_CONFIG.setStatus(RouteStatus.ENABLE.getStatus()); - } - - @Override - public void load(String serviceId) { - - } - - @Override - public void update(RouteConfig routeConfig) { - this.doUpdate(routeConfig.getRouteId(), routeConfig); - } - - protected void save(RouteConfig routeConfig) { - Byte status = routeConfig.getStatus(); - if (status == null) { - routeConfig.setStatus(RouteStatus.ENABLE.getStatus()); - } - routeConfigMap.put(routeConfig.getRouteId(), routeConfig); - } - - protected void doUpdate(String routeId, Object res) { - RouteConfig routeConfig = routeConfigMap.get(routeId); - if (routeConfig == null) { - routeConfig = newRouteConfig(); - routeConfig.setRouteId(routeId); - routeConfigMap.put(routeId, routeConfig); - } - MyBeanUtil.copyPropertiesIgnoreNull(res, routeConfig); - } - - protected RouteConfig newRouteConfig() { - return new RouteConfig(); - } - - @Override - public RouteConfig get(String routeId) { - return routeConfigMap.getOrDefault(routeId, DEFAULT_ROUTE_CONFIG); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultServiceErrorManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultServiceErrorManager.java deleted file mode 100644 index a658bfcf..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultServiceErrorManager.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.ErrorDefinition; -import com.gitee.sop.gatewaycommon.bean.ErrorEntity; -import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.beans.BeanUtils; - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author 六如 - */ -public class DefaultServiceErrorManager implements ServiceErrorManager { - - private static Map store = new ConcurrentHashMap<>(128); - - @Override - public Collection listAllErrors() { - return store.values(); - } - - @Override - public void saveBizError(ErrorDefinition errorDefinition) { - - } - - @Override - public void saveUnknownError(ErrorDefinition errorDefinition) { - boolean hasCapacity = store.size() < ApiConfig.getInstance().getStoreErrorCapacity(); - // 这里还可以做其它事情,比如错误量到达一定数目后,自动发送邮件/微信给开发人员,方便及时获取异常情况 - String id = this.buildId(errorDefinition); - ErrorEntity errorEntity = store.get(id); - if (errorEntity == null && hasCapacity) { - errorEntity = new ErrorEntity(); - BeanUtils.copyProperties(errorDefinition, errorEntity); - errorEntity.setId(id); - store.put(id, errorEntity); - } - if (errorEntity != null) { - errorEntity.setCount(errorEntity.getCount() + 1); - } - } - - @Override - public void clear() { - store.clear(); - } - - protected String buildId(ErrorDefinition errorDefinition) { - return DigestUtils.md5Hex(errorDefinition.getServiceId() + errorDefinition.getErrorMsg()); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvGrayManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvGrayManager.java deleted file mode 100644 index 2c4ca1d7..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvGrayManager.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.BeanInitializer; -import com.gitee.sop.gatewaycommon.loadbalancer.ServiceGrayConfig; - -/** - * @author 六如 - */ -public interface EnvGrayManager extends BeanInitializer { - - /** - * 保存灰度配置 - * @param serviceGrayConfig 灰度配置 - */ - void saveServiceGrayConfig(ServiceGrayConfig serviceGrayConfig); - - /** - * 实例是否允许 - * @param serviceId serviceId - * @param userKey 用户key,如appKey - * @return true:允许访问 - */ - boolean containsKey(String serviceId, Object userKey); - - /** - * 获取灰度发布新版本号 - * @param serviceId serviceId - * @param nameVersion 路由id - * @return 返回新版本号 - */ - String getVersion(String serviceId, String nameVersion); - - /** - * 开启灰度 - * @param instanceId instanceId - * @param serviceId serviceId - */ - void openGray(String instanceId, String serviceId); - - /** - * 关闭灰度 - * @param instanceId instanceId - */ - void closeGray(String instanceId); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentContext.java deleted file mode 100644 index 9de8462d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentContext.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import org.springframework.core.env.Environment; - -/** - * @author 六如 - */ -public class EnvironmentContext { - private static Environment environment; - - public static Environment getEnvironment() { - return environment; - } - - public static void setEnvironment(Environment environment) { - EnvironmentContext.environment = environment; - } - - public static String getValue(String key, String defaultValue) { - return environment.getProperty(key, defaultValue); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java deleted file mode 100644 index 8ba239cb..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -public enum EnvironmentKeys { - SPRING_PROFILES_ACTIVE("spring.profiles.active", "default"), - /** - * spring.application.name - */ - SPRING_APPLICATION_NAME("spring.application.name"), - - /** - * sign.urlencode=true,签名验证拼接字符串的value部分进行urlencode - */ - SIGN_URLENCODE("sign.urlencode"), - - /** - * sop.restful.path=/xx ,指定请求前缀,默认/rest - */ - SOP_RESTFUL_PATH("sop.restful.path", "/rest"), - - /** - * 排除其它微服务,多个用英文逗号隔开 - */ - SOP_SERVICE_EXCLUDE("sop.service.exclude"), - /** - * 排除其它微服务,正则形式,多个用英文逗号隔开 - */ - SOP_SERVICE_EXCLUDE_REGEX("sop.service.exclude-regex"), - /** - * 预发布域名 - */ - PRE_DOMAIN("pre.domain"), - - /** - * post请求body缓存大小 - */ - MAX_IN_MEMORY_SIZE("spring.codec.max-in-memory-size", "262144"), - - ; - - private final String key; - private String defaultValue; - - public String getKey() { - return key; - } - - EnvironmentKeys(String key) { - this.key = key; - } - - EnvironmentKeys(String key, String defaultValue) { - this.key = key; - this.defaultValue = defaultValue; - } - - public String getValue() { - return EnvironmentContext.getValue(key, defaultValue); - } - - public String getValue(String defaultValue) { - return EnvironmentContext.getValue(key, defaultValue); - } -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/Formatter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/Formatter.java deleted file mode 100644 index fcff7849..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/Formatter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import java.util.Map; - -/** - * 参数格式化 - * - * @author 六如 - */ -public interface Formatter> { - - /** - * 参数格式化,即动态修改请求参数 - * - * @param requestParams 原始请求参数,在此基础上追加或修改参数 - */ - void format(T requestParams); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IPBlacklistManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IPBlacklistManager.java deleted file mode 100644 index a8063e49..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IPBlacklistManager.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.BeanInitializer; - -/** - * @author 六如 - */ -public interface IPBlacklistManager extends BeanInitializer { - - /** - * 添加IP - * - * @param ip ip - */ - void add(String ip); - - /** - * 移除黑名单IP - * - * @param ip ip - */ - void remove(String ip); - - /** - * ip是否在黑名单中 - * - * @param ip ip - * @return true:在黑名单中 - */ - boolean contains(String ip); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/InstanceManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/InstanceManager.java deleted file mode 100644 index 8c7571f6..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/InstanceManager.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.route.RegistryEvent; - -import java.util.List; - -/** - * @author 六如 - */ -public interface InstanceManager extends RegistryEvent { - - List listInstance(String serviceId); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java deleted file mode 100644 index 2c713b60..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.BeanInitializer; -import com.gitee.sop.gatewaycommon.bean.IsvRoutePermission; - -/** - * @author 六如 - */ -public interface IsvRoutePermissionManager extends BeanInitializer { - - /** - * 加载权限 - * @param isvRoutePermission isvRoutePermission - */ - void update(IsvRoutePermission isvRoutePermission); - - /** - * 判断是否有权限 - * @param appKey appKey - * @param routeId 路由id - * @return true:有 - */ - boolean hasPermission(String appKey, String routeId); - - /** - * 删除权限 - * @param appKey appKey - */ - void remove(String appKey); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/LimitConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/LimitConfigManager.java deleted file mode 100644 index 82eb9603..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/LimitConfigManager.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; -import com.gitee.sop.gatewaycommon.bean.ServiceBeanInitializer; - -/** - * @author 六如 - */ -public interface LimitConfigManager extends ServiceBeanInitializer { - /** - * 更新限流配置 - * @param configLimitDto 路由配置 - */ - void update(ConfigLimitDto configLimitDto); - - /** - * 获取限流配置 - * @param limitKey 路由id - * @return 返回ConfigLimitDto - */ - ConfigLimitDto get(String limitKey); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java deleted file mode 100644 index 80bb3727..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.RouteConfig; -import com.gitee.sop.gatewaycommon.bean.ServiceBeanInitializer; - -/** - * 路由配置管理 - * @author 六如 - */ -public interface RouteConfigManager extends ServiceBeanInitializer { - /** - * 更新路由配置 - * @param routeConfig 路由配置 - */ - void update(RouteConfig routeConfig); - - /** - * 获取路由配置 - * @param routeId 路由id - * @return 返回RouteConfig - */ - RouteConfig get(String routeId); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteLoader.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteLoader.java deleted file mode 100644 index d2ca83b3..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteLoader.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; - -import java.util.function.Consumer; - -/** - * @author 六如 - */ -public interface RouteLoader { - /** - * 加载路由 - * - * @param serviceRouteInfo 服务路由信息 - * @param callback 加载成功后回调 - */ - void load(ServiceRouteInfo serviceRouteInfo, Consumer callback); - - /** - * 移除某个微服务下的所有路由信息 - * - * @param serviceId 服务id - */ - void remove(String serviceId); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteManager.java deleted file mode 100644 index 92740724..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteManager.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -/** - * 管理各服务路由信息 - * @author 六如 - */ -@Deprecated -public interface RouteManager { - - /** - * 刷新素有的微服务接口信息 - */ - void refresh(); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java deleted file mode 100644 index 31045e95..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.TargetRoute; - -import java.util.Collection; - -/** - * @author 六如 - */ -public interface RouteRepository { - /** - * 获取路由信息 - * @param id 路由id - * @return 返回路由信息,找不到返回null - */ - T get(String id); - - /** - * 返回所有路由信息 - * @return 返回所有路由信息 - */ - Collection getAll(); - - /** - * 添加路由 - * @param targetRoute 模板路由对象 - * @return 返回路由id - */ - String add(T targetRoute); - - /** - * 更新路由 - * @param targetRoute 模板路由对象 - */ - void update(T targetRoute); - - /** - * 删除路由 - * @param id 路由id - */ - void delete(String id); - - /** - * 删除service下的所有路由 - * @param serviceId 服务id - */ - void deleteAll(String serviceId); - - /** - * 刷新 - */ - default void refresh() {} -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java deleted file mode 100644 index 10297ba6..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.TargetRoute; - -/** - * @author 六如 - */ -public class RouteRepositoryContext { - - private RouteRepositoryContext() { - } - - private static RouteRepository routeRepository; - - public static RouteRepository getRouteRepository() { - return routeRepository; - } - - public static void setRouteRepository(RouteRepository routeRepository) { - RouteRepositoryContext.routeRepository = routeRepository; - } - - /** - * 根据路由id查询路由 - * - * @param routeId 路由id - * @return 返回路由信息,没有返回null - */ - public static TargetRoute getTargetRoute(String routeId) { - return routeRepository.get(routeId); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceErrorManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceErrorManager.java deleted file mode 100644 index e2a1bc7d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceErrorManager.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.gitee.sop.gatewaycommon.manager; - -import com.gitee.sop.gatewaycommon.bean.ErrorDefinition; -import com.gitee.sop.gatewaycommon.bean.ErrorEntity; - -import java.util.Collection; - -/** - * @author 六如 - */ -public interface ServiceErrorManager { - - /** - * 保存业务错误,一般由开发人员自己throw的异常 - * @param errorDefinition - */ - void saveBizError(ErrorDefinition errorDefinition); - - /** - * 保存未知的错误信息 - * @param errorDefinition - */ - void saveUnknownError(ErrorDefinition errorDefinition); - - /** - * 清除日志 - */ - void clear(); - - /** - * 获取所有错误信息 - * @return - */ - Collection listAllErrors(); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/Error.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/Error.java deleted file mode 100644 index 80ce4be4..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/Error.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gitee.sop.gatewaycommon.message; - -/** - * 定义错误返回 - * code(返回码) - * msg(返回码描述) - * sub_code(明细返回码) - * sub_msg(明细返回码描述) - * 解决方案 - * @author 六如 - */ -public interface Error { - /** - * 获取网关状态码 - * - * @return 返回状态码 - */ - String getCode(); - - /** - * 获取网关错误信息 - * - * @return 返回错误信息 - */ - String getMsg(); - - /** - * sub_code(明细返回码) - * @return sub_code(明细返回码) - */ - String getSub_code(); - - /** - * sub_msg(明细返回码描述) - * @return sub_msg(明细返回码描述) - */ - String getSub_msg(); - - /** - * 解决方案 - * @return 解决方案 - */ - String getSolution(); - - - - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java deleted file mode 100644 index 5095c5e4..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.gitee.sop.gatewaycommon.message; - -/** - * 网关错误定义 - * @author 六如 - */ -public enum ErrorEnum { - /** 成功 */ - SUCCESS(Codes.CODE_SUCCESS, ""), - - /** 服务暂不可用 */ - ISP_UNKNOWN_ERROR(Codes.CODE_UNKNOWN, "isp.unknown-error"), - /** 微服务未知错误 */ - ISP_SERVICE_UNKNOWN_ERROR(Codes.CODE_UNKNOWN, "isp.service-unknown-error"), - /** 服务不可用,路由被禁用 */ - ISP_API_DISABLED(Codes.CODE_UNKNOWN, "isp.service-not-available"), - /** 网关响应超时 */ - ISP_GATEWAY_RESPONSE_TIMEOUT(Codes.CODE_UNKNOWN, "isp.gateway-response-timeout"), - /** 限流处理 */ - ISV_REQUEST_LIMIT(Codes.CODE_UNKNOWN, "isv.service-busy"), - - /** 无效的访问令牌 */ - AOP_INVALID_AUTH_TOKEN(Codes.CODE_AUTH, "aop.invalid-auth-token"), - /** 访问令牌已过期 */ - AOP_AUTH_TOKEN_TIME_OUT(Codes.CODE_AUTH, "aop.auth-token-time-out"), - /** 无效的应用授权令牌 */ - AOP_INVALID_APP_AUTH_TOKEN(Codes.CODE_AUTH, "aop.invalid-app-auth-token"), - /** 商户未授权当前接口 */ - AOP_INVALID_APP_AUTH_TOKEN_NO_API(Codes.CODE_AUTH, "aop.invalid-app-auth-token-no-api"), - /** 应用授权令牌已过期 */ - AOP_APP_AUTH_TOKEN_TIME_OUT(Codes.CODE_AUTH, "aop.app-auth-token-time-out"), - /** 商户未签约任何产品 */ - AOP_NO_PRODUCT_REG_BY_PARTNER(Codes.CODE_AUTH, "aop.no-product-reg-by-partner"), - - /** 缺少方法名参数 */ - ISV_MISSING_METHOD(Codes.CODE_MISSING, "isv.missing-method"), - /** 缺少签名参数 */ - ISV_MISSING_SIGNATURE(Codes.CODE_MISSING, "isv.missing-signature"), - /** 缺少签名类型参数 */ - ISV_MISSING_SIGNATURE_TYPE(Codes.CODE_MISSING, "isv.missing-signature-type"), - /** 缺少签名配置 */ - ISV_MISSING_SIGNATURE_KEY(Codes.CODE_MISSING, "isv.missing-signature-key"), - /** 缺少appId参数 */ - ISV_MISSING_APP_ID(Codes.CODE_MISSING, "isv.missing-app-id"), - /** 缺少时间戳参数 */ - ISV_MISSING_TIMESTAMP(Codes.CODE_MISSING, "isv.missing-timestamp"), - /** 缺少版本参数 */ - ISV_MISSING_VERSION(Codes.CODE_MISSING, "isv.missing-version"), - /** 解密出错, 未指定加密算法 */ - ISV_DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE(Codes.CODE_MISSING, "isv.decryption-error-missing-encrypt-type"), - - /** 参数无效 */ - ISV_INVALID_PARAMETER(Codes.CODE_INVALID, "isv.invalid-parameter"), - /** 文件上传失败 */ - ISV_UPLOAD_FAIL(Codes.CODE_INVALID, "isv.upload-fail"), - /** 文件扩展名无效 */ - ISV_INVALID_FILE_EXTENSION(Codes.CODE_INVALID, "isv.invalid-file-extension"), - /** 文件大小无效 */ - ISV_INVALID_FILE_SIZE(Codes.CODE_INVALID, "isv.invalid-file-size"), - /** 不存在的方法名 */ - ISV_INVALID_METHOD(Codes.CODE_INVALID, "isv.invalid-method"), - /** 无效的数据格式 */ - ISV_INVALID_FORMAT(Codes.CODE_INVALID, "isv.invalid-format"), - /** 无效的签名类型 */ - ISV_INVALID_SIGNATURE_TYPE(Codes.CODE_INVALID, "isv.invalid-signature-type"), - /** 无效签名 */ - ISV_INVALID_SIGNATURE(Codes.CODE_INVALID, "isv.invalid-signature"), - /** 无效的加密类型 */ - ISV_INVALID_ENCRYPT_TYPE(Codes.CODE_INVALID, "isv.invalid-encrypt-type"), - /** 解密异常 */ - ISV_INVALID_ENCRYPT(Codes.CODE_INVALID, "isv.invalid-encrypt"), - /** 无效的appId参数 */ - ISV_INVALID_APP_ID(Codes.CODE_INVALID, "isv.invalid-app-id"), - /** 非法的时间戳参数 */ - ISV_INVALID_TIMESTAMP(Codes.CODE_INVALID, "isv.invalid-timestamp"), - /** 字符集错误 */ - ISV_INVALID_CHARSET(Codes.CODE_INVALID, "isv.invalid-charset"), - /** 摘要错误 */ - ISV_INVALID_DIGEST(Codes.CODE_INVALID, "isv.invalid-digest"), - /** 解密出错,不支持的加密算法 */ - ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_TYPE(Codes.CODE_INVALID, "isv.decryption-error-not-valid-encrypt-type"), - /** 解密出错, 未配置加密密钥或加密密钥格式错误 */ - ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_KEY(Codes.CODE_INVALID, "isv.decryption-error-not-valid-encrypt-key"), - /** 解密出错,未知异常 */ - ISV_DECRYPTION_ERROR_UNKNOWN(Codes.CODE_INVALID, "isv.decryption-error-unknown"), - /** 验签出错, 未配置对应签名算法的公钥或者证书 */ - ISV_MISSING_SIGNATURE_CONFIG(Codes.CODE_INVALID, "isv.missing-signature-config"), - /** 本接口不支持第三方代理调用 */ - ISV_NOT_SUPPORT_APP_AUTH(Codes.CODE_INVALID, "isv.not-support-app-auth"), - /** 可疑的攻击请求 */ - ISV_SUSPECTED_ATTACK(Codes.CODE_INVALID, "isv.suspected-attack"), - /** 无效的content-type */ - ISV_INVALID_CONTENT_TYPE(Codes.CODE_INVALID, "isv.invalid-content-type"), - - /** 业务处理失败 */ - BIZ_ERROR(Codes.CODE_BIZ, ""), - - /** 请检查配置的账户是否有当前接口权限 */ - ISV_INSUFFICIENT_ISV_PERMISSIONS(Codes.CODE_ISV_PERM, "isv.insufficient-isv-permissions"), - /** 代理的商户没有当前接口权限 */ - ISV_INSUFFICIENT_USER_PERMISSIONS(Codes.CODE_ISV_PERM, "isv.insufficient-user-permissions"), - /** 没有当前接口权限 */ - ISV_ROUTE_NO_PERMISSIONS(Codes.CODE_ISV_PERM, "isv.route-no-permissions"), - /** 禁止访问 */ - ISV_ACCESS_FORBIDDEN(Codes.CODE_ISV_PERM, "isv.access-forbidden"), - /** 禁止IP访问 */ - ISV_IP_FORBIDDEN(Codes.CODE_ISV_PERM, "isv.ip-forbidden"), - - ; - private ErrorMeta errorMeta; - - ErrorEnum(String code, String subCode) { - this.errorMeta = new ErrorMeta("open.error_", code, subCode); - } - - public ErrorMeta getErrorMeta() { - return errorMeta; - } - - private static class Codes { - public static final String CODE_SUCCESS = "10000"; - public static final String CODE_AUTH = "20001"; - public static final String CODE_MISSING = "40001"; - public static final String CODE_INVALID = "40002"; - public static final String CODE_BIZ = "40004"; - public static final String CODE_ISV_PERM = "40006"; - public static final String CODE_UNKNOWN = "20000"; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorFactory.java deleted file mode 100644 index a2134610..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorFactory.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.gitee.sop.gatewaycommon.message; - - -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.context.support.ResourceBundleMessageSource; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * 负责构建错误消息 - * - * @author 六如 - */ -@Slf4j -public class ErrorFactory { - - private ErrorFactory(){} - - public static final String SYS_ERR = "系统错误"; - - private static final String I18N_OPEN_ERROR = "i18n/open/error"; - public static final String UNDERLINE = "_"; - - private static Set noModuleCache = new HashSet<>(); - - private static Map errorCache = new HashMap<>(64); - - private static List localeList = Arrays.asList(Locale.ENGLISH, Locale.SIMPLIFIED_CHINESE); - - /** - * 错误信息的国际化信息 - */ - private static MessageSourceAccessor errorMessageSourceAccessor; - - /** - * 设置国际化资源信息 - */ - public static void initMessageSource(List isvModules) { - HashSet baseNamesSet = new HashSet<>(); - baseNamesSet.add(I18N_OPEN_ERROR); - - if (!isvModules.isEmpty()) { - baseNamesSet.addAll(isvModules); - } - - String[] totalBaseNames = baseNamesSet.toArray(new String[0]); - - log.info("加载错误码国际化资源:{}", StringUtils.arrayToCommaDelimitedString(totalBaseNames)); - ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource(); - bundleMessageSource.setBasenames(totalBaseNames); - MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(bundleMessageSource); - setErrorMessageSourceAccessor(messageSourceAccessor); - } - - /** - * 通过ErrorMeta,Locale,params构建国际化错误消息 - * - * @param errorMeta 错误信息 - * @param locale 本地化 - * @param params 参数 - * @return 如果没有配置国际化消息,则直接返回errorMeta中的信息 - */ - public static Error getError(ErrorMeta errorMeta, Locale locale, Object... params) { - if (locale == null) { - locale = Locale.SIMPLIFIED_CHINESE; - } - if (!localeList.contains(locale)) { - locale = Locale.ENGLISH; - } - String key = errorMeta.getModulePrefix() + errorMeta.getCode() + errorMeta.getSubCode() + locale.toString(); - Error error = errorCache.get(key); - if (error == null) { - Assert.notNull(locale, "未设置Locale"); - String modulePrefix = errorMeta.getModulePrefix(); - String code = errorMeta.getCode(); - // open.error_20000=Service is temporarily unavailable - String msg = getErrorMessage(modulePrefix + code, locale); - String subCode = errorMeta.getSubCode(); - // open.error_20000_isp.unknown-error=Service is temporarily unavailable - String subMsg = getErrorMessage(modulePrefix + code + UNDERLINE + subCode, locale, params); - if (StringUtils.isEmpty(msg)) { - msg = SYS_ERR; - } - if (StringUtils.isEmpty(subMsg)) { - subMsg = SYS_ERR; - } - // solution暂未实现,如果要实现,可以这样配置: - // open.error_20000_isp.unknown-error_solution=Service is temporarily unavailable - // String solution = getErrorMessage(modulePrefix + code + UNDERLINE + subCode + "_solution", locale, params); - error = new ErrorImpl(code, msg, subCode, subMsg, null); - errorCache.put(key, error); - } - return error; - } - - - public static void setErrorMessageSourceAccessor(MessageSourceAccessor errorMessageSourceAccessor) { - ErrorFactory.errorMessageSourceAccessor = errorMessageSourceAccessor; - } - - /** - * 返回本地化信息 - * - * @param module 错误模块 - * @param locale 本地化 - * @param params 参数 - * @return 返回信息 - */ - public static String getErrorMessage(String module, Locale locale, Object... params) { - if (noModuleCache.contains(module)) { - return null; - } - try { - return errorMessageSourceAccessor.getMessage(module, params, locale); - } catch (Exception e) { - noModuleCache.add(module); - return null; - } - } - - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java deleted file mode 100644 index f226ae82..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gitee.sop.gatewaycommon.message; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class ErrorImpl implements Error { - - private String code; - private String msg; - private String sub_code; - private String sub_msg; - private String solution; - - public ErrorImpl(String code, String msg, String sub_code, String sub_msg, String solution) { - this.code = code; - this.msg = msg; - this.sub_code = sub_code; - this.sub_msg = sub_msg; - this.solution = solution; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorMeta.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorMeta.java deleted file mode 100644 index 592ebd40..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorMeta.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.gitee.sop.gatewaycommon.message; - -import com.gitee.sop.gatewaycommon.exception.ApiException; -import lombok.Getter; - -import java.util.Locale; - -/** - * 错误对象 - * - * @author 六如 - */ -@Getter -public class ErrorMeta { - - private final String modulePrefix; - private final String code; - private final String subCode; - - public ErrorMeta(String modulePrefix, String code, String subCode) { - this.modulePrefix = modulePrefix; - this.code = code; - this.subCode = subCode; - } - - public Error getError(Locale locale) { - return ErrorFactory.getError(this, locale); - } - - /** - * 返回网关exception - * - * @param params 参数 - * @return 返回exception - */ - public ApiException getException(Object... params) { - if (params != null && params.length == 1) { - Object param = params[0]; - if (param instanceof Throwable) { - return new ApiException((Throwable) param, this); - } - } - return new ApiException(this, params); - } - - @Override - public String toString() { - return modulePrefix + code + "_" + subCode; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorDTO.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorDTO.java deleted file mode 100644 index cd6b103f..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorDTO.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.gitee.sop.gatewaycommon.monitor; - -import lombok.Data; - -import java.util.Collection; - -/** - * 每个接口 总调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数,错误查看。 - * - * @author 六如 - */ -@Data -public class MonitorDTO { - - /** - * 路由id - */ - private String routeId; - - /** - * 接口名 - */ - private String name; - /** - * 版本号 - */ - private String version; - /** - * serviceId - */ - private String serviceId; - - /** - * 实例id - */ - private String instanceId; - - /** 请求耗时最长时间, 数据库字段:max_time */ - private Integer maxTime; - - /** 请求耗时最小时间, 数据库字段:min_time */ - private Integer minTime; - - /** - * 总时长 - */ - private Long totalTime; - - /** 总调用次数, 数据库字段:total_request_count */ - private Long totalRequestCount; - - /** 成功次数, 数据库字段:success_count */ - private Long successCount; - - /** 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败), 数据库字段:error_count */ - private Long errorCount; - - /** - * 错误信息 - */ - private Collection errorMsgList; - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorData.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorData.java deleted file mode 100644 index 86ce3372..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorData.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.gitee.sop.gatewaycommon.monitor; - -import com.gitee.sop.gatewaycommon.bean.LRUCache; -import lombok.Data; -import org.apache.commons.codec.digest.DigestUtils; - -import java.time.LocalDateTime; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * 每个接口 总调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数,错误查看。 - * - * @author 六如 - */ -@Data -public class MonitorData { - - public static final int LIMIT_SIZE = 50; - - - private String routeId; - - /** - * 接口名 - */ - private String name; - /** - * 版本号 - */ - private String version; - /** - * serviceId - */ - private String serviceId; - - private String instanceId; - - /** - * 请求耗时最长时间 - */ - private Integer maxTime; - /** - * 请求耗时最小时间 - */ - private Integer minTime; - /** - * 总时长 - */ - private AtomicInteger totalTime; - /** - * 总调用次数 - */ - private AtomicInteger totalRequestCount; - /** - * 成功次数 - */ - private AtomicInteger successCount; - /** - * 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败) - */ - private AtomicInteger errorCount; - - /** - * 错误信息,key: errorId - */ - private LRUCache monitorErrorMsgMap; - - /** - * 下一次刷新到数据库的时间 - */ - private LocalDateTime flushTime; - - public synchronized void storeMaxTime(int spendTime) { - if (spendTime > maxTime) { - maxTime = spendTime; - } - } - - public synchronized void storeMinTime(int spendTime) { - if (minTime == 0 || spendTime < minTime) { - minTime = spendTime; - } - } - - public void addErrorMsg(String errorMsg, int httpStatus) { - if (errorMsg == null || "".equals(errorMsg)) { - return; - } - synchronized (this) { - String errorId = DigestUtils.md5Hex(instanceId + routeId + errorMsg); - MonitorErrorMsg monitorErrorMsg = monitorErrorMsgMap.computeIfAbsent(errorId, (k) -> { - MonitorErrorMsg value = new MonitorErrorMsg(); - value.setErrorId(errorId); - value.setInstanceId(instanceId); - value.setRouteId(routeId); - value.setErrorMsg(errorMsg); - value.setErrorStatus(httpStatus); - value.setCount(0); - return value; - }); - monitorErrorMsg.setCount(monitorErrorMsg.getCount() + 1); - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorErrorMsg.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorErrorMsg.java deleted file mode 100644 index a14799da..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorErrorMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.sop.gatewaycommon.monitor; - -import lombok.Data; - -/** - * @author thc - */ -@Data -public class MonitorErrorMsg { - /** 错误id,md5(error_msg), 数据库字段:error_id */ - private String errorId; - - /** 实例id, 数据库字段:instance_id */ - private String instanceId; - - /** 数据库字段:route_id */ - private String routeId; - - /** 数据库字段:isp_id */ - private Long ispId; - - /** 数据库字段:error_msg */ - private String errorMsg; - - /** http status,非200错误 */ - private Integer errorStatus; - - /** 错误次数, 数据库字段:count */ - private Integer count; -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorManager.java deleted file mode 100644 index 4bf555c0..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/MonitorManager.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gitee.sop.gatewaycommon.monitor; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -/** - * @author 六如 - */ -public class MonitorManager { - - private static Map monitorMap = new ConcurrentHashMap<>(128); - - public Map getMonitorData() { - return monitorMap; - } - - public MonitorData getMonitorInfo(String routeId, Function createFun) { - return monitorMap.computeIfAbsent(routeId, createFun); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/RouteErrorCount.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/RouteErrorCount.java deleted file mode 100644 index 8ee3a32b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/monitor/RouteErrorCount.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gitee.sop.gatewaycommon.monitor; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class RouteErrorCount { - - private String routeId; - private Integer count; -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java deleted file mode 100644 index c607a5ff..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java +++ /dev/null @@ -1,230 +0,0 @@ -package com.gitee.sop.gatewaycommon.param; - -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.annotation.JSONField; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import org.apache.commons.lang3.StringUtils; - -import java.util.Map; -import java.util.UUID; - -/** - * 客户端传来的参数放在这里. - * - * @author 六如 - */ -public class ApiParam extends JSONObject implements Param { - - - public ApiParam() { - } - - public ApiParam(Map map) { - super(map); - } - - private String requestId = UUID.randomUUID().toString().replace("-", ""); - - private String serviceId; - private String ip; - private boolean mergeResult = true; - - private boolean isGrayRequest; - - private transient UploadContext uploadContext; - - public static ApiParam build(Map map) { - ApiParam apiParam = new ApiParam(); - for (Map.Entry entry : map.entrySet()) { - apiParam.put(entry.getKey(), entry.getValue()); - } - return apiParam; - } - - @JSONField(serialize = false) - public String getRouteId() { - return this.fetchNameVersion(); - } - - /** - * 获取sign,并从param中删除 - * - * @return 返回sign内容 - */ - public String fetchSignAndRemove() { - String sign = this.fetchSign(); - this.remove(ParamNames.SIGN_NAME); - return sign; - } - - public void setName(String name) { - put(ParamNames.API_NAME, name); - } - - /** - * 接口名,如:goods.list - */ - @Override - public String fetchName() { - return getString(ParamNames.API_NAME); - } - - public String fetchNameVersion() { - return buildNameVersion(this.fetchName(), this.fetchVersion()); - } - - public static String buildNameVersion(String name, String version) { - if (name == null && version == null) { - return null; - } - if (StringUtils.isEmpty(version)) { - return name; - } else { - return name + version; - } - } - - /** - * 版本号 - */ - @Override - public String fetchVersion() { - return getString(ParamNames.VERSION_NAME); - } - - /** - * 接入应用ID - */ - @Override - public String fetchAppKey() { - return getString(ParamNames.APP_KEY_NAME); - } - - public void setAppKey(String appKey) { - put(ParamNames.APP_KEY_NAME, appKey); - } - - - - /** - * 参数,urlencode后的 - */ - @Override - public String fetchData() { - return getString(ParamNames.BIZ_CONTENT_NAME); - } - - public void setData(String json) { - put(ParamNames.BIZ_CONTENT_NAME, json); - } - - /** - * 时间戳,格式为yyyy-MM-dd HH:mm:ss,例如:2015-01-01 12:00:00 - */ - @Override - public String fetchTimestamp() { - return getString(ParamNames.TIMESTAMP_NAME); - } - - public void setTimestamp(String timestamp) { - put(ParamNames.TIMESTAMP_NAME, timestamp); - } - - /** - * 签名串 - */ - @Override - public String fetchSign() { - return getString(ParamNames.SIGN_NAME); - } - - public void setSign(String sign) { - put(ParamNames.SIGN_NAME, sign); - } - - @Override - public String fetchFormat() { - String format = getString(ParamNames.FORMAT_NAME); - if (format == null || "".equals(format)) { - return SopConstants.FORMAT_JSON; - } - return format; - } - - public void setFormat(String format) { - put(ParamNames.FORMAT_NAME, format); - } - - @Override - public String fetchAccessToken() { - return getString(ParamNames.APP_AUTH_TOKEN_NAME); - } - - @Override - public String fetchSignMethod() { - return getString(ParamNames.SIGN_TYPE_NAME); - } - - @Override - public String fetchCharset() { - return getString(ParamNames.CHARSET_NAME); - } - - public void setUploadContext(UploadContext uploadContext) { - this.uploadContext = uploadContext; - } - - public UploadContext fetchUploadContext() { - return uploadContext; - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - public void setIp(String ip) { - this.ip = ip; - } - - public String fetchIp() { - return ip; - } - - public boolean fetchGrayRequest() { - return isGrayRequest; - } - - public void setGrayRequest(boolean grayRequest) { - isGrayRequest = grayRequest; - } - - public String fetchServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public boolean fetchMergeResult() { - return mergeResult; - } - - public void setMergeResult(boolean mergeResult) { - this.mergeResult = mergeResult; - } - - public void setRequestId(String requestId) { - this.requestId = requestId; - } - - public String fetchRequestId() { - return requestId; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiUploadContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiUploadContext.java deleted file mode 100644 index 1c1e64f8..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiUploadContext.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gitee.sop.gatewaycommon.param; - -import org.springframework.web.multipart.MultipartFile; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * 存放上传文件 - * - * @author 六如 - */ -public class ApiUploadContext implements UploadContext { - - /** - * key: 表单name - */ - private Map> fileMap; - private List allFile; - - public ApiUploadContext(Map> map) { - if (map == null) { - map = Collections.emptyMap(); - } - this.fileMap = map; - this.allFile = new ArrayList<>(); - map.values().forEach(list -> this.allFile.addAll(list)); - } - - @Override - public MultipartFile getFile(int index) { - return this.allFile.get(index); - } - - @Override - public List getFile(String name) { - return fileMap.get(name); - } - - @Override - public List getAllFile() { - return this.allFile; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/FormHttpOutputMessage.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/FormHttpOutputMessage.java deleted file mode 100644 index 752d412c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/FormHttpOutputMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.gatewaycommon.param; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpOutputMessage; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * @author 六如 - */ -public class FormHttpOutputMessage implements HttpOutputMessage { - - private HttpHeaders headers = new HttpHeaders(); - private ByteArrayOutputStream output = new ByteArrayOutputStream(); - - @Override - public HttpHeaders getHeaders() { - return this.headers; - } - - @Override - public OutputStream getBody() throws IOException { - return this.output; - } - - public byte[] getInput() throws IOException { - this.output.flush(); - return this.output.toByteArray(); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/Param.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/Param.java deleted file mode 100644 index 4d2fee1b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/Param.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.gitee.sop.gatewaycommon.param; - -import java.io.Serializable; - -/** - * @author 六如 - */ -public interface Param extends Serializable { - - /** - * 获取接口名 - * @return 返回接口名 - */ - String fetchName(); - - /** - * 获取版本号 - * @return 返回版本号 - */ - String fetchVersion(); - - /** - * 获取appKey - * @return 返回appKey - */ - String fetchAppKey(); - - /** - * 获取业务参数 - * @return 返回业务参数 - */ - String fetchData(); - - /** - * 获取时间戳 - * @return 返回时间戳 - */ - String fetchTimestamp(); - - /** - * 获取签名串 - * @return 返回签名串 - */ - String fetchSign(); - - /** - * 获取格式化类型 - * @return 返回格式化类型 - */ - String fetchFormat(); - - /** - * 获取accessToken - * @return 返回accessToken - */ - String fetchAccessToken(); - - /** - * 获取签名方式 - * @return 返回签名方式 - */ - String fetchSignMethod(); - - /** - * 请求使用的编码格式,如utf-8,gbk,gb2312等 - * @return 请求使用的编码格式,如utf-8,gbk,gb2312等 - */ - String fetchCharset(); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java deleted file mode 100644 index 904f8418..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gitee.sop.gatewaycommon.param; - -/** - * 请求参数名定义 - * - * 参数 类型 是否必填 最大长度 描述 示例值 - * app_id String 是 32 支付宝分配给开发者的应用ID 2014072300007148 - * method String 是 128 接口名称 alipay.trade.fastpay.refund.query - * format String 否 40 仅支持JSON JSON - * charset String 是 10 请求使用的编码格式,如utf-8,gbk,gb2312等 utf-8 - * sign_type String 是 10 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2 - * sign String 是 344 商户请求参数的签名串,详见签名 详见示例 - * timestamp String 是 19 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50 - * version String 是 3 调用的接口版本,固定为:1.0 1.0 - * app_auth_token String 否 40 详见应用授权概述 - * biz_content String 是 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 - * - * @author 六如 - */ -public class ParamNames { - /** 分配给开发者的应用ID */ - public static String APP_KEY_NAME = "app_id"; - /** 接口名称 */ - public static String API_NAME = "method"; - /** 仅支持JSON */ - public static String FORMAT_NAME = "format"; - /** 请求使用的编码格式 */ - public static String CHARSET_NAME = "charset"; - /** 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 */ - public static String SIGN_TYPE_NAME = "sign_type"; - /** 商户请求参数的签名串 */ - public static String SIGN_NAME = "sign"; - /** 发送请求的时间 */ - public static String TIMESTAMP_NAME = "timestamp"; - /** 调用的接口版本 */ - public static String VERSION_NAME = "version"; - /** OAuth 2.0授权token */ - public static String APP_AUTH_TOKEN_NAME = "app_auth_token"; - /** 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 */ - public static String BIZ_CONTENT_NAME = "biz_content"; - - /** 时间戳格式 */ - public static String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss"; - - /** 返回sign名称 */ - public static String RESPONSE_SIGN_NAME = "sign"; - - public static String HEADER_VERSION_NAME = "sop-version"; - - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParameterFormatter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParameterFormatter.java deleted file mode 100644 index 1653f36d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParameterFormatter.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gitee.sop.gatewaycommon.param; - -import com.alibaba.fastjson.JSONObject; -import com.gitee.sop.gatewaycommon.manager.Formatter; - -/** - * @author 六如 - */ -public interface ParameterFormatter extends Formatter { -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/UploadContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/UploadContext.java deleted file mode 100644 index 530a3265..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/UploadContext.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.sop.gatewaycommon.param; - -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; - -/** - * 获取上传文件 - * - * @author 六如 - */ -public interface UploadContext { - /** - * 根据索引获取上传文件,从0开始 - * - * @param index - * @return 返回上传文件信息 - */ - MultipartFile getFile(int index); - - /** - * 根据表单名获取上传文件 - * - * @param name - * 表单名称 - * @return 返回上传文件信息 - */ - List getFile(String name); - - /** - * 获取所有的上传文件 - * - * @return 返回所有的上传文件 - */ - List getAllFile(); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ApiResult.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ApiResult.java deleted file mode 100644 index 347a1f7b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ApiResult.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import lombok.Data; - -/** - * 默认的结果封装类. - *
- *
- * xml返回结果:
- * 
- *     50
- *     Remote service error
- *     isv.invalid-parameter
- *     非法参数
- * 
- * 成功情况:
- * 
- *     0
- *     成功消息
- *     
- *         ...返回内容
- *     
- * 
- *
- * json返回格式:
- * {
- *  "code":"50",
- * 	"msg":"Remote service error",
- * 	"sub_code":"isv.invalid-parameter",
- * 	"sub_msg":"非法参数"
- * }
- * 成功情况:
- * {
- *  "code":"0",
- * 	"msg":"成功消息内容。。。",
- * 	"data":{
- * 	    ...返回内容
- *    }
- * }
- * 
- *

- * 字段说明: - * code:网关异常码
- * msg:网关异常信息
- * sub_code:业务异常码
- * sub_msg:业务异常信息
- * - * @author 六如 - */ -@Data -public class ApiResult implements Result { - - /** - * 网关异常码,范围0~100 成功返回"0" - */ - private String code = ErrorEnum.SUCCESS.getErrorMeta().getCode(); - - /** - * 网关异常信息 - */ - private String msg; - - /** - * 业务异常码 - */ - private String sub_msg; - - /** - * 业务异常信息 - */ - private String sub_code; - - private String sign; -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java deleted file mode 100644 index 588fc673..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java +++ /dev/null @@ -1,297 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.parser.Feature; -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext; -import com.gitee.sop.gatewaycommon.bean.Isv; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext; -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.message.ErrorMeta; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.param.ParamNames; -import com.gitee.sop.gatewaycommon.secret.IsvManager; -import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil; -import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySignature; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.http.HttpStatus; -import org.springframework.util.CollectionUtils; -import org.springframework.web.util.UriUtils; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; - -/** - * 处理微服务返回结果 - * - * @author 六如 - */ -@Slf4j -public abstract class BaseExecutorAdapter implements ResultExecutor { - - private static final Map HTTP_STATUS_ERROR_ENUM_MAP = new HashMap<>(8); - - private static final String GATEWAY_CODE_NAME = "code"; - private static final String GATEWAY_MSG_NAME = "msg"; - private static final String ARRAY_START = "["; - private static final String ARRAY_END = "]"; - private static final String ROOT_JSON = "{'items':%s}".replace("'", "\""); - - static { - HTTP_STATUS_ERROR_ENUM_MAP.put(HttpStatus.OK.value(), ErrorEnum.SUCCESS); - HTTP_STATUS_ERROR_ENUM_MAP.put(SopConstants.BIZ_ERROR_STATUS, ErrorEnum.BIZ_ERROR); - HTTP_STATUS_ERROR_ENUM_MAP.put(SopConstants.UNKNOWN_ERROR_STATUS, ErrorEnum.ISP_SERVICE_UNKNOWN_ERROR); - HTTP_STATUS_ERROR_ENUM_MAP.put(HttpStatus.NOT_FOUND.value(), ErrorEnum.ISV_INVALID_METHOD); - } - - - /** - * 获取业务方约定的返回码 - * - * @param t request - * @return 返回返回码 - */ - public abstract int getResponseStatus(T t); - - /** - * 获取微服务端的错误信息 - * - * @param t request - * @return 返回错误信息 - */ - public abstract String getResponseErrorMessage(T t); - - /** - * 业务返回的错误结果 - * @param t request - * @param status status - * @return 业务返回结果 - */ - public abstract Optional getServiceResultForError(T t, int status); - - /** - * 返回Api参数 - * - * @param t request - * @return 返回api参数 - */ - public abstract ApiParam getApiParam(T t); - - /** - * 获取locale - * - * @param t request - * @return 返回locale - */ - protected abstract Locale getLocale(T t); - - /** - * 返回拦截器上下文 - * - * @param t request - * @return 返回拦截器上下文 - */ - protected abstract RouteInterceptorContext getRouteInterceptorContext(T t); - - @Override - public String mergeResult(T request, String serviceResult) { - serviceResult = formatResult(serviceResult); - boolean isMergeResult = this.isMergeResult(request); - int responseStatus = this.getResponseStatus(request); - Optional serviceResultForError = this.getServiceResultForError(request, responseStatus); - if (serviceResultForError.isPresent()) { - serviceResult = UriUtils.decode(serviceResultForError.get(), StandardCharsets.UTF_8); - } - this.doAfterRoute(serviceResult, responseStatus, request); - String finalResult; - if (isMergeResult) { - Map responseData = this.parseServiceResult(serviceResult, responseStatus, request); - finalResult = this.merge(request, responseData); - } else { - finalResult = serviceResult; - } - return finalResult; - } - - /** - * 执行拦截器after操作 - * - * @param serviceResult 微服务结果 - * @param responseStatus 微服务状态码 - * @param requestContext 请求上下文 - */ - private void doAfterRoute(String serviceResult, int responseStatus, T requestContext) { - RouteInterceptorContext routeInterceptorContext = getRouteInterceptorContext(requestContext); - if (routeInterceptorContext instanceof DefaultRouteInterceptorContext) { - DefaultRouteInterceptorContext defaultRouteInterceptorContext = (DefaultRouteInterceptorContext) routeInterceptorContext; - defaultRouteInterceptorContext.setResponseStatus(responseStatus); - defaultRouteInterceptorContext.setServiceResult(serviceResult); - defaultRouteInterceptorContext.setFinishTimeMillis(System.currentTimeMillis()); - defaultRouteInterceptorContext.setResponseDataSize(serviceResult.length()); - if (responseStatus != HttpStatus.OK.value()) { - String responseErrorMessage = getResponseErrorMessage(requestContext); - if (StringUtils.isEmpty(responseErrorMessage)) { - responseErrorMessage = serviceResult; - } - ServiceInstance serviceInstance = defaultRouteInterceptorContext.getServiceInstance(); - log.error("微服务端报错,instance:{}:{}, errorMsg:{}", serviceInstance.getHost(), serviceInstance.getPort(), responseErrorMessage); - defaultRouteInterceptorContext.setServiceErrorMsg(responseErrorMessage); - } - } - this.bindRouteInterceptorContextProperties(routeInterceptorContext, requestContext); - RouteInterceptorUtil.runAfterRoute(routeInterceptorContext); - } - - protected void bindRouteInterceptorContextProperties(RouteInterceptorContext routeInterceptorContext, T requestContext) { - - } - - /** - * 将微服务的返回结果解析成JSONObject - * - * @param serviceResult 微服务返回结果 - * @param responseStatus 返回状态 - * @param request 请求 - * @return 返回JSONObject - */ - protected Map parseServiceResult(String serviceResult, int responseStatus, T request) { - ErrorEnum errorEnum = HTTP_STATUS_ERROR_ENUM_MAP.get(responseStatus); - if (errorEnum == null) { - // 其它异常不应该把异常信息告诉给客户端,将微服务内容设置成空的json - serviceResult = SopConstants.EMPTY_JSON; - errorEnum = ErrorEnum.ISP_UNKNOWN_ERROR; - } - ErrorMeta errorMeta = errorEnum.getErrorMeta(); - Map serviceData = new LinkedHashMap<>(); - ApiParam apiParam = this.getApiParam(request); - if (apiParam != null) { - // 全局请求id,方便追踪定位 - serviceData.put("request_id", apiParam.fetchRequestId()); - } - serviceData.put(GATEWAY_CODE_NAME, errorMeta.getCode()); - serviceData.put(GATEWAY_MSG_NAME, errorMeta.getError(getLocale(request)).getMsg()); - JSONObject serviceObj = JSON.parseObject(serviceResult, Feature.OrderedField); - this.handleBizResult(serviceData, serviceObj, apiParam, request); - return serviceData; - } - - protected abstract void handleBizResult(Map serviceData, JSONObject serviceObj, ApiParam apiParam, T request); - - - /** - * 该路由是否合并结果 - * - * @param request request - * @return true:需要合并 - */ - protected boolean isMergeResult(T request) { - ApiParam params = this.getApiParam(request); - return params != null && params.fetchMergeResult(); - } - - protected String formatResult(String serviceResult) { - if (StringUtils.isBlank(serviceResult)) { - return SopConstants.EMPTY_JSON; - } - // 如果直接返回数组,需要进行包装,变成:{"items": [...]} - if (serviceResult.startsWith(ARRAY_START) && serviceResult.endsWith(ARRAY_END)) { - return String.format(ROOT_JSON, serviceResult); - } - return serviceResult; - } - - public String merge(T exchange, Map responseData) { - JSONObject finalData = new JSONObject(true); - ApiParam params = this.getApiParam(exchange); - if (params == null) { - params = new ApiParam(); - params.setName("error"); - } - ApiConfig apiConfig = ApiConfig.getInstance(); - // 点换成下划线 - DataNameBuilder dataNameBuilder = apiConfig.getDataNameBuilder(); - // alipay_goods_get_response - String responseDataNodeName = dataNameBuilder.build(params.fetchName()); - finalData.put(responseDataNodeName, responseData); - ResultAppender resultAppender = apiConfig.getResultAppender(); - // 追加额外的结果 - if (resultAppender != null) { - resultAppender.append(finalData, params, exchange); - } - // 添加服务端sign - this.addResponseSign(apiConfig, params, finalData, responseDataNodeName); - return finalData.toJSONString(); - } - - private void addResponseSign(ApiConfig apiConfig, ApiParam params, JSONObject finalData, String responseDataNodeName) { - if (apiConfig.isShowReturnSign() && !CollectionUtils.isEmpty(params)) { - // 添加try...catch,生成sign出错不影响结果正常返回 - try { - String responseSignContent = this.buildResponseSignContent(responseDataNodeName, finalData); - String sign = this.createResponseSign(apiConfig.getIsvManager(), params, responseSignContent); - if (StringUtils.isNotBlank(sign)) { - finalData.put(ParamNames.RESPONSE_SIGN_NAME, sign); - } - } catch (Exception e) { - log.error("生成平台签名失败, params: {}", params.toJSONString(), e); - } - } - } - - /** - * 获取待签名内容 - * - * @param rootNodeName 业务数据节点 - * @param finalData 最终结果 - * @return 返回待签名内容 - */ - protected String buildResponseSignContent(String rootNodeName, JSONObject finalData) { - String body = finalData.toJSONString(); - int indexOfRootNode = body.indexOf(rootNodeName); - if (indexOfRootNode > 0) { - int signDataStartIndex = indexOfRootNode + rootNodeName.length() + 2; - int length = body.length() - 1; - return body.substring(signDataStartIndex, length); - } - return null; - } - - /** - * 这里需要使用平台的私钥生成一个sign,需要配置两套公私钥。 - * - * @param isvManager isvManager - * @param params 请求参数 - * @param responseSignContent 待签名内容 - * @return 返回平台生成的签名 - */ - protected String createResponseSign(IsvManager isvManager, ApiParam params, String responseSignContent) { - if (StringUtils.isEmpty(responseSignContent)) { - return null; - } - // 根据appId获取秘钥 - String appKey = params.fetchAppKey(); - if (StringUtils.isEmpty(appKey)) { - return null; - } - Isv isvInfo = isvManager.getIsv(appKey); - String privateKeyPlatform = isvInfo == null ? null : isvInfo.getPrivateKeyPlatform(); - if (StringUtils.isEmpty(privateKeyPlatform)) { - return null; - } - return AlipaySignature.rsaSign( - responseSignContent - , privateKeyPlatform - , params.fetchCharset() - , params.fetchSignMethod() - ); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/CustomDataNameBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/CustomDataNameBuilder.java deleted file mode 100644 index d4e23576..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/CustomDataNameBuilder.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -/** - * 返回固定的 - * { - * "result": { - * "code": "20000", - * "msg": "Service Currently Unavailable", - * "sub_code": "isp.unknown-error", - * "sub_msg": "系统繁忙" - * }, - * "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" - * } - * @author 六如 - */ -public class CustomDataNameBuilder implements DataNameBuilder { - private String dataName = "result"; - - public CustomDataNameBuilder() { - } - - public CustomDataNameBuilder(String dataName) { - this.dataName = dataName; - } - - @Override - public String build(String method) { - return dataName; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/DataNameBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/DataNameBuilder.java deleted file mode 100644 index 6b009a1d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/DataNameBuilder.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -/** - * @author 六如 - */ -public interface DataNameBuilder { - /** - * 构建数据节点名称 - * @param method - * @return - */ - String build(String method); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/DefaultDataNameBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/DefaultDataNameBuilder.java deleted file mode 100644 index 58a19d98..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/DefaultDataNameBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -/** - * 将方法名中的"."转成"_"并在后面追加"_response"
- * 如:alipay.trade.order.settle --> alipay_trade_order_settle_response
- *

- * {
- *     "alipay_trade_order_settle_response": {
- *         "code": "20000",
- *         "msg": "Service Currently Unavailable",
- *         "sub_code": "isp.unknown-error",
- *         "sub_msg": "系统繁忙"
- *     },
- *     "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
- * }
- * 
- * @author 六如 - */ -public class DefaultDataNameBuilder implements DataNameBuilder { - private static final char DOT = '.'; - private static final char UNDERLINE = '_'; - private static final String DATA_SUFFIX = "_response"; - - @Override - public String build(String method) { - if (method == null) { - method = "error"; - } - return method.replace(DOT, UNDERLINE) + DATA_SUFFIX; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResult.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResult.java deleted file mode 100644 index 1255ed1f..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResult.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * @author 六如 - */ -@Data -@EqualsAndHashCode(callSuper = false) -public class JsonResult extends ApiResult { - private Object data; -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/Result.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/Result.java deleted file mode 100644 index 5f9e2c67..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/Result.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -import java.io.Serializable; - -/** - * 返回结果,后续自定义的返回类需要实现这个接口。 - * - * @author 六如 - */ -public interface Result extends Serializable { - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultAppender.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultAppender.java deleted file mode 100644 index df6b378a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultAppender.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -import com.alibaba.fastjson.JSONObject; - -import java.util.Map; - -/** - * 对结果进行追加 - * - * @author 六如 - */ -public interface ResultAppender { - /** - * 追加最终结果 - * - * @param result 最终结果 - * @param params 请求参数 - * @param ctx 请求上下文,zuul对应的是RequestContext,Gateway对应的是exchange - */ - void append(JSONObject result, Map params, Object ctx); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutor.java deleted file mode 100644 index e07d8946..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutor.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -/** - * 对返回结果进行处理 - * 成功示例 - * { - * "alipay_trade_fastpay_refund_query_response": { - * "code": "10000", - * "msg": "Success", - * "trade_no": "2014112611001004680073956707", - * "out_trade_no": "20150320010101001", - * "out_request_no": "20150320010101001", - * "refund_reason": "用户退款请求", - * "total_amount": 100.2, - * "refund_amount": 12.33 - * }, - * "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" - * } - *

- * 异常示例 - * { - * "alipay_trade_fastpay_refund_query_response": { - * "code": "20000", - * "msg": "Service Currently Unavailable", - * "sub_code": "isp.unknown-error", - * "sub_msg": "系统繁忙" - * }, - * "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" - * } - * - * @author 六如 - */ -public interface ResultExecutor { - /** - * 合并结果 - * @param request - * @param serviceResult - * @return 返回最终输出结果 - */ - String mergeResult(T request, String serviceResult); - - /** - * 合并错误结果 - * @param request - * @param ex - * @return 返回最终输出结果 - */ - R buildErrorResult(T request, Throwable ex); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutorForGateway.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutorForGateway.java deleted file mode 100644 index 54bf741d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutorForGateway.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gitee.sop.gatewaycommon.result; - -import org.springframework.web.server.ServerWebExchange; - -/** - * @author 六如 - */ -public interface ResultExecutorForGateway extends ResultExecutor { -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseForwardChooser.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseForwardChooser.java deleted file mode 100644 index 358501a9..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseForwardChooser.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.ApiParamAware; -import com.gitee.sop.gatewaycommon.bean.TargetRoute; -import com.gitee.sop.gatewaycommon.loadbalancer.builder.GrayUserBuilder; -import com.gitee.sop.gatewaycommon.manager.EnvGrayManager; -import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - -/** - * @author 六如 - */ -public abstract class BaseForwardChooser implements ForwardChooser, ApiParamAware { - - @Autowired - private EnvGrayManager envGrayManager; - - @Override - public ForwardInfo getForwardInfo(T t) { - ApiParam apiParam = getApiParam(t); - String nameVersion = apiParam.fetchNameVersion(); - TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(nameVersion); - String serviceId = targetRoute.getServiceDefinition().fetchServiceIdLowerCase(); - // 如果服务在灰度阶段,返回一个灰度版本号 - String grayVersion = envGrayManager.getVersion(serviceId, nameVersion); - // 如果是灰度环境 - if (grayVersion != null && isGrayUser(serviceId, apiParam)) { - String newNameVersion = apiParam.fetchName() + grayVersion; - TargetRoute targetRouteDest = RouteRepositoryContext.getRouteRepository().get(newNameVersion); - if (targetRouteDest != null) { - apiParam.setGrayRequest(true); - targetRoute = targetRouteDest; - } - } - return new ForwardInfo(targetRoute); - } - - protected boolean isGrayUser(String serviceId, ApiParam apiParam) { - List grayUserBuilders = ApiConfig.getInstance().getGrayUserBuilders(); - for (GrayUserBuilder grayUserBuilder : grayUserBuilders) { - String userKey = grayUserBuilder.buildGrayUserKey(apiParam); - if (envGrayManager.containsKey(serviceId, userKey)) { - return true; - } - } - return false; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseRegistryListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseRegistryListener.java deleted file mode 100644 index 5124da62..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseRegistryListener.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.bean.SpringContext; -import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author 六如 - */ -@Slf4j -public abstract class BaseRegistryListener implements RegistryListener { - - private static final int FIVE_SECONDS = 1000 * 5; - - private final Map updateTimeMap = new ConcurrentHashMap<>(16); - - public static List EXCLUDE_SERVICE_ID_LIST = new ArrayList<>(8); - - static { - EXCLUDE_SERVICE_ID_LIST.add("sop-gateway"); - EXCLUDE_SERVICE_ID_LIST.add("sop-website"); - EXCLUDE_SERVICE_ID_LIST.add("sop-admin"); - EXCLUDE_SERVICE_ID_LIST.add("website-server"); - } - - @Autowired - private ServiceListener serviceListener; - - /** - * 移除路由信息 - * - * @param serviceId serviceId - */ - public synchronized void removeRoutes(String serviceId) { - serviceListener.onRemoveService(serviceId.toLowerCase()); - } - - /** - * 拉取路由信息 - * - * @param instance 服务实例 - */ - public synchronized void pullRoutes(InstanceDefinition instance) { - // serviceId统一小写 - instance.setServiceId(instance.getServiceId().toLowerCase()); - serviceListener.onAddInstance(instance); - } - - protected boolean canOperator(ServiceHolder serviceHolder) { - String serviceId = serviceHolder.getServiceId(); - // 被排除的服务,不能操作 - if (isExcludeService(serviceId)) { - return false; - } - // nacos会不停的触发事件,这里做了一层拦截 - // 同一个serviceId5秒内允许访问一次 - Long lastUpdateTime = updateTimeMap.getOrDefault(serviceId, 0L); - long now = System.currentTimeMillis(); - boolean can = now - lastUpdateTime > FIVE_SECONDS; - if (can) { - updateTimeMap.put(serviceId, now); - } - return can; - } - - /** - * 是否是被排除的服务 - * - * @param serviceId 服务id - * @return true,是 - */ - private boolean isExcludeService(String serviceId) { - List excludeServiceIdList = getExcludeServiceId(); - for (String excludeServiceId : excludeServiceIdList) { - if (excludeServiceId.equalsIgnoreCase(serviceId)) { - return true; - } - } - // 匹配正则 - String sopServiceExcludeRegex = EnvironmentKeys.SOP_SERVICE_EXCLUDE_REGEX.getValue(); - if (StringUtils.isNotBlank(sopServiceExcludeRegex)) { - String[] regexArr = sopServiceExcludeRegex.split(";"); - for (String regex : regexArr) { - if (serviceId.matches(regex)) { - return true; - } - } - } - return false; - } - - private List getExcludeServiceId() { - String excludeServiceIds = EnvironmentKeys.SOP_SERVICE_EXCLUDE.getValue(); - List excludeServiceIdList = new ArrayList<>(8); - if (StringUtils.isNotBlank(excludeServiceIds)) { - String[] serviceIdArr = excludeServiceIds.split(","); - excludeServiceIdList.addAll(Arrays.asList(serviceIdArr)); - } - excludeServiceIdList.addAll(EXCLUDE_SERVICE_ID_LIST); - return excludeServiceIdList; - } - - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseServiceListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseServiceListener.java deleted file mode 100644 index 3d2e4fec..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseServiceListener.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import org.springframework.http.HttpStatus; -import org.springframework.util.DigestUtils; -import org.springframework.web.client.DefaultResponseErrorHandler; -import org.springframework.web.client.RestTemplate; - -/** - * @author 六如 - */ -public abstract class BaseServiceListener implements ServiceListener, RegistryMetadata { - - private static final String SECRET = "a3d9sf!1@odl90zd>fkASwq"; - - private static RestTemplate restTemplate = new RestTemplate(); - - static { - // 解决statusCode不等于200,就抛异常问题 - restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { - @Override - protected boolean hasError(HttpStatus statusCode) { - return statusCode == null; - } - }); - } - - protected static String buildQuery(String secret) { - String time = String.valueOf(System.currentTimeMillis()); - String source = secret + time + secret; - String sign = DigestUtils.md5DigestAsHex(source.getBytes()); - return "?time=" + time + "&sign=" + sign; - } - - protected static String buildQuery() { - return buildQuery(SECRET); - } - - public static RestTemplate getRestTemplate() { - return restTemplate; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ForwardChooser.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ForwardChooser.java deleted file mode 100644 index d58c0506..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ForwardChooser.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -/** - * 转发选择 - * @author 六如 - */ -public interface ForwardChooser { - - /** - * 返回转发信息 - * @param t 上下文 - * @return 返回转发信息 - */ - ForwardInfo getForwardInfo(T t); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ForwardInfo.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ForwardInfo.java deleted file mode 100644 index eedf30b4..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ForwardInfo.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.gitee.sop.gatewaycommon.bean.TargetRoute; -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class ForwardInfo { - - private TargetRoute targetRoute; - - public static ForwardInfo getErrorForwardInfo() { - return ErrorForwardInfo.errorForwardInfo; - } - - public ForwardInfo(TargetRoute targetRoute) { - this.targetRoute = targetRoute; - } - - public String getFullPath() { - return targetRoute.getFullPath(); - } - - public String getPath() { - return targetRoute.getRouteDefinition().getPath(); - } - - public String getVersion() { - return targetRoute.getRouteDefinition().getVersion(); - } - - static class ErrorForwardInfo extends ForwardInfo { - - private static final String VALIDATE_ERROR_PATH = "/sop/validateError"; - - public static ErrorForwardInfo errorForwardInfo = new ErrorForwardInfo(); - - public ErrorForwardInfo() { - this(null); - } - - public ErrorForwardInfo(TargetRoute targetRoute) { - super(targetRoute); - } - - @Override - public String getFullPath() { - return getPath(); - } - - @Override - public String getPath() { - return VALIDATE_ERROR_PATH; - } - - @Override - public String getVersion() { - return ""; - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryEvent.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryEvent.java deleted file mode 100644 index be58ec24..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; - -/** - * 新的实例注册事件 - * - * @author 六如 - */ -public interface RegistryEvent { - - /** - * 新实例注册进来时触发 - * @param instanceDefinition 实例信息 - */ - void onRegistry(InstanceDefinition instanceDefinition); - - /** - * 服务下线时触发 - * @param serviceId 服务id - */ - void onRemove(String serviceId); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryListener.java deleted file mode 100644 index ecb959ea..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryListener.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import org.springframework.context.ApplicationEvent; - -/** - * 发现新服务,更新路由信息 - * - * @author 六如 - */ -public interface RegistryListener { - - void onEvent(ApplicationEvent applicationEvent); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryMetadata.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryMetadata.java deleted file mode 100644 index 115b1e5d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryMetadata.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; - -import java.util.Map; - -/** - * @author 六如 - */ -public interface RegistryMetadata { - - default String getContextPath(Map metadata) { - return metadata.getOrDefault(SopConstants.METADATA_SERVER_CONTEXT_PATH - , metadata.getOrDefault(SopConstants.METADATA_SERVER_CONTEXT_PATH_COMPATIBILITY, "")); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RoutesProcessor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RoutesProcessor.java deleted file mode 100644 index 8d39981f..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RoutesProcessor.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; - -/** - * @author 六如 - */ -public interface RoutesProcessor { - /** - * 删除serviceId下所有路由 - * - * @param serviceId serviceId - */ - void removeAllRoutes(String serviceId); - - /** - * 保存路由 - * - * @param serviceRouteInfo 路由信息 - * @param instance 服务实例 - */ - void saveRoutes(ServiceRouteInfo serviceRouteInfo, InstanceDefinition instance); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java deleted file mode 100644 index 568ada29..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -import java.util.Objects; - -@Getter -@Setter -@AllArgsConstructor -public class ServiceHolder { - private String serviceId; - private long lastUpdatedTimestamp; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ServiceHolder that = (ServiceHolder) o; - return lastUpdatedTimestamp == that.lastUpdatedTimestamp && - serviceId.equals(that.serviceId); - } - - @Override - public int hashCode() { - return Objects.hash(serviceId, lastUpdatedTimestamp); - } -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceListener.java deleted file mode 100644 index d2e0ccd7..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; - -/** - * @author 六如 - */ -public interface ServiceListener { - void onRemoveService(String serviceId); - - void onAddInstance(InstanceDefinition instance); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java deleted file mode 100644 index c4c87f0c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.gitee.sop.gatewaycommon.route; - -import com.alibaba.fastjson.JSON; -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.bean.RouteDefinition; -import com.gitee.sop.gatewaycommon.bean.ServiceBeanInitializer; -import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; -import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.util.DigestUtils; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * @author 六如 - */ -@Slf4j -public class ServiceRouteListener extends BaseServiceListener { - - private static final String SOP_ROUTES_PATH = "/sop/routes"; - - private static final String METADATA_SOP_ROUTES_PATH = "sop.routes.path"; - - @Autowired - private GatewayRouteCache gatewayRouteCache; - - @Autowired - private RoutesProcessor routesProcessor; - - @Autowired - private ApplicationContext applicationContext; - - @Override - public void onRemoveService(String serviceId) { - log.info("服务下线,删除路由配置,serviceId: {}", serviceId); - gatewayRouteCache.remove(serviceId); - routesProcessor.removeAllRoutes(serviceId); - } - - @Override - public void onAddInstance(InstanceDefinition instance) { - String serviceId = instance.getServiceId(); - String url = getRouteRequestUrl(instance); - log.info("拉取路由配置,serviceId: {}, url: {}", serviceId, url); - ResponseEntity responseEntity = getRestTemplate().getForEntity(url, String.class); - if (responseEntity.getStatusCode() == HttpStatus.OK) { - String body = responseEntity.getBody(); - ServiceRouteInfo serviceRouteInfo; - try { - serviceRouteInfo = this.parseServiceRouteInfo(body); - } catch (Exception e) { - log.error("解析路由配置错误,body:{}", body, e); - return; - } - gatewayRouteCache.load(serviceRouteInfo, callback -> routesProcessor.saveRoutes(serviceRouteInfo, instance)); - this.initServiceBeanInitializer(serviceId); - } else { - log.error("拉取路由配置异常,url: {}, status: {}, body: {}", url, responseEntity.getStatusCodeValue(), responseEntity.getBody()); - } - } - - private void initServiceBeanInitializer(String serviceId) { - Map serviceBeanInitializerMap = applicationContext.getBeansOfType(ServiceBeanInitializer.class); - serviceBeanInitializerMap.values().forEach(serviceBeanInitializer -> serviceBeanInitializer.load(serviceId)); - } - - private ServiceRouteInfo parseServiceRouteInfo(String body) { - ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class); - serviceRouteInfo.setServiceId(serviceRouteInfo.getServiceId().toLowerCase()); - List routeDefinitionList = serviceRouteInfo.getRouteDefinitionList(); - String md5 = buildMd5(routeDefinitionList); - serviceRouteInfo.setMd5(md5); - return serviceRouteInfo; - } - - /** - * 构建路由id MD5 - * - * @param routeDefinitionList 路由列表 - * @return 返回MD5 - */ - private String buildMd5(List routeDefinitionList) { - List routeIdList = routeDefinitionList.stream() - .map(JSON::toJSONString) - .sorted() - .collect(Collectors.toList()); - String md5Source = org.apache.commons.lang3.StringUtils.join(routeIdList, ""); - return DigestUtils.md5DigestAsHex(md5Source.getBytes(StandardCharsets.UTF_8)); - } - - protected HttpEntity getHttpEntity() { - HttpHeaders headers = new HttpHeaders(); - return new HttpEntity<>(headers); - } - - /** - * 拉取路由请求url - * - * @param instance 服务实例 - * @return 返回最终url - */ - private String getRouteRequestUrl(InstanceDefinition instance) { - Map metadata = instance.getMetadata(); - String customPath = metadata.get(METADATA_SOP_ROUTES_PATH); - String homeUrl; - String servletPath; - // 如果metadata中指定了获取路由的url - if (StringUtils.isNotBlank(customPath)) { - // 自定义完整的url - if (customPath.startsWith("http")) { - homeUrl = customPath; - servletPath = ""; - } else { - homeUrl = getHomeUrl(instance); - servletPath = customPath; - } - } else { - // 默认处理 - homeUrl = getHomeUrl(instance); - String contextPath = this.getContextPath(metadata); - servletPath = contextPath + SOP_ROUTES_PATH; - } - if (StringUtils.isNotBlank(servletPath) && !servletPath.startsWith("/")) { - servletPath = '/' + servletPath; - } - String query = buildQuery(); - return homeUrl + servletPath + query; - } - - private static String getHomeUrl(InstanceDefinition instance) { - return "http://" + instance.getIp() + ":" + instance.getPort(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/AppSecretManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/AppSecretManager.java deleted file mode 100644 index 036cc465..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/AppSecretManager.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gitee.sop.gatewaycommon.secret; - -import java.util.Map; - -/** - * 负责秘钥管理 - * @author 六如 - */ -public interface AppSecretManager { - - /** - * 初始化秘钥数据 - * @param appSecretStore - */ - void addAppSecret(Map appSecretStore); - - /** - * 获取应用程序的密钥 - * - * @param appKey - * @return 返回秘钥 - */ - String getSecret(String appKey); - - /** - * 是否是合法的appKey - * - * @param appKey - * @return 返回appKey - */ - boolean isValidAppKey(String appKey); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/CacheAppSecretManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/CacheAppSecretManager.java deleted file mode 100644 index d79fa825..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/CacheAppSecretManager.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.gatewaycommon.secret; - -import java.util.HashMap; -import java.util.Map; - -/** - * appkey,secret默认管理,简单放在map中,如果要放在redis中,可以参照此方式实现AppSecretManager,然后在ApiConfig中setAppSecretManager() - * @author 六如 - * - */ -public class CacheAppSecretManager implements AppSecretManager { - - private Map secretMap = new HashMap<>(64); - - @Override - public void addAppSecret(Map appSecretStore) { - secretMap.putAll(appSecretStore); - } - - @Override - public String getSecret(String appKey) { - return secretMap.get(appKey); - } - - @Override - public boolean isValidAppKey(String appKey) { - if (appKey == null) { - return false; - } - return getSecret(appKey) != null; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/CacheIsvManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/CacheIsvManager.java deleted file mode 100644 index bdfda754..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/CacheIsvManager.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gitee.sop.gatewaycommon.secret; - -import com.gitee.sop.gatewaycommon.bean.IsvDefinition; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author 六如 - */ -public class CacheIsvManager implements IsvManager { - - /** - * key: appKey - */ - private Map isvCache = new ConcurrentHashMap<>(64); - - @Override - public void load() { - - } - - @Override - public void update(IsvDefinition isvInfo) { - isvCache.put(isvInfo.getAppKey(), isvInfo); - } - - @Override - public void remove(String appKey) { - isvCache.remove(appKey); - } - - @Override - public IsvDefinition getIsv(String appKey) { - return isvCache.get(appKey); - } - - public Map getIsvCache() { - return isvCache; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/FileAppSecretManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/FileAppSecretManager.java deleted file mode 100644 index 81078e40..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/FileAppSecretManager.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.gitee.sop.gatewaycommon.secret; - -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PropertiesLoaderUtils; - -import java.io.IOException; -import java.util.Map; -import java.util.Properties; - -/** - * appkey,secret文件管理,功能同CacheAppSecretManager,这个是将appKey,secret放在属性文件中
- * key为appKey,value为secret - * @author 六如 - * - */ -public class FileAppSecretManager implements AppSecretManager { - - private String appSecretFile = "appSecret.properties"; - - private Properties properties; - - @Override - public void addAppSecret(Map appSecretStore) { - properties.putAll(appSecretStore); - } - - @Override - public String getSecret(String appKey) { - if (properties == null) { - try { - // 默认加载class根目录的appSecret.properties文件 - DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); - Resource resource = resourceLoader.getResource(appSecretFile); - properties = PropertiesLoaderUtils.loadProperties(resource); - } catch (IOException e) { - throw new IllegalArgumentException("在类路径下找不到appSecret.properties的应用密钥的属性文件"); - } - } - - return properties.getProperty(appKey); - } - - public void setAppSecretFile(String appSecretFile) { - this.appSecretFile = appSecretFile; - } - - @Override - public boolean isValidAppKey(String appKey) { - if(appKey == null){ - return false; - } - return getSecret(appKey) != null; - } -} - diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/IsvManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/IsvManager.java deleted file mode 100644 index 93389c89..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/IsvManager.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.gitee.sop.gatewaycommon.secret; - -import com.gitee.sop.gatewaycommon.bean.BeanInitializer; -import com.gitee.sop.gatewaycommon.bean.Isv; - -/** - * Isv管理 - * - * @author 六如 - */ -public interface IsvManager extends BeanInitializer { - - /** - * 更新isv - * - * @param t isv - */ - void update(T t); - - /** - * 删除isv - * - * @param appKey isv对应的appKey - */ - void remove(String appKey); - - /** - * 获取isv - * - * @param appKey isv对应的key - * @return 返回isv - */ - T getIsv(String appKey); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/support/BaseMonitorController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/support/BaseMonitorController.java deleted file mode 100644 index bc2904d1..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/support/BaseMonitorController.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gitee.sop.gatewaycommon.support; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.monitor.MonitorManager; -import com.gitee.sop.gatewaycommon.result.ApiResult; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * 提供监控数据 - * - * @author 六如 - */ -public abstract class BaseMonitorController extends SopBaseController { - - @GetMapping("/sop/getMonitorData") - public ApiResult doExecute(T request) { - MonitorManager monitorManager = ApiConfig.getInstance().getMonitorManager(); - return execute(request, monitorManager::getMonitorData); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/support/SopBaseController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/support/SopBaseController.java deleted file mode 100644 index 1f54038e..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/support/SopBaseController.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.gitee.sop.gatewaycommon.support; - -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.result.ApiResult; -import com.gitee.sop.gatewaycommon.result.JsonResult; -import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; -import org.springframework.beans.factory.annotation.Value; - -import java.util.function.Supplier; - -/** - * @author 六如 - */ -public abstract class SopBaseController { - - TaobaoSigner signer = new TaobaoSigner(); - - @Value("${sop.secret}") - private String secret; - - protected abstract ApiParam getApiParam(T t); - - public ApiResult execute(T request, Supplier supplier) { - try { - this.check(request); - JsonResult apiResult = new JsonResult(); - apiResult.setData(supplier.get()); - return apiResult; - } catch (Exception e) { - ApiResult apiResult = new ApiResult(); - apiResult.setCode("505050"); - apiResult.setMsg(e.getMessage()); - return apiResult; - } - } - - protected void check(T request) { - ApiParam apiParam = getApiParam(request); - boolean right = signer.checkSign(apiParam, secret); - if (!right) { - throw new RuntimeException("签名校验失败"); - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/MyNamedThreadFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/MyNamedThreadFactory.java deleted file mode 100644 index 267af1d1..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/MyNamedThreadFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.sop.gatewaycommon.sync; - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author thc - */ -public class MyNamedThreadFactory implements ThreadFactory { - private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); - private final AtomicInteger threadNumber = new AtomicInteger(1); - private final ThreadGroup group; - private final String namePrefix; - - public MyNamedThreadFactory(String name) { - - SecurityManager s = System.getSecurityManager(); - group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); - if (null == name || name.isEmpty()) { - name = "pool"; - } - - namePrefix = name + "-" + POOL_NUMBER.getAndIncrement() + "-thread-"; - } - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement()); - if (t.isDaemon()) { - t.setDaemon(false); - } - if (t.getPriority() != Thread.NORM_PRIORITY) { - t.setPriority(Thread.NORM_PRIORITY); - } - return t; - } -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/SopAsyncConfigurer.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/SopAsyncConfigurer.java deleted file mode 100644 index ec0cd138..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/sync/SopAsyncConfigurer.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gitee.sop.gatewaycommon.sync; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; -import org.springframework.scheduling.annotation.AsyncConfigurer; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * 异步执行配置 - * - * @author 六如 - */ -@Slf4j -public class SopAsyncConfigurer implements AsyncConfigurer { - - private final ThreadPoolExecutor threadPoolExecutor; - - public SopAsyncConfigurer(String threadName, int poolSize) { - threadPoolExecutor = new ThreadPoolExecutor(poolSize, poolSize, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), new MyNamedThreadFactory(threadName)); - } - - @Override - public Executor getAsyncExecutor() { - return threadPoolExecutor; - } - - @Override - public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { - return (Throwable e, Method method, Object... args) -> { - log.error("异步运行方法出错, method:{}, args:{}, message:{}", method, Arrays.deepToString(args), e.getMessage(), e); - }; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/AESUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/AESUtil.java deleted file mode 100644 index 21cc95e0..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/AESUtil.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.binary.Hex; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -/** - * AES-128 ECB加密.
- * - *
- * 字符集:UTF-8
- * 算法模式:ECB
- * 数据块:128位
- * 补码方式:PKCS5Padding
- * 加密结果编码方式:Base64
- * 
- * - * @author 六如 - * - */ -public class AESUtil { - private static final String UTF8 = "UTF-8"; - private static final String ALGORITHM = "AES"; - /** 默认的加密算法 */ - private static final String ALGORITHM_CIPHER = "AES/ECB/PKCS5Padding"; - - private static final int LIMIT_LEN = 16; - - /** - * 生成一个SecretKey - * @param password 长度必须小于等于16 - * @return 返回SecretKey - */ - public static SecretKey getSecretKey(String password) { - byte[] passwordData = password.getBytes(); - if(passwordData.length > LIMIT_LEN) { - throw new IllegalArgumentException("password 长度必须小于等于16"); - } - // 创建一个空的16位字节数组(默认值为0),16byte(128bit) - byte[] keyData = new byte[16]; - System.arraycopy(passwordData, 0, keyData, 0, passwordData.length); - - return new SecretKeySpec(keyData, ALGORITHM); - } - - /** - * 加密 - * @param data 待加密数据 - * @param password 密码 - * @return 返回加密成功后数据 - * @throws Exception - */ - public static byte[] encrypt(byte[] data, String password) throws Exception { - SecretKey secretKey = getSecretKey(password); - // Ciphr完成加密或解密工作类 - Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER); - // 对Cipher初始化,解密模式 - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - // 加密data - return cipher.doFinal(data); - } - - /** - * 解密 - * @param data 待解密数据 - * @param password 密码 - * @return 返回解密后的数据 - * @throws Exception - */ - public static byte[] decrypt(byte[] data, String password) throws Exception { - SecretKey secretKey = getSecretKey(password); - // Cipher完成加密或解密工作类 - Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER); - // 对Cipher初始化,解密模式 - cipher.init(Cipher.DECRYPT_MODE, secretKey); - // 解密data - return cipher.doFinal(data); - } - - /** - * 文本加密 - * @param content 明文 - * @param password 密码 - * @return 返回base64内容 - * @throws Exception - */ - public static String encryptToBase64String(String content, String password) throws Exception { - byte[] data = content.getBytes(UTF8); - byte[] result = encrypt(data, password); - return Base64.encodeBase64String(result); - } - - /** - * 文本解密 - * @param base64String 待解密文本 - * @param password 密码 - * @return 返回明文 - * @throws Exception - */ - public static String decryptFromBase64String(String base64String, String password) throws Exception { - byte[] data = Base64.decodeBase64(base64String); - byte[] contentData = decrypt(data, password); - return new String(contentData, UTF8); - } - - /** - * 文本加密 - * @param content 明文 - * @param password 密码 - * @return 返回16进制内容 - * @throws Exception - */ - public static String encryptToHex(String content, String password) throws Exception { - byte[] data = content.getBytes(UTF8); - byte[] result = encrypt(data, password); - return Hex.encodeHexString(result); - } - - /** - * 文本解密 - * @param hex 待解密文本 - * @param password 密码 - * @return 返回明文 - * @throws Exception - */ - public static String decryptFromHex(String hex, String password) throws Exception { - byte[] data = Hex.decodeHex(hex); - byte[] contentData = decrypt(data, password); - return new String(contentData,UTF8); - } - - /*public static void main(String[] args) throws Exception { - String content = "我爱你"; - String password = "1234567890123456"; - System.out.println("password:" + password); - - String ret2 = encryptToBase64String(content, password); - System.out.println("密文:" + ret2); - String content3 = decryptFromBase64String(ret2, password); - System.out.println(content.equals(content3)); - }*/ - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/CopyUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/CopyUtil.java deleted file mode 100644 index 27c253ed..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/CopyUtil.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -/** - * @author 六如 - */ -@Slf4j -public class CopyUtil { - - public static void copyPropertiesIgnoreNull(Object from, Object to) { - MyBeanUtil.copyPropertiesIgnoreNull(from, to); - } - - public static void copyProperties(Object from, Object to) { - BeanUtils.copyProperties(from, to); - } - - public static T copyBean(Object from, Supplier supplier) { - Objects.requireNonNull(from); - T to = supplier.get(); - BeanUtils.copyProperties(from, to); - return to; - } - - public static T copyBeanNullable(Object from, Supplier supplier) { - if (from == null) { - return supplier.get(); - } - T to = supplier.get(); - BeanUtils.copyProperties(from, to); - return to; - } - - public static T copyBean(Object from, Supplier supplier, Consumer after) { - if (from == null) { - return null; - } - T to = supplier.get(); - BeanUtils.copyProperties(from, to); - after.accept(to); - return to; - } - - public static List copyList(List fromList, Supplier toElement) { - if (fromList == null) { - return Collections.emptyList(); - } - return fromList.stream() - .map(source -> { - T target = toElement.get(); - BeanUtils.copyProperties(source, target); - return target; - }) - .collect(Collectors.toList()); - } - - public static List copyList(List fromList, Supplier toElement, Consumer after) { - if (fromList == null) { - return Collections.emptyList(); - } - return fromList.stream() - .map(source -> { - T target = toElement.get(); - BeanUtils.copyProperties(source, target); - after.accept(target); - return target; - }) - .collect(Collectors.toList()); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/KeyStore.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/KeyStore.java deleted file mode 100644 index 62b3e616..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/KeyStore.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -/** - * @author 六如 - */ -public class KeyStore { - private String publicKey; - private String privateKey; - - public String getPublicKey() { - return publicKey; - } - - public void setPublicKey(String publicKey) { - this.publicKey = publicKey; - } - - public String getPrivateKey() { - return privateKey; - } - - public void setPrivateKey(String privateKey) { - this.privateKey = privateKey; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/LoadBalanceUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/LoadBalanceUtil.java deleted file mode 100644 index ed97c1e7..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/LoadBalanceUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadLocalRandom; - -/** - * @author 六如 - */ -public class LoadBalanceUtil { - - /** - * key:serviceId,value:指示变量i - */ - private static Map serviceIdRoundMap = new ConcurrentHashMap<>(8); - - /** - * 轮询选择一台机器。
- *

- * 假设有N台服务器:S = {S1, S2, …, Sn},一个指示变量i表示上一次选择的服务器ID。变量i被初始化为N-1。 - *

- * 参考:https://blog.csdn.net/qq_37469055/article/details/87991327 - * @param serviceId serviceId,不同的serviceId对应的服务器数量不一样,需要区分开 - * @param servers 服务器列表 - * @return 返回一台服务器实例 - */ - public static T chooseByRoundRobin(String serviceId, List servers) { - if (servers == null || servers.isEmpty()) { - return null; - } - int n = servers.size(); - int i = serviceIdRoundMap.computeIfAbsent(serviceId, (k) -> n - 1); - int j = i; - do { - j = (j + 1) % n; - i = j; - serviceIdRoundMap.put(serviceId, i); - return servers.get(i); - } while (j != i); - } - - /** - * 随机选取一台实例 - * - * @param servers 服务列表 - * @return 返回实例,没有返回null - */ - public static T chooseByRandom(List servers) { - if (servers.isEmpty()) { - return null; - } - int serverCount = servers.size(); - // 随机选取一台实例 - int index = chooseRandomInt(serverCount); - return servers.get(index); - } - - private static int chooseRandomInt(int serverCount) { - return ThreadLocalRandom.current().nextInt(serverCount); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/MyBeanUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/MyBeanUtil.java deleted file mode 100644 index 3fe1e94b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/MyBeanUtil.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import org.springframework.beans.BeansException; -import org.springframework.beans.FatalBeanException; -import org.springframework.util.Assert; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * 对象拷贝 - * - * @author 六如 - */ -public class MyBeanUtil extends org.springframework.beans.BeanUtils { - - /** - * 属性拷贝,第一个参数中的属性值拷贝到第二个参数中
- * 注意:当第一个参数中的属性有null值时,不会拷贝进去 - * - * @param source 源对象 - * @param target 目标对象 - * @throws BeansException - */ - public static void copyPropertiesIgnoreNull(Object source, Object target) - throws BeansException { - Assert.notNull(source, "Source must not be null"); - Assert.notNull(target, "Target must not be null"); - Class actualEditable = target.getClass(); - PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); - for (PropertyDescriptor targetPd : targetPds) { - Method writeMethod = targetPd.getWriteMethod(); - if (writeMethod != null) { - PropertyDescriptor sourcePd = getPropertyDescriptor( - source.getClass(), targetPd.getName()); - if (sourcePd != null && sourcePd.getReadMethod() != null) { - try { - Method readMethod = sourcePd.getReadMethod(); - if (!Modifier.isPublic(readMethod.getDeclaringClass() - .getModifiers())) { - readMethod.setAccessible(true); - } - Object value = readMethod.invoke(source); - // 这里判断value是否为空 当然这里也能进行一些特殊要求的处理 - // 例如绑定时格式转换等等 - if (value != null) { - if (!Modifier.isPublic(writeMethod - .getDeclaringClass().getModifiers())) { - writeMethod.setAccessible(true); - } - writeMethod.invoke(target, value); - } - } catch (Throwable ex) { - throw new FatalBeanException( - "Could not copy properties from source to target field name mismatch:" + targetPd.getName(), - ex); - } - } - } - } - } - - - /** - * 属性拷贝,把map中的值拷贝到target中去 - * - * @param map map对象 - * @param target 目标对象 - */ - public static void copyPropertiesForMap(Map map, Object target) { - Assert.notNull(map, "map must not be null"); - Assert.notNull(target, "Target must not be null"); - Object pojo = mapToPojo(map, target.getClass()); - copyProperties(pojo, target); - } - - /** - * 将实体对象转换成Map - * - * @param pojo 实体类 - * @return 返回map - */ - public static Map pojoToMap(Object pojo) { - if (pojo == null) { - return Collections.emptyMap(); - } - String json = JSON.toJSONString(pojo); - return JSON.parseObject(json); - } - - - /** - * 将map对象转换成普通类 - * - * @param 普通类类型 - * @param map map对象 - * @param pojoClass 普通类 - * @return 返回普通类 - */ - public static T mapToPojo(Map map, Class pojoClass) { - return new JSONObject(map).toJavaObject(pojoClass); - } - - /** - * map集合转换成对象集合 - * - * @param 普通类类型 - * @param list map集合 - * @param pojoClass 待转换的对象类型 - * @return 返回对象集合 - */ - public static List mapListToObjList(List> list, Class pojoClass) { - if (list == null) { - return Collections.emptyList(); - } - List retList = new ArrayList<>(list.size()); - for (Map map : list) { - retList.add(mapToPojo(map, pojoClass)); - } - return retList; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSANewUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSANewUtil.java deleted file mode 100644 index 0c69f9d9..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSANewUtil.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import org.apache.commons.codec.binary.Base64; - -import javax.crypto.Cipher; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; - -/** - * RSA加解密工具
- * @author 六如 - */ -public class RSANewUtil { - public static final String RSA_ALGORITHM = "RSA"; - public static final String UTF8 = "UTF-8"; - - /** - * 创建公钥私钥 - * - * @return 返回公私钥对 - * @throws Exception - */ - public static KeyStore createKeys() throws Exception { - KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM); - keyPairGeno.initialize(1024); - KeyPair keyPair = keyPairGeno.generateKeyPair(); - - RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); - RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); - - KeyStore keyStore = new KeyStore(); - keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); - keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded())); - return keyStore; - } - - /** - * 获取公钥对象 - * - * @param pubKeyData 公钥数据 - * @return 公钥对象 - * @throws Exception - */ - public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception { - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData); - KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); - return (RSAPublicKey) keyFactory.generatePublic(keySpec); - } - - /** - * 获取公钥对象 - * - * @param pubKey - * 公钥 - * @return 返回公钥对象 - * @throws Exception - */ - public static RSAPublicKey getPublicKey(String pubKey) throws Exception { - return getPublicKey(Base64.decodeBase64(pubKey)); - - } - - /** - * 获取私钥对象 - * - * @param priKey - * 私钥 - * @return 返回私钥对象 - * @throws Exception - */ - public static RSAPrivateKey getPrivateKey(String priKey) throws Exception { - return getPrivateKey(Base64.decodeBase64(priKey)); - } - - /** - * 通过私钥byte[]将公钥还原,适用于RSA算法 - * - * @param keyBytes 私钥数据 - * @return 返回公钥对象 - * @throws Exception - */ - public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); - return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); - - } - - public static String encryptByPublicKey(String data, String publicKey) throws Exception { - return encryptByPublicKey(data, getPublicKey(publicKey)); - } - - /** - * 公钥加密 - * - * @param data 内容 - * @param publicKey 公钥 - * @return 返回密文 - * @throws Exception - */ - public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - byte[] bytes = cipher.doFinal(data.getBytes(UTF8)); - return Base64.encodeBase64String(bytes); - } - - public static String decryptByPublicKey(String data, String rsaPublicKey) throws Exception { - return decryptByPublicKey(data, getPublicKey(rsaPublicKey)); - } - - /** - * 公钥解密 - * - * @param data 待解密内容 - * @param rsaPublicKey 公钥 - * @return 返回明文 - * @throws Exception - */ - public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); - byte[] inputData = Base64.decodeBase64(data); - byte[] bytes = cipher.doFinal(inputData); - return new String(bytes, UTF8); - } - - public static String encryptByPrivateKey(String data, String privateKey) throws Exception { - return encryptByPrivateKey(data, getPrivateKey(privateKey)); - } - - /** - * 私钥加密 - * - * @param data 内容 - * @param privateKey 私钥 - * @return 返回密文 - * @throws Exception - */ - public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, privateKey); - byte[] bytes = cipher.doFinal(data.getBytes(UTF8)); - return Base64.encodeBase64String(bytes); - } - - public static String decryptByPrivateKey(String data, String privateKey) throws Exception { - return decryptByPrivateKey(data, getPrivateKey(privateKey)); - } - - /** - * 私钥解密 - * - * @param data 待解密内容 - * @param privateKey 私钥 - * @return 返回明文 - * @throws Exception - */ - public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, privateKey); - byte[] inputData = Base64.decodeBase64(data); - byte[] bytes = cipher.doFinal(inputData); - return new String(bytes, UTF8); - } - - - /* - pubKey: -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCG/iIZZzb16PxKqslkDMYa4tVFb3IVPBpLj4BgHQmDfe843sG4gkJIPXCm7+t6QxIbfDfynBpqZJLvu0c6E7TqlCtynBIlRFOBZrQVNEFkaanR2Kln3vd3CIidR571UstOC32XDyqAQNlvjD19zeIDVfmLa0Q+Or0zaxY99QwBHwIDAQAB -priKey: -MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIb+IhlnNvXo/EqqyWQMxhri1UVvchU8GkuPgGAdCYN97zjewbiCQkg9cKbv63pDEht8N/KcGmpkku+7RzoTtOqUK3KcEiVEU4FmtBU0QWRpqdHYqWfe93cIiJ1HnvVSy04LfZcPKoBA2W+MPX3N4gNV+YtrRD46vTNrFj31DAEfAgMBAAECgYBiNPQdwwcq86rHr2QAE4L0AF3ju+YlKKqAmg9s3PMU5ENq/jO0xZ7u6zPPXu/S7IR51m7lY0ecazqyiW6SA9AzYH7ImWWkZ4stZ03beTB2US3cSeJIkugoexoN5fQRAGZiZezTLs91CeJivESOZyDKnnQdgJ49mveBV5OvievD8QJBAMztpqiWWavdR4tqQ+plat+rwYoXqejsK3Hyfg0pVJqEdazve2sr74rla7yI9P47ZAh1sklCv0CO//ctICv366UCQQCoop3T0FeZtbKJG+fHzZvpAe63tXpdhLMaQvTBuXLG8vi78Wyfhg5r7HOWR0Z1V7nzF1gzMywL53Pmkq9tB65zAkAiHu/A4kfL9ewTqn3kaT6CP3baJ1aDEc+qCVYzms4bbDKruLQ0A/y+g7SMj8E7E2h0gCRPTm3JsgWsgjb5Gy6BAkAA8mjQd6sGQe7utilnBdCKTmh4v5wgSk53J0kYjWIHm/WpmIFzo90Q3hMIFP5gSk3Q/6CPKQpmRrZv5QL3KcPhAkEAuMoQbij/7hyLlIxRHZs2SMXxfHPiZgDc6rVi1KNxeq8HXTlERi7Npc2Uz5TeWN4JwBBx9uA50zowk9iS05nclQ== -用公钥加密mi : c3B0jtMdvkqrgaPxHZCK2cXMUQC2QzLud2ouLMNx0nBAj9k2/ytOuVJViTGe/DozB/ky5jvl4spD9Ey6aTMrwLHfQVhn0gRJ+wHcmx/51dXQDIgsldt6bf7YpdPdnghBjQz2+P5RhqSkeFDbTZKkl2BNaLE78a/OyWWeCGwN+4s= -true -用私钥加密mi2 : QU5vDnQ1ukj8GsauokFlgcB/g61U882tj82wHGrrqHEnvaga+4cXjML9RhjpZtKqwDGZTCujsmpynDk4qek6IGOQ/oxdWLwV4ZNjfa/oqA8OFDothVUT8wpqCu9kOYHrTdGybmXD0dB2Iy1/AMQTAgPNNXXiRXdvsz9xWYTV6z8= -true - */ - /*public static void main(String[] args) throws Exception { - KeyStore keys = createKeys(); - String pubKey = keys.getPublicKey(); - System.out.println("pubKey:"); - System.out.println(pubKey); - String priKey = keys.getPrivateKey(); - System.out.println("priKey:"); - System.out.println(priKey); - - String ming = "1234567890123456"; - // 用公钥加密 - String mi = encryptByPublicKey(ming, pubKey); - System.out.println("用公钥加密mi : " + mi); - // 用私钥解密 - System.out.println(ming.equals(decryptByPrivateKey(mi, priKey))); - - // 用私钥加密 - String mi2 = encryptByPrivateKey(ming, priKey); - - System.out.println("用私钥加密mi2 : " + mi2); - // 用公钥解密 - String ming2 = decryptByPublicKey(mi2, pubKey); - System.out.println(ming.equals(ming2)); - }*/ - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSAUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSAUtil.java deleted file mode 100644 index 6f81d310..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSAUtil.java +++ /dev/null @@ -1,312 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import org.apache.commons.codec.binary.Base64; - -import javax.crypto.Cipher; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; - -/** - * RSA加解密工具
- * @author 六如 - */ -public class RSAUtil { - public static String RSA_ALGORITHM = "RSA"; - - /** - * 创建公钥私钥 - * - * @return 返回公私钥对 - * @throws Exception - */ - public static KeyStore createKeys() throws Exception { - KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM); - keyPairGeno.initialize(1024); - KeyPair keyPair = keyPairGeno.generateKeyPair(); - - RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); - RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); - - KeyStore keyStore = new KeyStore(); - keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); - keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded())); - return keyStore; - } - - /** - * 获取公钥对象 - * - * @param pubKeyData 公钥 - * @return 返回公钥对象 - * @throws Exception - */ - public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception { - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData); - KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); - return (RSAPublicKey) keyFactory.generatePublic(keySpec); - } - - /** - * 获取公钥对象 - * - * @param pubKey - * 公钥 - * @return 返回私钥对象 - * @throws Exception - */ - public static RSAPublicKey getPublicKey(String pubKey) throws Exception { - return getPublicKey(Base64.decodeBase64(pubKey)); - - } - - /** - * 获取私钥对象 - * - * @param priKey - * 私钥 - * @return 私钥对象 - * @throws Exception - */ - public static RSAPrivateKey getPrivateKey(String priKey) throws Exception { - return getPrivateKey(Base64.decodeBase64(priKey)); - } - - /** - * 通过私钥byte[]将公钥还原,适用于RSA算法 - * - * @param keyBytes - * @return 返回私钥 - * @throws Exception - */ - public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); - return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); - - } - - /** - * 公钥加密 - * - * @param data 待加密内容 - * @param publicKey 公钥 - * @return 返回密文 - * @throws Exception - */ - public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - // 模长 - int key_len = publicKey.getModulus().bitLength() / 8; - // 加密数据长度 <= 模长-11 - String[] datas = splitString(data, key_len - 11); - String mi = ""; - // 如果明文长度大于模长-11则要分组加密 - for (String s : datas) { - mi += bcd2Str(cipher.doFinal(s.getBytes())); - } - return mi; - } - public static String encryptByPrivateKey(String data, String privateKey) throws Exception { - return encryptByPrivateKey(data, getPrivateKey(privateKey)); - } - - /** - * 私钥加密 - * - * @param data 待加密数据 - * @param privateKey 私钥 - * @return 返回密文 - * @throws Exception - */ - public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, privateKey); - // 模长 - int key_len = privateKey.getModulus().bitLength() / 8; - // 加密数据长度 <= 模长-11 - String[] datas = splitString(data, key_len - 11); - String mi = ""; - // 如果明文长度大于模长-11则要分组加密 - for (String s : datas) { - mi += bcd2Str(cipher.doFinal(s.getBytes())); - } - return mi; - } - - public static String decryptByPrivateKey(String data, String privateKey) throws Exception { - return decryptByPrivateKey(data, getPrivateKey(privateKey)); - } - - /** - * 私钥解密 - * - * @param data 待解密内容 - * @param privateKey 私钥 - * @return 返回明文 - * @throws Exception - */ - public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, privateKey); - // 模长 - int key_len = privateKey.getModulus().bitLength() / 8; - byte[] bytes = data.getBytes(); - byte[] bcd = ASCII_To_BCD(bytes, bytes.length); - // 如果密文长度大于模长则要分组解密 - String ming = ""; - byte[][] arrays = splitArray(bcd, key_len); - for (byte[] arr : arrays) { - ming += new String(cipher.doFinal(arr)); - } - return ming; - } - - /** - * 公钥解密 - * - * @param data 待解密内容 - * @param rsaPublicKey 公钥 - * @return 返回明文 - * @throws Exception - */ - public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception { - Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); - // 模长 - int key_len = rsaPublicKey.getModulus().bitLength() / 8; - byte[] bytes = data.getBytes(); - byte[] bcd = ASCII_To_BCD(bytes, bytes.length); - // 如果密文长度大于模长则要分组解密 - String ming = ""; - byte[][] arrays = splitArray(bcd, key_len); - for (byte[] arr : arrays) { - ming += new String(cipher.doFinal(arr)); - } - return ming; - } - - - /** - * ASCII码转BCD码 - * - */ - public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) { - byte[] bcd = new byte[asc_len / 2]; - int j = 0; - for (int i = 0; i < (asc_len + 1) / 2; i++) { - bcd[i] = asc_to_bcd(ascii[j++]); - bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++]) & 0xff) + (bcd[i] << 4)); - } - return bcd; - } - - public static byte asc_to_bcd(byte asc) { - byte bcd; - - if ((asc >= '0') && (asc <= '9')) { - bcd = (byte) (asc - '0'); - } else if ((asc >= 'A') && (asc <= 'F')) { - bcd = (byte) (asc - 'A' + 10); - } else if ((asc >= 'a') && (asc <= 'f')) { - bcd = (byte) (asc - 'a' + 10); - } else { - bcd = (byte) (asc - 48); - } - return bcd; - } - - /** - * BCD转字符串 - */ - public static String bcd2Str(byte[] bytes) { - char[] temp = new char[bytes.length * 2]; - char val; - - for (int i = 0; i < bytes.length; i++) { - val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f); - temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); - - val = (char) (bytes[i] & 0x0f); - temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); - } - return new String(temp); - } - - /** - * 拆分字符串 - */ - public static String[] splitString(String string, int len) { - int x = string.length() / len; - int y = string.length() % len; - int z = 0; - if (y != 0) { - z = 1; - } - String[] strings = new String[x + z]; - String str = ""; - for (int i = 0; i < x + z; i++) { - if (i == x + z - 1 && y != 0) { - str = string.substring(i * len, i * len + y); - } else { - str = string.substring(i * len, i * len + len); - } - strings[i] = str; - } - return strings; - } - - /** - * 拆分数组 - */ - public static byte[][] splitArray(byte[] data, int len) { - int x = data.length / len; - int y = data.length % len; - int z = 0; - if (y != 0) { - z = 1; - } - byte[][] arrays = new byte[x + z][]; - byte[] arr; - for (int i = 0; i < x + z; i++) { - arr = new byte[len]; - if (i == x + z - 1 && y != 0) { - System.arraycopy(data, i * len, arr, 0, y); - } else { - System.arraycopy(data, i * len, arr, 0, len); - } - arrays[i] = arr; - } - return arrays; - } - - /*public static void main(String[] args) throws Exception { - KeyStore keys = createKeys(); - String pubKey = keys.getPublicKey(); - System.out.println("pubKey:"); - System.out.println(pubKey); - String priKey = keys.getPrivateKey(); - System.out.println("priKey:"); - System.out.println(priKey); - - String ming = "6460201d23954f8e90cf79b818844ca0"; - // 用公钥加密 - String mi = encryptByPublicKey(ming, getPublicKey(pubKey)); - System.out.println("mi : " + mi); - // 用私钥解密 - System.out.println("ming : " + decryptByPrivateKey(mi, getPrivateKey(priKey))); - - // 用私钥加密 - String mi2 = encryptByPrivateKey(ming, getPrivateKey(priKey)); - - System.out.println("mi2 : " + mi2); - // 用公钥解密 - System.out.println("ming2 : " + decryptByPublicKey(mi2, getPublicKey(pubKey))); - }*/ - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RedisLockUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RedisLockUtil.java deleted file mode 100644 index 870c1cdd..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RedisLockUtil.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.script.RedisScript; - -import java.util.Collections; - -/** - * redis分布式锁,详见:https://blog.csdn.net/thc1987/article/details/80355155
- * 思路: - *
- * 用SETNX命令,SETNX只有在key不存在时才返回成功。这意味着只有一个线程可以成功运行SETNX命令,而其他线程会失败,然后不断重试,直到它们能建立锁。
- * 然后使用脚本来创建锁,因为一个redis脚本同一时刻只能运行一次。
- * 创建锁代码:
- * 
--- KEYS[1] key,
--- ARGV[1] value,
--- ARGV[2] expireTimeMilliseconds
-
-if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then
-    redis.call('pexpire', KEYS[1], ARGV[2])
-    return 1
-else
-    return 0
-end
- * 
- * 最后使用脚本来解锁。
- * 解锁代码:
- *
- * 
--- KEYS[1] key,
--- ARGV[1] value
-if redis.call("get", KEYS[1]) == ARGV[1]
-then
-    return redis.call("del", KEYS[1])
-else
-    return 0
-end
- * 
- * 
- * - * @author 六如 - */ -public class RedisLockUtil { - - private RedisLockUtil(){} - - private static final Long SUCCESS = 1L; - - /** 加锁脚本 */ - private static final String SCRIPT_LOCK = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('pexpire', KEYS[1], ARGV[2]) return 1 else return 0 end"; - /** 解锁脚本 */ - private static final String SCRIPT_UNLOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; - /** 加锁脚本sha1值 */ - private static final String SCRIPT_LOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_LOCK); - /** 解锁脚本sha1值 */ - private static final String SCRIPT_UNLOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_UNLOCK); - - /** - * 尝试获取分布式锁 - * - * @param redisTemplate - * Redis客户端 - * @param lockKey - * 锁 - * @param requestId - * 请求标识 - * @param expireTimeMilliseconds - * 超期时间,多少毫秒后这把锁自动释放 - * @return 返回true表示拿到锁 - */ - @SuppressWarnings("unchecked") - public static boolean tryGetDistributedLock(@SuppressWarnings("rawtypes") final RedisTemplate redisTemplate, - final String lockKey, final String requestId, final int expireTimeMilliseconds) { - - Object result = redisTemplate.execute(new RedisScript() { - @Override - public String getSha1() { - return SCRIPT_LOCK_SHA1; - } - - @Override - public Class getResultType() { - return Long.class; - } - - @Override - public String getScriptAsString() { - return SCRIPT_LOCK; - } - - }, - // KEYS[1] - Collections.singletonList(lockKey), - // ARGV[1] - requestId, - // ARGV[2] - String.valueOf(expireTimeMilliseconds) - ); - - return SUCCESS.equals(result); - } - - /** - * 释放分布式锁 - * - * @param redisTemplate - * Redis客户端 - * @param lockKey - * 锁 - * @param requestId - * 请求标识 - * @return 返回true表示释放锁成功 - */ - @SuppressWarnings("unchecked") - public static boolean releaseDistributedLock(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate, - String lockKey, String requestId) { - - Object result = redisTemplate.execute(new RedisScript() { - @Override - public String getSha1() { - return SCRIPT_UNLOCK_SHA1; - } - - @Override - public Class getResultType() { - return Long.class; - } - - @Override - public String getScriptAsString() { - return SCRIPT_UNLOCK; - } - - }, Collections.singletonList(lockKey), requestId); - - return SUCCESS.equals(result); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java deleted file mode 100644 index 418716d6..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java +++ /dev/null @@ -1,350 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import com.alibaba.fastjson.JSON; -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.param.ApiUploadContext; -import com.gitee.sop.gatewaycommon.param.UploadContext; -import lombok.Data; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.util.MultiValueMap; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.commons.CommonsMultipartFile; -import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - - -/** - * @author 六如 - */ -public class RequestUtil { - - private static final Logger log = LoggerFactory.getLogger(RequestUtil.class); - - private RequestUtil() { - } - - public static final String MULTIPART = "multipart/"; - - private static final String UTF8 = "UTF-8"; - private static final String IP_UNKNOWN = "unknown"; - private static final String IP_LOCAL = "127.0.0.1"; - private static final int IP_LEN = 15; - - /** - * 将get类型的参数转换成map, - * - * @param query charset=utf-8&biz_content=xxx - * @return 返回map参数 - */ - public static Map parseQueryToMap(String query) { - if (query == null) { - return Collections.emptyMap(); - } - String[] queryList = StringUtils.split(query, '&'); - Map params = new HashMap<>(16); - for (String param : queryList) { - String[] paramArr = param.split("\\="); - if (paramArr.length == 2) { - try { - params.put(paramArr[0], URLDecoder.decode(paramArr[1], UTF8)); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } else if (paramArr.length == 1) { - params.put(paramArr[0], ""); - } - } - return params; - } - - /** - * 将map参数转换成查询参数 - * - * @return 返回aa=1&b=c... - */ - public static String convertMapToQueryString(Map apiParam) { - List list = new ArrayList<>(apiParam.size()); - try { - for (Map.Entry entry : apiParam.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - if (value instanceof Collection) { - Collection collection = (Collection) value; - for (Object el : collection) { - list.add(key + "=" + URLEncoder.encode(String.valueOf(el), SopConstants.UTF8)); - } - } else { - list.add(key + "=" + URLEncoder.encode(String.valueOf(value), SopConstants.UTF8)); - } - } - } catch (UnsupportedEncodingException e) { - log.error("字符集不支持", e); - } - return org.apache.commons.lang3.StringUtils.join(list, "&"); - } - - /** - * request中的参数转换成map - * - * @param request request对象 - * @return 返回参数键值对 - */ - public static Map convertRequestParamsToMap(HttpServletRequest request) { - Map paramMap = request.getParameterMap(); - if (paramMap == null || paramMap.isEmpty()) { - return Collections.emptyMap(); - } - Map retMap = new HashMap<>(paramMap.size() * 2); - - Set> entrySet = paramMap.entrySet(); - - for (Map.Entry entry : entrySet) { - String name = entry.getKey(); - String[] values = entry.getValue(); - if (values.length >= 1) { - retMap.put(name, values[0]); - } else { - retMap.put(name, ""); - } - } - return retMap; - } - - /** - * 获取文件上传表单中的字段,不包括文件,请求类型是multipart/form-data - * - * @param request - * @return 返回表单中的字段内容 - */ - public static Map convertMultipartRequestToMap(HttpServletRequest request) { - // 创建一个文件上传解析器 - ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); - Map params = new HashMap<>(16); - try { - List fileItems = upload.parseRequest(request); - for (FileItem fileItem : fileItems) { - if (fileItem.isFormField()) { - params.put(fileItem.getFieldName(), fileItem.getString(UTF8)); - } - } - } catch (Exception e) { - log.error("参数解析错误", e); - } - return params; - } - - /** - * 转换json请求到Map, - * - * @param request 请求类型为application/json的Request - * @return 返回Map - */ - public static Map convertJsonRequestToMap(HttpServletRequest request) { - try { - String text = getText(request); - return JSON.parseObject(text); - } catch (IOException e) { - log.error("解析json请求失败", e); - return Collections.emptyMap(); - } - } - - /** - * 是否是文件上传请求 - * - * @param request 请求 - * @return true:是 - */ - public static boolean isMultipart(HttpServletRequest request) { - String contentType = request.getContentType(); - // Don't use this filter on GET method - if (contentType == null) { - return false; - } - return contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART); - } - - - public static String getText(HttpServletRequest request) throws IOException { - return IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8); - } - - /** - * 获取客户端IP - * - * @param request request - * @return 返回ip - */ - public static String getIP(HttpServletRequest request) { - String ipAddress = request.getHeader("x-forwarded-for"); - if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = request.getHeader("Proxy-Client-IP"); - } - if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = request.getHeader("WL-Proxy-Client-IP"); - } - if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = request.getRemoteAddr(); - if (IP_LOCAL.equals(ipAddress)) { - // 根据网卡取本机配置的IP - try { - InetAddress inet = InetAddress.getLocalHost(); - ipAddress = inet.getHostAddress(); - } catch (UnknownHostException e) { - // ignore - } - } - - } - - // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 - if (ipAddress != null && ipAddress.length() > IP_LEN) { - if (ipAddress.indexOf(",") > 0) { - ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); - } - } - return ipAddress; - } - - /** - * 获取客户端真实ip - * @param request request - * @return 返回ip - */ - public static String getIP(ServerHttpRequest request) { - HttpHeaders headers = request.getHeaders(); - String ipAddress = headers.getFirst("x-forwarded-for"); - if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = headers.getFirst("Proxy-Client-IP"); - } - if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = headers.getFirst("WL-Proxy-Client-IP"); - } - if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = Optional.ofNullable(request.getRemoteAddress()) - .map(address -> address.getAddress().getHostAddress()) - .orElse(""); - if (IP_LOCAL.equals(ipAddress)) { - // 根据网卡取本机配置的IP - try { - InetAddress inet = InetAddress.getLocalHost(); - ipAddress = inet.getHostAddress(); - } catch (UnknownHostException e) { - // ignore - } - } - } - - // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 - if (ipAddress != null && ipAddress.length() > IP_LEN) { - int index = ipAddress.indexOf(","); - if (index > 0) { - ipAddress = ipAddress.substring(0, index); - } - } - return ipAddress; - } - - /** - * 获取上传文件内容 - * - * @param request request - * @return 返回文件内容和表单内容 - */ - public static UploadInfo getUploadInfo(HttpServletRequest request) { - if (request instanceof StandardMultipartHttpServletRequest) { - return getUploadInfo((StandardMultipartHttpServletRequest)request); - } - UploadInfo uploadInfo = new UploadInfo(); - // 创建一个文件上传解析器 - ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); - Map uploadParams = new HashMap<>(16); - UploadContext uploadContext = null; - try { - List multipartFileList = new ArrayList<>(16); - List fileItems = upload.parseRequest(request); - for (FileItem fileItem : fileItems) { - if (fileItem.isFormField()) { - uploadParams.put(fileItem.getFieldName(), fileItem.getString(SopConstants.UTF8)); - } else { - multipartFileList.add(new CommonsMultipartFile(fileItem)); - } - } - if (multipartFileList.size() > 0) { - Map> multipartFileMap = multipartFileList - .stream() - .collect(Collectors.groupingBy(MultipartFile::getName)); - uploadContext = new ApiUploadContext(multipartFileMap); - } - uploadInfo.setUploadParams(uploadParams); - uploadInfo.setUploadContext(uploadContext); - } catch (Exception e) { - log.error("参数解析错误", e); - } - return uploadInfo; - } - - public static UploadInfo getUploadInfo(StandardMultipartHttpServletRequest request) { - UploadInfo uploadInfo = new UploadInfo(); - Map uploadParams = new HashMap<>(16); - request.getParameterMap().forEach((key, value)-> uploadParams.put(key, value[0])); - MultiValueMap multiFileMap = request.getMultiFileMap(); - List multipartFileList = new ArrayList<>(10); - for (String key : multiFileMap.keySet()) { - multipartFileList.addAll(multiFileMap.get(key)); - } - Map> multipartFileMap = multipartFileList - .stream() - .collect(Collectors.groupingBy(MultipartFile::getName)); - UploadContext uploadContext = new ApiUploadContext(multipartFileMap); - - uploadInfo.setUploadParams(uploadParams); - uploadInfo.setUploadContext(uploadContext); - return uploadInfo; - } - - public static void checkResponseBody(String responseBody, String sign, String secret) throws Exception { - if (sign == null) { - throw new Exception("签名不存在"); - } - String signContent = secret + responseBody + secret; - String clientSign = DigestUtils.md5Hex(signContent); - if (!sign.equals(clientSign)) { - throw new Exception("签名错误"); - } - } - - @Data - public static class UploadInfo { - private Map uploadParams; - private UploadContext uploadContext; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java deleted file mode 100644 index 8960f660..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; -import lombok.Data; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; - -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author 六如 - */ -public class ResponseUtil { - private ResponseUtil(){} - - public static final String UTF_8 = "UTF-8"; - - private static Logger log = LoggerFactory.getLogger(ResponseUtil.class); - - public static void writeJson(HttpServletResponse response, Object result) { - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.setCharacterEncoding(UTF_8); - try { - response.getWriter().write(result instanceof String ? (String)result : JSON.toJSONString(result)); - } catch (IOException e) { - log.error("writeJson error", e); - } - } - - /** - * map转成xml - * - * @param parameters - * @return 返回xml内容 - */ - public static String mapToXml(JSONObject parameters) { - String content = doMap2xml(parameters); - return content; - } - - private static String doMap2xml(JSONObject parameters) { - StringBuilder sb = new StringBuilder(); - Set es = parameters.entrySet(); - Iterator it = es.iterator(); - while (it.hasNext()) { - Map.Entry entry = (Map.Entry) it.next(); - String k = (String) entry.getKey(); - Object v = entry.getValue(); - if (v instanceof JSONObject) { - sb.append("<").append(k).append(">") - .append(doMap2xml((JSONObject) v)) - .append(""); - } else if (v instanceof JSONArray) { - JSONArray collection = (JSONArray) v; - String items = buildItems(k + "_item", collection); - sb.append("<").append(k).append(">") - .append(items) - .append(""); - } else { - sb.append("<").append(k).append(">"); - } - } - return sb.toString(); - } - - private static String buildItems(String key, JSONArray collection) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < collection.size(); i++) { - Object jsonObject = collection.get(i); - sb.append("<").append(key).append(">"); - if (jsonObject instanceof JSONObject) { - sb.append(doMap2xml((JSONObject) jsonObject)); - } else { - sb.append(jsonObject); - } - sb.append(""); - } - return sb.toString(); - } - - @Data - public static class Persion { - int id; - String name; - List items = Arrays.asList("item1", "item2"); - List child = Arrays.asList(new Man("Jim"), new Man("Tom")); - } - - @Data - public static class Man{ - String name; - - public Man(String name) { - this.name = name; - } - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteInterceptorUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteInterceptorUtil.java deleted file mode 100644 index 4b92485a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteInterceptorUtil.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import lombok.extern.slf4j.Slf4j; - -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.function.Consumer; - -/** - * @author 六如 - */ -@Slf4j -public class RouteInterceptorUtil { - - public static void runPreRoute(Object requestContext, ApiParam param, Consumer saveContext) { - DefaultRouteInterceptorContext defaultRouteInterceptorContext = new DefaultRouteInterceptorContext(); - saveContext.accept(defaultRouteInterceptorContext); - defaultRouteInterceptorContext.setBeginTimeMillis(System.currentTimeMillis()); - defaultRouteInterceptorContext.setRequestContext(requestContext); - defaultRouteInterceptorContext.setApiParam(param); - getRouteInterceptors().forEach(routeInterceptor -> { - if (routeInterceptor.match(defaultRouteInterceptorContext)) { - routeInterceptor.preRoute(defaultRouteInterceptorContext); - } - }); - } - - public static void runAfterRoute(RouteInterceptorContext routeInterceptorContext) { - if (routeInterceptorContext == null) { - return; - } - try { - getRouteInterceptors().forEach(routeInterceptor -> { - if (routeInterceptor.match(routeInterceptorContext)) { - routeInterceptor.afterRoute(routeInterceptorContext); - } - }); - } catch (Exception e) { - log.error("执行路由拦截器异常, apiParam:{}", routeInterceptorContext.getApiParam().toJSONString()); - } - } - - public static List getRouteInterceptors() { - return ApiConfig.getInstance().getRouteInterceptors(); - } - - public static void addInterceptors(Collection interceptors) { - List routeInterceptors = getRouteInterceptors(); - routeInterceptors.addAll(interceptors); - routeInterceptors.sort(Comparator.comparing(RouteInterceptor::getOrder)); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java deleted file mode 100644 index bed9c632..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gitee.sop.gatewaycommon.util; - -import org.springframework.util.StringUtils; - -/** - * @author 六如 - */ -public class RouteUtil { - - private RouteUtil() { - } - - private static final String REGEX = "\\#"; - - public static final String PROTOCOL_LOAD_BALANCE = "lb://"; - - public static String findPath(String uri) { - // #后面是对应的path - String[] uriArr = uri.split(REGEX); - if (uriArr.length == 2) { - return uriArr[1]; - } else { - return null; - } - } - - /** - * 将springmvc接口路径转换成SOP方法名 - * - * @param path springmvc路径,如:/goods/listGoods - * @return 返回接口方法名,/goods/listGoods -> goods.listGoods - */ - public static String buildApiName(String path) { - char separatorChar = '/'; - path = StringUtils.trimLeadingCharacter(path, separatorChar); - path = StringUtils.trimTrailingCharacter(path, separatorChar); - return path.replace(separatorChar, '.'); - } - - public static String getZuulLocation(String uri) { - if (uri.toLowerCase().startsWith(PROTOCOL_LOAD_BALANCE)) { - return uri.substring(PROTOCOL_LOAD_BALANCE.length()); - } - return uri; - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java deleted file mode 100644 index 38816bcf..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; - -/** - * @author 六如 - */ -@Slf4j -public abstract class AbstractSigner implements Signer { - - /** - * 构建服务端签名串 - * - * @param params 接口参数 - * @param secret 秘钥 - * @return 返回服务端签名串 - */ - protected abstract String buildServerSign(ApiParam params, String secret); - - @Override - public boolean checkSign(ApiParam apiParam, String secret) { - String clientSign = apiParam.fetchSignAndRemove(); - if (StringUtils.isBlank(clientSign)) { - throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(); - } - String serverSign = buildServerSign(apiParam, secret); - return clientSign.equals(serverSign); - } - - protected static String byte2hex(byte[] bytes) { - StringBuilder sign = new StringBuilder(); - for (int i = 0; i < bytes.length; i++) { - String hex = Integer.toHexString(bytes[i] & 0xFF); - if (hex.length() == 1) { - sign.append("0"); - } - sign.append(hex); - } - return sign.toString(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiEncrypter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiEncrypter.java deleted file mode 100644 index 9b5c2595..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiEncrypter.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import org.apache.commons.codec.digest.DigestUtils; -import com.gitee.sop.gatewaycommon.util.AESUtil; -import com.gitee.sop.gatewaycommon.util.RSANewUtil; -import com.gitee.sop.gatewaycommon.util.RSAUtil; - -/** - * 负责各类加解密 - * @author 六如 - * - */ -public class ApiEncrypter implements Encrypter { - - @Override - public String aesEncryptToHex(String content, String password) throws Exception { - return AESUtil.encryptToHex(content, password); - } - - @Override - public String aesDecryptFromHex(String hex, String password) throws Exception { - return AESUtil.decryptFromHex(hex, password); - } - - @Override - public String aesEncryptToBase64String(String content, String password) throws Exception { - return AESUtil.encryptToBase64String(content, password); - } - - @Override - public String aesDecryptFromBase64String(String base64String, String password) throws Exception { - return AESUtil.decryptFromBase64String(base64String, password); - } - - @Override - public String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception { - return RSAUtil.decryptByPrivateKey(data, privateKey); - } - - @Override - public String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception { - return RSAUtil.encryptByPrivateKey(data, privateKey); - } - - @Override - public String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception { - return RSANewUtil.decryptByPrivateKey(data, privateKey); - } - - @Override - public String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception { - return RSANewUtil.encryptByPrivateKey(data, privateKey); - } - - @Override - public String md5(String value) { - return DigestUtils.md5Hex(value); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiSigner.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiSigner.java deleted file mode 100644 index bf3258da..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiSigner.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - - -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.param.ApiParam; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * 签名验证实现 - * - * @author 六如 - */ -public class ApiSigner extends AbstractSigner { - - private Map signEncipherMap = new HashMap<>(); - - public ApiSigner() { - signEncipherMap.put("md5", new SignEncipherMD5()); - signEncipherMap.put("hmac", new SignEncipherHMAC_MD5()); - } - - - @Override - public String buildServerSign(ApiParam param, String secret) { - String signMethod = param.fetchSignMethod(); - SignEncipher signEncipher = signEncipherMap.get(signMethod); - if (signEncipher == null) { - throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(signMethod); - } - - // 第一步:参数排序 - Set keySet = param.keySet(); - List paramNames = new ArrayList<>(keySet); - Collections.sort(paramNames); - - // 第二步:把所有参数名和参数值串在一起 - StringBuilder paramNameValue = new StringBuilder(); - for (String paramName : paramNames) { - paramNameValue.append(paramName).append(param.get(paramName)); - } - - // 第三步:使用MD5/HMAC加密 - String source = paramNameValue.toString(); - byte[] bytes = signEncipher.encrypt(source, secret); - - // 第四步:把二进制转化为大写的十六进制 - return byte2hex(bytes).toUpperCase(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java deleted file mode 100644 index 60cf06c9..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java +++ /dev/null @@ -1,295 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.bean.Isv; -import com.gitee.sop.gatewaycommon.bean.RouteConfig; -import com.gitee.sop.gatewaycommon.bean.RouteDefinition; -import com.gitee.sop.gatewaycommon.bean.TargetRoute; -import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager; -import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager; -import com.gitee.sop.gatewaycommon.manager.RouteConfigManager; -import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.param.ParamNames; -import com.gitee.sop.gatewaycommon.param.UploadContext; -import com.gitee.sop.gatewaycommon.secret.IsvManager; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.util.StringUtils; -import org.springframework.util.unit.DataSize; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -/** - * 负责校验,校验工作都在这里 - * - * @author 六如 - */ -@Slf4j -@Getter -public class ApiValidator implements Validator { - - private static final int MILLISECOND_OF_ONE_SECOND = 1000; - private static final int STATUS_FORBIDDEN = 2; - - private static List FORMAT_LIST = Arrays.asList("json", "xml"); - - @Autowired - private IsvManager isvManager; - - @Autowired - private IsvRoutePermissionManager isvRoutePermissionManager; - - @Autowired - private IPBlacklistManager ipBlacklistManager; - - @Autowired - private RouteConfigManager routeConfigManager; - - /** - * 单个文件大小 - */ - @Value("${upload.max-file-size:${spring.servlet.multipart.max-file-size:10MB}}") - private String maxFileSize; - - - @Override - public void validate(ApiParam param) { - checkIP(param); - TargetRoute targetRoute = checkEnable(param); - initFields(targetRoute, param); - ApiConfig apiConfig = ApiContext.getApiConfig(); - checkAppKey(param); - if (apiConfig.isIgnoreValidate() - || BooleanUtils.toBoolean(targetRoute.getRouteDefinition().getIgnoreValidate())) { - if (log.isDebugEnabled()) { - log.debug("忽略签名校验, name:{}, version:{}", param.fetchName(), param.fetchVersion()); - } - } else { - checkSign(param); - } - checkTimeout(param); - checkFormat(param); - checkUploadFile(param); - checkPermission(param); - checkToken(param); - } - - /** - * 是否在IP黑名单中 - * - * @param param 接口参数 - */ - protected void checkIP(ApiParam param) { - String ip = param.fetchIp(); - if (ipBlacklistManager.contains(ip)) { - throw ErrorEnum.ISV_IP_FORBIDDEN.getErrorMeta().getException(); - } - } - - /** - * 检测能否访问 - * - * @param param 接口参数 - */ - protected TargetRoute checkEnable(ApiParam param) { - String name = param.fetchName(); - if (name == null) { - throw ErrorEnum.ISV_MISSING_METHOD.getErrorMeta().getException(); - } - String version = param.fetchVersion(); - if (version == null) { - throw ErrorEnum.ISV_MISSING_VERSION.getErrorMeta().getException(); - } - String routeId = param.fetchNameVersion(); - // 检查路由是否存在 - TargetRoute targetRoute = RouteRepositoryContext.getTargetRoute(routeId); - if (targetRoute == null) { - throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException(); - } - // 检查路由是否启用 - RouteConfig routeConfig = routeConfigManager.get(routeId); - if (!routeConfig.enable()) { - throw ErrorEnum.ISP_API_DISABLED.getErrorMeta().getException(); - } - return targetRoute; - } - - private void initFields(TargetRoute targetRoute, ApiParam apiParam) { - apiParam.setServiceId(targetRoute.getServiceDefinition().getServiceId()); - boolean mergeResult; - Boolean defaultSetting = ApiContext.getApiConfig().getMergeResult(); - if (defaultSetting != null) { - mergeResult = defaultSetting; - } else { - RouteDefinition routeDefinition = targetRoute.getRouteDefinition(); - mergeResult = routeDefinition == null || BooleanUtils.toBoolean(routeDefinition.getMergeResult()); - } - apiParam.setMergeResult(mergeResult); - } - - /** - * 校验上传文件内容 - * - * @param param - */ - protected void checkUploadFile(ApiParam param) { - UploadContext uploadContext = param.fetchUploadContext(); - if (uploadContext != null) { - try { - List files = uploadContext.getAllFile(); - for (MultipartFile file : files) { - checkSingleFileSize(file); - checkFileMd5(param, file); - } - } catch (IOException e) { - log.error("验证上传文件MD5错误", e); - throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException(); - } - } - } - - private void checkFileMd5(ApiParam param, MultipartFile file) throws IOException { - // 客户端传来的文件md5 - String clientMd5 = param.getString(file.getName()); - if (clientMd5 != null) { - String fileMd5 = DigestUtils.md5Hex(file.getBytes()); - if (!clientMd5.equals(fileMd5)) { - throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException(); - } - } - } - - /** - * 校验单个文件大小 - * - * @param file 文件 - */ - private void checkSingleFileSize(MultipartFile file) { - long fileSize = file.getSize(); - if (fileSize > DataSize.parse(maxFileSize).toBytes()) { - throw ErrorEnum.ISV_INVALID_FILE_SIZE.getErrorMeta().getException(file.getName(), maxFileSize); - } - } - - protected void checkTimeout(ApiParam param) { - int timeoutSeconds = ApiContext.getApiConfig().getTimeoutSeconds(); - // 如果设置为0,表示不校验 - if (timeoutSeconds == 0) { - return; - } - if (timeoutSeconds < 0) { - throw new IllegalArgumentException("服务端timeoutSeconds设置错误"); - } - String requestTime = param.fetchTimestamp(); - try { - Date requestDate = new SimpleDateFormat(ParamNames.TIMESTAMP_PATTERN).parse(requestTime); - long requestMilliseconds = requestDate.getTime(); - if (System.currentTimeMillis() - requestMilliseconds > timeoutSeconds * MILLISECOND_OF_ONE_SECOND) { - throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(); - } - } catch (ParseException e) { - throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(param.fetchNameVersion()); - } - } - - protected void checkAppKey(ApiParam param) { - if (StringUtils.isEmpty(param.fetchAppKey())) { - throw ErrorEnum.ISV_MISSING_APP_ID.getErrorMeta().getException(); - } - Isv isv = isvManager.getIsv(param.fetchAppKey()); - // 没有用户 - if (isv == null) { - throw ErrorEnum.ISV_INVALID_APP_ID.getErrorMeta().getException(); - } - // 禁止访问 - if (isv.getStatus() == null || isv.getStatus() == STATUS_FORBIDDEN) { - throw ErrorEnum.ISV_ACCESS_FORBIDDEN.getErrorMeta().getException(); - } - } - - protected void checkSign(ApiParam param) { - String clientSign = param.fetchSign(); - try { - if (StringUtils.isEmpty(clientSign)) { - throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion(), ParamNames.SIGN_NAME); - } - ApiConfig apiConfig = ApiContext.getApiConfig(); - // 根据appId获取秘钥 - Isv isvInfo = isvManager.getIsv(param.fetchAppKey()); - String secret = isvInfo.getSecretInfo(); - if (StringUtils.isEmpty(secret)) { - throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException(); - } - Signer signer = apiConfig.getSigner(); - // 错误的sign - if (!signer.checkSign(param, secret)) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion()); - } - } finally { - // 校验过程中会移除sign,这里需要重新设置进去 - param.setSign(clientSign); - } - } - - - protected void checkFormat(ApiParam param) { - String format = param.fetchFormat(); - boolean contains = FORMAT_LIST.contains(format.toLowerCase()); - - if (!contains) { - throw ErrorEnum.ISV_INVALID_FORMAT.getErrorMeta().getException(param.fetchNameVersion(), format); - } - } - - /** - * 校验访问权限 - * - * @param apiParam 参数 - */ - protected void checkPermission(ApiParam apiParam) { - String routeId = apiParam.fetchNameVersion(); - TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(routeId); - RouteDefinition routeDefinition = targetRoute.getRouteDefinition(); - boolean needCheckPermission = BooleanUtils.toBoolean(routeDefinition.getPermission()); - if (needCheckPermission) { - String appKey = apiParam.fetchAppKey(); - boolean hasPermission = isvRoutePermissionManager.hasPermission(appKey, routeId); - if (!hasPermission) { - throw ErrorEnum.ISV_ROUTE_NO_PERMISSIONS.getErrorMeta().getException(); - } - } - } - - /** - * 校验token - * - * @param apiParam 参数 - */ - protected void checkToken(ApiParam apiParam) { - String routeId = apiParam.fetchNameVersion(); - TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(routeId); - RouteDefinition routeDefinition = targetRoute.getRouteDefinition(); - boolean needToken = BooleanUtils.toBoolean(routeDefinition.getNeedToken()); - if (needToken) { - TokenValidator tokenValidator = ApiConfig.getInstance().getTokenValidator(); - boolean rightToken = tokenValidator.validateToken(apiParam); - if (!rightToken) { - throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException(); - } - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Encrypter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Encrypter.java deleted file mode 100644 index 12d5d11c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Encrypter.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -/** - * 负责加解密 - * - * @author 六如 - */ -public interface Encrypter { - - /** - * AES文本加密 - * - * @param content 明文 - * @param password 密码 - * @return 返回16进制内容 - * @throws Exception - */ - String aesEncryptToHex(String content, String password) throws Exception; - - /** - * AES文本解密 - * - * @param hex 待解密文本,16进制内容 - * @param password 密码 - * @return 返回明文 - * @throws Exception - */ - String aesDecryptFromHex(String hex, String password) throws Exception; - - /** - * AES文本加密 - * - * @param content 明文 - * @param password 密码 - * @return 返回base64内容 - * @throws Exception - */ - String aesEncryptToBase64String(String content, String password) throws Exception; - - /** - * AES文本解密 - * - * @param base64String 待解密文本,16进制内容 - * @param password 密码 - * @return 返回明文 - * @throws Exception - */ - String aesDecryptFromBase64String(String base64String, String password) throws Exception; - - /** - * RSA私钥解密 - * - * @param data 解密内容 - * @param privateKey 私钥 - * @return 返回明文 - * @throws Exception - */ - String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception; - - /** - * 新版rsa私钥解密 - * @param data 解密内容 - * @param privateKey 私钥 - * @return 返回明文 - * @throws Exception - */ - String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception; - - /** - * RSA私钥加密 - * - * @param data 明文 - * @param privateKey 私钥 - * @return 返回密文 - * @throws Exception - */ - String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception; - - /** - * 新版rsa私钥加密 - * @param data 明文 - * @param privateKey 私钥 - * @return 返回密文 - * @throws Exception - */ - String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception; - - /** - * md5加密,全部小写 - * - * @param value - * @return 返回md5内容 - */ - String md5(String value); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignConfig.java deleted file mode 100644 index fdcc0a1d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -/** - * @author 六如 - */ -public class SignConfig { - private static volatile Wrapper wrapper = new Wrapper() {}; - - public static void enableUrlencodeMode() { - wrapper = new Wrapper() { - @Override - public String wrapVal(Object val) { - String valStr = String.valueOf(val); - try { - return URLEncoder.encode(valStr, SopConstants.UTF8); - } catch (UnsupportedEncodingException e) { - return valStr; - } - } - }; - } - - public static String wrapVal(Object val) { - return wrapper.wrapVal(val); - } - - interface Wrapper { - default String wrapVal(Object val) { - return String.valueOf(val); - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipher.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipher.java deleted file mode 100644 index d0a58f04..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipher.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -/** - * @author 六如 - */ -public interface SignEncipher { - /** - * 签名的摘要算法 - * @param input 待签名数据 - * @param secret 秘钥 - * @return 返回加密后的数据 - */ - byte[] encrypt(String input, String secret); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherHMAC_MD5.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherHMAC_MD5.java deleted file mode 100644 index 48b3625d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherHMAC_MD5.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import lombok.extern.slf4j.Slf4j; - -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -/** - * HMAC_MD5加密 - * @author 六如 - */ -@Slf4j -public class SignEncipherHMAC_MD5 implements SignEncipher { - - public static final String HMAC_MD5 = "HmacMD5"; - - @Override - public byte[] encrypt(String input, String secret) { - try { - SecretKey secretKey = new SecretKeySpec(secret.getBytes(SopConstants.CHARSET_UTF8), HMAC_MD5); - Mac mac = Mac.getInstance(secretKey.getAlgorithm()); - mac.init(secretKey); - return mac.doFinal(input.getBytes(SopConstants.CHARSET_UTF8)); - } catch (NoSuchAlgorithmException e) { - log.error("HMAC_MD5加密加密失败NoSuchAlgorithmException", e); - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); - } catch (InvalidKeyException e) { - log.error("HMAC_MD5加密加密失败InvalidKeyException", e); - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); - } - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherMD5.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherMD5.java deleted file mode 100644 index ad015284..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherMD5.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import org.apache.commons.codec.digest.DigestUtils; - -import java.nio.charset.StandardCharsets; - -/** - * @author 六如 - */ -public class SignEncipherMD5 implements SignEncipher { - @Override - public byte[] encrypt(String input, String secret) { - String source = secret + input + secret; - return DigestUtils.md5(source.getBytes(StandardCharsets.UTF_8)); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Signer.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Signer.java deleted file mode 100644 index 3b525108..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Signer.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import com.gitee.sop.gatewaycommon.param.ApiParam; - -import javax.servlet.http.HttpServletRequest; - -/** - * 负责签名校验 - * @author 六如 - * - */ -public interface Signer { - - /** - * 签名校验 - * @param apiParam 参数 - * @param secret 秘钥 - * @return true签名正确 - */ - boolean checkSign(ApiParam apiParam, String secret); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/TokenValidator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/TokenValidator.java deleted file mode 100644 index 93683530..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/TokenValidator.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import com.gitee.sop.gatewaycommon.param.ApiParam; - -/** - * @author 六如 - */ -@FunctionalInterface -public interface TokenValidator { - boolean validateToken(ApiParam apiParam); -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Validator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Validator.java deleted file mode 100644 index c14f95b9..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Validator.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate; - -import com.gitee.sop.gatewaycommon.param.ApiParam; - -/** - * 校验接口 - * - * @author 六如 - * - */ -public interface Validator { - /** - * 接口验证 - * @param param 接口参数 - */ - void validate(ApiParam param); - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipayConstants.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipayConstants.java deleted file mode 100644 index ceb140f8..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipayConstants.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Alipay.com Inc. - * Copyright (c) 2004-2012 All Rights Reserved. - */ -package com.gitee.sop.gatewaycommon.validate.alipay; - -/** - * - * @author runzhi - */ -public class AlipayConstants { - - public static final String SIGN_TYPE = "sign_type"; - - public static final String SIGN_TYPE_RSA = "RSA"; - - /** - * sha256WithRsa 算法请求类型 - */ - public static final String SIGN_TYPE_RSA2 = "RSA2"; - - public static final String SIGN_ALGORITHMS = "SHA1WithRSA"; - - public static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; - - public static final String ENCRYPT_TYPE_AES = "AES"; - - public static final String APP_ID = "app_id"; - - public static final String FORMAT = "format"; - - public static final String METHOD = "method"; - - public static final String TIMESTAMP = "timestamp"; - - public static final String VERSION = "version"; - - public static final String SIGN = "sign"; - - public static final String ALIPAY_SDK = "alipay_sdk"; - - public static final String ACCESS_TOKEN = "auth_token"; - - public static final String APP_AUTH_TOKEN = "app_auth_token"; - - public static final String TERMINAL_TYPE = "terminal_type"; - - public static final String TERMINAL_INFO = "terminal_info"; - - public static final String CHARSET = "charset"; - - public static final String NOTIFY_URL = "notify_url"; - - public static final String RETURN_URL = "return_url"; - - public static final String ENCRYPT_TYPE = "encrypt_type"; - - //-----===-------/// - - public static final String BIZ_CONTENT_KEY = "biz_content"; - - /** 默认时间格式 **/ - public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; - - /** Date默认时区 **/ - public static final String DATE_TIMEZONE = "GMT+8"; - - /** UTF-8字符集 **/ - public static final String CHARSET_UTF8 = "UTF-8"; - - /** GBK字符集 **/ - public static final String CHARSET_GBK = "GBK"; - - /** JSON 应格式 */ - public static final String FORMAT_JSON = "json"; - - /** XML 应格式 */ - public static final String FORMAT_XML = "xml"; - - /** SDK版本号 */ - public static final String SDK_VERSION = "alipay-sdk-java-3.6.0.ALL"; - - public static final String PROD_CODE = "prod_code"; - - /** 老版本失败节点 */ - public static final String ERROR_RESPONSE = "error_response"; - - /** 新版本节点后缀 */ - public static final String RESPONSE_SUFFIX = "_response"; - - /** 加密后XML返回报文的节点名字 */ - public static final String RESPONSE_XML_ENCRYPT_NODE_NAME = "response_encrypted"; - - /** 批量请求id **/ - public static final String BATCH_REQUEST_ID = "batch_request_id"; - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySignature.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySignature.java deleted file mode 100644 index 2c769afc..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySignature.java +++ /dev/null @@ -1,617 +0,0 @@ -/** - * Alipay.com Inc. - * Copyright (c) 2004-2012 All Rights Reserved. - */ -package com.gitee.sop.gatewaycommon.validate.alipay; - -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.validate.SignConfig; -import org.apache.commons.codec.binary.Base64; - -import javax.crypto.Cipher; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.security.KeyFactory; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * @author runzhi - */ -public class AlipaySignature { - - /** - * RSA最大加密明文大小 - */ - private static final int MAX_ENCRYPT_BLOCK = 117; - - /** - * RSA最大解密密文大小 - */ - private static final int MAX_DECRYPT_BLOCK = 128; - - - /** - * @param sortedParams - * @return - */ - public static String getSignContent(Map sortedParams) { - StringBuffer content = new StringBuffer(); - List keys = new ArrayList(sortedParams.keySet()); - Collections.sort(keys); - int index = 0; - for (int i = 0; i < keys.size(); i++) { - String key = keys.get(i); - String value = String.valueOf(sortedParams.get(key)); - if (StringUtils.areNotEmpty(key, value)) { - content.append((index == 0 ? "" : "&") + key + "=" + value); - index++; - } - } - return content.toString(); - } - - /** - * rsa内容签名 - * - * @param content - * @param publicKey - * @param charset - * @return - */ - public static String rsaSign(String content, String publicKey, String charset, - String signType) { - - if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { - - return rsaSign(content, publicKey, charset); - } else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { - - return rsa256Sign(content, publicKey, charset); - } else { - throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(); -// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); - } - - } - - /** - * sha256WithRsa 加签 - * - * @param content - * @param privateKey - * @param charset - * @return - */ - public static String rsa256Sign(String content, String privateKey, - String charset) { - - try { - PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, - new ByteArrayInputStream(privateKey.getBytes())); - - java.security.Signature signature = java.security.Signature - .getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); - - signature.initSign(priKey); - - if (StringUtils.isEmpty(charset)) { - signature.update(content.getBytes()); - } else { - signature.update(content.getBytes(charset)); - } - - byte[] signed = signature.sign(); - - return new String(Base64.encodeBase64(signed)); - } catch (Exception e) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); -// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); - } - - } - - /** - * sha1WithRsa 加签 - * - * @param content - * @param publicKey - * @param charset - * @return - */ - public static String rsaSign(String content, String publicKey, - String charset) { - try { - PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, - new ByteArrayInputStream(publicKey.getBytes())); - - java.security.Signature signature = java.security.Signature - .getInstance(AlipayConstants.SIGN_ALGORITHMS); - - signature.initSign(priKey); - - if (StringUtils.isEmpty(charset)) { - signature.update(content.getBytes()); - } else { - signature.update(content.getBytes(charset)); - } - - byte[] signed = signature.sign(); - - return new String(Base64.encodeBase64(signed)); - } catch (InvalidKeySpecException ie) { - throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(ie); -// throw new AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie); - } catch (Exception e) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); -// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); - } - } - - public static String rsaSign(Map params, String publicKey, - String charset, String signType) { - String signContent = getSignContent(params); - - return rsaSign(signContent, publicKey, charset, signType); - - } - - public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, - InputStream ins) throws Exception { - if (ins == null || StringUtils.isEmpty(algorithm)) { - return null; - } - - KeyFactory keyFactory = KeyFactory.getInstance(algorithm); - - byte[] encodedKey = StreamUtil.readText(ins, "UTF-8").getBytes(); - - encodedKey = Base64.decodeBase64(encodedKey); - - return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey)); - } - - public static String getSignCheckContentV1(Map params) { - if (params == null) { - return null; - } - - params.remove("sign"); - params.remove("sign_type"); - - StringBuilder content = new StringBuilder(); - List keys = new ArrayList(params.keySet()); - Collections.sort(keys); - - for (int i = 0; i < keys.size(); i++) { - String key = keys.get(i); - String value = SignConfig.wrapVal(params.get(key)); - content.append((i == 0 ? "" : "&") + key + "=" + value); - } - - return content.toString(); - } - - public static String getSignCheckContentV2(Map params) { - if (params == null) { - return null; - } - - params.remove("sign"); - - StringBuilder content = new StringBuilder(); - List keys = new ArrayList(params.keySet()); - Collections.sort(keys); - - for (int i = 0; i < keys.size(); i++) { - String key = keys.get(i); - String value = SignConfig.wrapVal(params.get(key)); - content.append((i == 0 ? "" : "&") + key + "=" + value); - } - - return content.toString(); - } - - public static boolean rsaCheckV1(Map params, String publicKey, - String charset) { - String sign = params.get("sign"); - String content = getSignCheckContentV1(params); - - return rsaCheckContent(content, sign, publicKey, charset); - } - - public static boolean rsaCheckV1(Map params, String publicKey, - String charset, String signType) { - String sign = params.get("sign"); - String content = getSignCheckContentV1(params); - - return rsaCheck(content, sign, publicKey, charset, signType); - } - - public static boolean rsaCheckV2(Map params, String publicKey, - String charset) { - String sign = params.get("sign"); - String content = getSignCheckContentV2(params); - - return rsaCheckContent(content, sign, publicKey, charset); - } - - public static boolean rsaCheckV2(Map params, String publicKey, - String charset, String signType) { - String sign = String.valueOf(params.get("sign")); - String content = getSignCheckContentV2(params); - - return rsaCheck(content, sign, publicKey, charset, signType); - } - - public static boolean rsaCheck(String content, String sign, String publicKey, String charset, - String signType) { - - if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { - - return rsaCheckContent(content, sign, publicKey, charset); - - } else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { - - return rsa256CheckContent(content, sign, publicKey, charset); - - } else { - throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(); -// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); - } - - } - - public static boolean rsa256CheckContent(String content, String sign, String publicKey, - String charset) { - try { - PublicKey pubKey = getPublicKeyFromX509("RSA", - new ByteArrayInputStream(publicKey.getBytes())); - - java.security.Signature signature = java.security.Signature - .getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); - - signature.initVerify(pubKey); - - if (StringUtils.isEmpty(charset)) { - signature.update(content.getBytes()); - } else { - signature.update(content.getBytes(charset)); - } - - return signature.verify(Base64.decodeBase64(sign.getBytes())); - } catch (Exception e) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); -// throw new AlipayApiException( -// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e); - } - } - - public static boolean rsaCheckContent(String content, String sign, String publicKey, - String charset) { - try { - PublicKey pubKey = getPublicKeyFromX509("RSA", - new ByteArrayInputStream(publicKey.getBytes())); - - java.security.Signature signature = java.security.Signature - .getInstance(AlipayConstants.SIGN_ALGORITHMS); - - signature.initVerify(pubKey); - - if (StringUtils.isEmpty(charset)) { - signature.update(content.getBytes()); - } else { - signature.update(content.getBytes(charset)); - } - - return signature.verify(Base64.decodeBase64(sign.getBytes())); - } catch (Exception e) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); -// throw new AlipayApiException( -// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e); - } - } - - public static PublicKey getPublicKeyFromX509(String algorithm, - InputStream ins) throws Exception { - KeyFactory keyFactory = KeyFactory.getInstance(algorithm); - - StringWriter writer = new StringWriter(); - StreamUtil.io(new InputStreamReader(ins), writer); - - byte[] encodedKey = writer.toString().getBytes(); - - encodedKey = Base64.decodeBase64(encodedKey); - - return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); - } - - /** - * 验签并解密 - *

- * 目前适用于公众号
- * params参数示例: - *
{ - *
biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, - *
sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, - * sign_type=RSA, - *
charset=UTF-8 - *
} - *

- * - * @param params - * @param alipayPublicKey 支付宝公钥 - * @param cusPrivateKey 商户私钥 - * @param isCheckSign 是否验签 - * @param isDecrypt 是否解密 - * @return 解密后明文,验签失败则异常抛出 - */ - public static String checkSignAndDecrypt(Map params, String alipayPublicKey, - String cusPrivateKey, boolean isCheckSign, - boolean isDecrypt) { - String charset = params.get("charset"); - String bizContent = params.get("biz_content"); - if (isCheckSign) { - if (!rsaCheckV2(params, alipayPublicKey, charset)) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); -// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params); - } - } - - if (isDecrypt) { - return rsaDecrypt(bizContent, cusPrivateKey, charset); - } - - return bizContent; - } - - /** - * 验签并解密 - *

- * 目前适用于公众号
- * params参数示例: - *
{ - *
biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, - *
sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, - * sign_type=RSA, - *
charset=UTF-8 - *
} - *

- * - * @param params - * @param alipayPublicKey 支付宝公钥 - * @param cusPrivateKey 商户私钥 - * @param isCheckSign 是否验签 - * @param isDecrypt 是否解密 - * @return 解密后明文,验签失败则异常抛出 - */ - public static String checkSignAndDecrypt(Map params, String alipayPublicKey, - String cusPrivateKey, boolean isCheckSign, - boolean isDecrypt, String signType) { - String charset = params.get("charset"); - String bizContent = params.get("biz_content"); - if (isCheckSign) { - if (!rsaCheckV2(params, alipayPublicKey, charset, signType)) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); -// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params); - } - } - - if (isDecrypt) { - return rsaDecrypt(bizContent, cusPrivateKey, charset); - } - - return bizContent; - } - - /** - * 加密并签名
- * 目前适用于公众号 - * - * @param bizContent 待加密、签名内容 - * @param alipayPublicKey 支付宝公钥 - * @param cusPrivateKey 商户私钥 - * @param charset 字符集,如UTF-8, GBK, GB2312 - * @param isEncrypt 是否加密,true-加密 false-不加密 - * @param isSign 是否签名,true-签名 false-不签名 - * @return 加密、签名后xml内容字符串 - *

- * 返回示例: - * - * 密文 - * RSA - * sign - * RSA - * - *

- */ - public static String encryptAndSign(String bizContent, String alipayPublicKey, - String cusPrivateKey, String charset, boolean isEncrypt, - boolean isSign) { - StringBuilder sb = new StringBuilder(); - if (StringUtils.isEmpty(charset)) { - charset = AlipayConstants.CHARSET_GBK; - } - sb.append(""); - if (isEncrypt) {// 加密 - sb.append(""); - String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); - sb.append("" + encrypted + ""); - sb.append("RSA"); - if (isSign) { - String sign = rsaSign(encrypted, cusPrivateKey, charset); - sb.append("" + sign + ""); - sb.append("RSA"); - } - sb.append(""); - } else if (isSign) {// 不加密,但需要签名 - sb.append(""); - sb.append("" + bizContent + ""); - String sign = rsaSign(bizContent, cusPrivateKey, charset); - sb.append("" + sign + ""); - sb.append("RSA"); - sb.append(""); - } else {// 不加密,不加签 - sb.append(bizContent); - } - return sb.toString(); - } - - /** - * 加密并签名
- * 目前适用于公众号 - * - * @param bizContent 待加密、签名内容 - * @param alipayPublicKey 支付宝公钥 - * @param cusPrivateKey 商户私钥 - * @param charset 字符集,如UTF-8, GBK, GB2312 - * @param isEncrypt 是否加密,true-加密 false-不加密 - * @param isSign 是否签名,true-签名 false-不签名 - * @return 加密、签名后xml内容字符串 - *

- * 返回示例: - * - * 密文 - * RSA - * sign - * RSA - * - *

- */ - public static String encryptAndSign(String bizContent, String alipayPublicKey, - String cusPrivateKey, String charset, boolean isEncrypt, - boolean isSign, String signType) { - StringBuilder sb = new StringBuilder(); - if (StringUtils.isEmpty(charset)) { - charset = AlipayConstants.CHARSET_GBK; - } - sb.append(""); - if (isEncrypt) {// 加密 - sb.append(""); - String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); - sb.append("" + encrypted + ""); - sb.append("RSA"); - if (isSign) { - String sign = rsaSign(encrypted, cusPrivateKey, charset, signType); - sb.append("" + sign + ""); - sb.append(""); - sb.append(signType); - sb.append(""); - } - sb.append(""); - } else if (isSign) {// 不加密,但需要签名 - sb.append(""); - sb.append("" + bizContent + ""); - String sign = rsaSign(bizContent, cusPrivateKey, charset, signType); - sb.append("" + sign + ""); - sb.append(""); - sb.append(signType); - sb.append(""); - sb.append(""); - } else {// 不加密,不加签 - sb.append(bizContent); - } - return sb.toString(); - } - - /** - * 公钥加密 - * - * @param content 待加密内容 - * @param publicKey 公钥 - * @param charset 字符集,如UTF-8, GBK, GB2312 - * @return 密文内容 - */ - public static String rsaEncrypt(String content, String publicKey, - String charset) { - try { - PublicKey pubKey = getPublicKeyFromX509(AlipayConstants.SIGN_TYPE_RSA, - new ByteArrayInputStream(publicKey.getBytes())); - Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); - cipher.init(Cipher.ENCRYPT_MODE, pubKey); - byte[] data = StringUtils.isEmpty(charset) ? content.getBytes() - : content.getBytes(charset); - int inputLen = data.length; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int offSet = 0; - byte[] cache; - int i = 0; - // 对数据分段加密 - while (inputLen - offSet > 0) { - if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { - cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); - } else { - cache = cipher.doFinal(data, offSet, inputLen - offSet); - } - out.write(cache, 0, cache.length); - i++; - offSet = i * MAX_ENCRYPT_BLOCK; - } - byte[] encryptedData = Base64.encodeBase64(out.toByteArray()); - out.close(); - - return StringUtils.isEmpty(charset) ? new String(encryptedData) - : new String(encryptedData, charset); - } catch (Exception e) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); -// throw new AlipayApiException("EncryptContent = " + content + ",charset = " + charset, -// e); - } - } - - /** - * 私钥解密 - * - * @param content 待解密内容 - * @param publicKey 私钥 - * @param charset 字符集,如UTF-8, GBK, GB2312 - * @return 明文内容 - */ - public static String rsaDecrypt(String content, String publicKey, - String charset) { - try { - PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, - new ByteArrayInputStream(publicKey.getBytes())); - Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); - cipher.init(Cipher.DECRYPT_MODE, priKey); - byte[] encryptedData = StringUtils.isEmpty(charset) - ? Base64.decodeBase64(content.getBytes()) - : Base64.decodeBase64(content.getBytes(charset)); - int inputLen = encryptedData.length; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int offSet = 0; - byte[] cache; - int i = 0; - // 对数据分段解密 - while (inputLen - offSet > 0) { - if (inputLen - offSet > MAX_DECRYPT_BLOCK) { - cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); - } else { - cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); - } - out.write(cache, 0, cache.length); - i++; - offSet = i * MAX_DECRYPT_BLOCK; - } - byte[] decryptedData = out.toByteArray(); - out.close(); - - return StringUtils.isEmpty(charset) ? new String(decryptedData) - : new String(decryptedData, charset); - } catch (Exception e) { - throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); -// throw new AlipayApiException("EncodeContent = " + content + ",charset = " + charset, e); - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySigner.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySigner.java deleted file mode 100644 index 72e081c5..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySigner.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate.alipay; - -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.validate.Signer; - -/** - * 支付宝签名验证实现。 - * - * @author 六如 - * @see 支付宝签名 - */ -public class AlipaySigner implements Signer { - - @Override - public boolean checkSign(ApiParam apiParam, String secret) { - // 服务端存的是公钥 - String publicKey = secret; - String charset = apiParam.fetchCharset(); - String signType = apiParam.fetchSignMethod(); - if (signType == null) { - throw ErrorEnum.ISV_DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE.getErrorMeta().getException(); - } - if (charset == null) { - throw ErrorEnum.ISV_INVALID_CHARSET.getErrorMeta().getException(); - } - return AlipaySignature.rsaCheckV2(apiParam, publicKey, charset, signType); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/StreamUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/StreamUtil.java deleted file mode 100644 index e7eeb84a..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/StreamUtil.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Alipay.com Inc. - * Copyright (c) 2004-2012 All Rights Reserved. - */ -package com.gitee.sop.gatewaycommon.validate.alipay; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; - -/** - * - * @author runzhi - */ -public class StreamUtil { - private StreamUtil(){} - - private static final int DEFAULT_BUFFER_SIZE = 8192; - - public static void io(InputStream in, OutputStream out) throws IOException { - io(in, out, -1); - } - - public static void io(InputStream in, OutputStream out, int bufferSize) throws IOException { - if (bufferSize == -1) { - bufferSize = DEFAULT_BUFFER_SIZE; - } - - byte[] buffer = new byte[bufferSize]; - int amount; - - while ((amount = in.read(buffer)) >= 0) { - out.write(buffer, 0, amount); - } - } - - public static void io(Reader in, Writer out) throws IOException { - io(in, out, -1); - } - - public static void io(Reader in, Writer out, int bufferSize) throws IOException { - if (bufferSize == -1) { - bufferSize = DEFAULT_BUFFER_SIZE >> 1; - } - - char[] buffer = new char[bufferSize]; - int amount; - - while ((amount = in.read(buffer)) >= 0) { - out.write(buffer, 0, amount); - } - } - - public static OutputStream synchronizedOutputStream(OutputStream out) { - return new SynchronizedOutputStream(out); - } - - public static OutputStream synchronizedOutputStream(OutputStream out, Object lock) { - return new SynchronizedOutputStream(out, lock); - } - - public static String readText(InputStream in) throws IOException { - return readText(in, null, -1); - } - - public static String readText(InputStream in, String encoding) throws IOException { - return readText(in, encoding, -1); - } - - public static String readText(InputStream in, String encoding, int bufferSize) - throws IOException { - Reader reader = (encoding == null) ? new InputStreamReader(in) : new InputStreamReader(in, - encoding); - - return readText(reader, bufferSize); - } - - public static String readText(Reader reader) throws IOException { - return readText(reader, -1); - } - - public static String readText(Reader reader, int bufferSize) throws IOException { - StringWriter writer = new StringWriter(); - - io(reader, writer, bufferSize); - return writer.toString(); - } - - private static class SynchronizedOutputStream extends OutputStream { - private OutputStream out; - private Object lock; - - SynchronizedOutputStream(OutputStream out) { - this(out, out); - } - - SynchronizedOutputStream(OutputStream out, Object lock) { - this.out = out; - this.lock = lock; - } - - @Override - public void write(int datum) throws IOException { - synchronized (lock) { - out.write(datum); - } - } - - @Override - public void write(byte[] data) throws IOException { - synchronized (lock) { - out.write(data); - } - } - - @Override - public void write(byte[] data, int offset, int length) throws IOException { - synchronized (lock) { - out.write(data, offset, length); - } - } - - @Override - public void flush() throws IOException { - synchronized (lock) { - out.flush(); - } - } - - @Override - public void close() throws IOException { - synchronized (lock) { - out.close(); - } - } - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/StringUtils.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/StringUtils.java deleted file mode 100644 index dd04341b..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/StringUtils.java +++ /dev/null @@ -1,171 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate.alipay; - -/** - * 字符串工具类。 - * - * @author carver.gu - * @since 1.0, Sep 12, 2009 - */ -public class StringUtils { - - private StringUtils() {} - - /** - * 检查指定的字符串是否为空。 - *
    - *
  • SysUtils.isEmpty(null) = true
  • - *
  • SysUtils.isEmpty("") = true
  • - *
  • SysUtils.isEmpty(" ") = true
  • - *
  • SysUtils.isEmpty("abc") = false
  • - *
- * - * @param value 待检查的字符串 - * @return true/false - */ - public static boolean isEmpty(String value) { - int strLen; - if (value == null || (strLen = value.length()) == 0) { - return true; - } - for (int i = 0; i < strLen; i++) { - if ((Character.isWhitespace(value.charAt(i)) == false)) { - return false; - } - } - return true; - } - - /** - * 检查对象是否为数字型字符串,包含负数开头的。 - */ - public static boolean isNumeric(Object obj) { - if (obj == null) { - return false; - } - char[] chars = obj.toString().toCharArray(); - int length = chars.length; - if(length < 1) { - return false; - } - - int i = 0; - if(length > 1 && chars[0] == '-') { - i = 1; - } - - for (; i < length; i++) { - if (!Character.isDigit(chars[i])) { - return false; - } - } - return true; - } - - /** - * 检查指定的字符串列表是否不为空。 - */ - public static boolean areNotEmpty(String... values) { - boolean result = true; - if (values == null || values.length == 0) { - result = false; - } else { - for (String value : values) { - result &= !isEmpty(value); - } - } - return result; - } - - /** - * 把通用字符编码的字符串转化为汉字编码。 - */ - public static String unicodeToChinese(String unicode) { - StringBuilder out = new StringBuilder(); - if (!isEmpty(unicode)) { - for (int i = 0; i < unicode.length(); i++) { - out.append(unicode.charAt(i)); - } - } - return out.toString(); - } - - /** - * 过滤不可见字符 - */ - public static String stripNonValidXMLCharacters(String input) { - if (input == null || ("".equals(input))) { - return ""; - } - StringBuilder out = new StringBuilder(); - char current; - for (int i = 0; i < input.length(); i++) { - current = input.charAt(i); - if ((current == 0x9) || (current == 0xA) || (current == 0xD) - || ((current >= 0x20) && (current <= 0xD7FF)) - || ((current >= 0xE000) && (current <= 0xFFFD)) - || ((current >= 0x10000) && (current <= 0x10FFFF))) { - out.append(current); - } - } - return out.toString(); - } - - public static String leftPad(String str, int size, char padChar) { - if (str == null) { - return null; - } else { - int pads = size - str.length(); - if (pads <= 0) { - return str; - } else { - return pads > 8192 ? leftPad(str, size, String.valueOf(padChar)) : padding(pads, padChar).concat(str); - } - } - } - - public static String leftPad(String str, int size, String padStr) { - if (str == null) { - return null; - } else { - if (isEmpty(padStr)) { - padStr = " "; - } - - int padLen = padStr.length(); - int strLen = str.length(); - int pads = size - strLen; - if (pads <= 0) { - return str; - } else if (padLen == 1 && pads <= 8192) { - return leftPad(str, size, padStr.charAt(0)); - } else if (pads == padLen) { - return padStr.concat(str); - } else if (pads < padLen) { - return padStr.substring(0, pads).concat(str); - } else { - char[] padding = new char[pads]; - char[] padChars = padStr.toCharArray(); - - for(int i = 0; i < pads; ++i) { - padding[i] = padChars[i % padLen]; - } - - return (new String(padding)).concat(str); - } - } - } - - private static String padding(int repeat, char padChar) { - if (repeat < 0) { - throw new IndexOutOfBoundsException("Cannot pad a negative amount: " + repeat); - } else { - char[] buf = new char[repeat]; - - for(int i = 0; i < buf.length; ++i) { - buf[i] = padChar; - } - - return new String(buf); - } - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/taobao/TaobaoSigner.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/taobao/TaobaoSigner.java deleted file mode 100644 index dfed7463..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/taobao/TaobaoSigner.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.gitee.sop.gatewaycommon.validate.taobao; - - -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.message.ErrorEnum; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.validate.AbstractSigner; -import com.gitee.sop.gatewaycommon.validate.SignConfig; -import com.gitee.sop.gatewaycommon.validate.SignEncipher; -import com.gitee.sop.gatewaycommon.validate.SignEncipherHMAC_MD5; -import com.gitee.sop.gatewaycommon.validate.SignEncipherMD5; -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * 淘宝开放平台签名验证实现,http://open.taobao.com/doc.htm?docId=101617&docType=1 - * - * @author 六如 - */ -public class TaobaoSigner extends AbstractSigner { - - private Map signEncipherMap = new HashMap<>(); - - public TaobaoSigner() { - signEncipherMap.put("md5", new SignEncipherMD5()); - signEncipherMap.put("hmac", new SignEncipherHMAC_MD5()); - } - - - @Override - public String buildServerSign(ApiParam param, String secret) { - String signMethod = param.fetchSignMethod(); - if (signMethod == null) { - signMethod = SopConstants.DEFAULT_SIGN_METHOD; - } - SignEncipher signEncipher = signEncipherMap.get(signMethod); - if (signEncipher == null) { - throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(signMethod); - } - - // 第一步:参数排序 - Set keySet = param.keySet(); - List paramNames = new ArrayList<>(keySet); - Collections.sort(paramNames); - - // 第二步:把所有参数名和参数值串在一起 - StringBuilder paramNameValue = new StringBuilder(); - for (String paramName : paramNames) { - String val = SignConfig.wrapVal(param.get(paramName)); - paramNameValue.append(paramName).append(val); - } - - // 第三步:使用MD5/HMAC加密 - String source = paramNameValue.toString(); - byte[] bytes = signEncipher.encrypt(source, secret); - - // 第四步:把二进制转化为大写的十六进制 - return byte2hex(bytes).toUpperCase(); - } -} diff --git a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties b/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties deleted file mode 100644 index db6a2c38..00000000 --- a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties +++ /dev/null @@ -1,64 +0,0 @@ -# \u7F51\u5173\u9519\u8BEF\u914D\u7F6E - -open.error_10000=Success - -# \u683C\u5F0F\uFF1A\u524D\u7F00 + \u7F51\u5173\u9519\u8BEF\u7801 + "_"+ \u5B50\u9519\u8BEF\u7801 -# open.error_\uFF08\u524D\u7F00\uFF0920000\uFF08\u7F51\u5173\u9519\u8BEF\u7801\uFF09_isp.unknow-error\uFF08\u5B50\u9519\u8BEF\u7801\uFF09 -open.error_20000=Service is temporarily unavailable -open.error_20000_isp.unknown-error=Service is temporarily unavailable -open.error_20000_isp.service-unknown-error=Service not available -open.error_20000_aop.unknown-error=Service is temporarily unavailable -open.error_20000_isp.service-not-available=Service is temporarily unavailable -open.error_20000_isp.gateway-response-timeout=Gateway response timeout -open.error_20000_isv.service-busy=service busy - -open.error_20001=Insufficient authorization authority -open.error_20001_aop.invalid-auth-token=Invalid access token -open.error_20001_aop.auth-token-time-out=The access token has expired -open.error_20001_aop.invalid-app-auth-token=Invalid application authorization token -open.error_20001_aop.invalid-app-auth-token-no-api=Merchant does not authorize current interface -open.error_20001_aop.app-auth-token-time-out=The application authorization token has expired -open.error_20001_aop.no-product-reg-by-partner=The merchant has not signed any product - -open.error_40001=Missing required parameters -open.error_40001_isv.missing-method=Method name parameter is missing -open.error_40001_isv.missing-signature=Lack of signature parameter -open.error_40001_isv.missing-signature-type=Missing signature type parameter -open.error_40001_isv.missing-signature-key=Lack of signature configuration -open.error_40001_isv.missing-app-id=Missing appId parameter -open.error_40001_isv.missing-timestamp=Missing timestamp parameter -open.error_40001_isv.missing-version=Missing version parameter -open.error_40001_isv.decryption-error-missing-encrypt-type=Decryption error, no encryption algorithm specified - -open.error_40002=Invalid parameter -open.error_40002_isv.invalid-parameter=Parameter is invalid -open.error_40002_isv.upload-fail=File upload failed -open.error_40002_isv.invalid-file-extension=Invalid file extension -open.error_40002_isv.invalid-file-size=Invalid {0} file size, the max size is {1} -open.error_40002_isv.invalid-method=Nonexistent method name -open.error_40002_isv.invalid-format=Invalid data format -open.error_40002_isv.invalid-signature-type=Invalid signature type -open.error_40002_isv.invalid-signature=Invalid signature -open.error_40002_isv.invalid-encrypt-type=Invalid encryption type -open.error_40002_isv.invalid-encrypt=Decryption exception -open.error_40002_isv.invalid-app-id=Invalid appId parameter -open.error_40002_isv.invalid-timestamp=Invalid timestamp parameter -open.error_40002_isv.invalid-charset=Character set error -open.error_40002_isv.invalid-digest=Digest error -open.error_40002_isv.decryption-error-not-valid-encrypt-type=Decryption error, unsupported encryption algorithm -open.error_40002_isv.decryption-error-not-valid-encrypt-key=Decryption error, unconfigured encryption key or encryption key format error -open.error_40002_isv.decryption-error-unknown=Decryption error, unknown exception -open.error_40002_isv.missing-signature-config=Signature verification error, no public key or certificate of the corresponding signature algorithm is configured -open.error_40002_isv.not-support-app-auth=This interface does not support third-party proxy calls -open.error_40002_isv.suspected-attack=Suspicious attack requests -open.error_40002_isv.invalid-content-type=Invalid content type - -open.error_40004=Business processing failure -open.error_40004_=Business processing failure - -open.error_40006=Insufficient permissions -open.error_40006_isv.insufficient-isv-permissions=Insufficient ISV permissions -open.error_40006_isv.insufficient-user-permissions=Insufficient user permissions -open.error_40006_isv.route-no-permissions=No api permissions -open.error_40006_isv.access-forbidden=Access forbidden -open.error_40006_isv.ip-forbidden=IP access forbidden diff --git a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties b/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties deleted file mode 100644 index c3e8f3ac..00000000 --- a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties +++ /dev/null @@ -1,120 +0,0 @@ -# \u7F51\u5173\u9519\u8BEF\u914D\u7F6E - -#open.error_20000=\u670D\u52A1\u4E0D\u53EF\u7528 -#open.error_20000_isp.unknow-error=\u670D\u52A1\u6682\u4E0D\u53EF\u7528 -#open.error_20000_aop.unknow-error=\u670D\u52A1\u6682\u4E0D\u53EF\u7528 -#open.error_20000_isp.service-not-available=\u670D\u52A1\u6682\u4E0D\u53EF\u7528 -# -#open.error_20001=\u6388\u6743\u6743\u9650\u4E0D\u8DB3 -#open.error_20001_aop.invalid-auth-token=\u65E0\u6548\u7684\u8BBF\u95EE\u4EE4\u724C -#open.error_20001_aop.auth-token-time-out=\u8BBF\u95EE\u4EE4\u724C\u5DF2\u8FC7\u671F -#open.error_20001_aop.invalid-app-auth-token=\u65E0\u6548\u7684\u5E94\u7528\u6388\u6743\u4EE4\u724C -#open.error_20001_aop.invalid-app-auth-token-no-api=\u5546\u6237\u672A\u6388\u6743\u5F53\u524D\u63A5\u53E3 -#open.error_20001_aop.app-auth-token-time-out=\u5E94\u7528\u6388\u6743\u4EE4\u724C\u5DF2\u8FC7\u671F -#open.error_20001_aop.no-product-reg-by-partner=\u5546\u6237\u672A\u7B7E\u7EA6\u4EFB\u4F55\u4EA7\u54C1 -# -#open.error_40001=\u7F3A\u5C11\u5FC5\u9009\u53C2\u6570 -#open.error_40001_isv.missing-method=\u7F3A\u5C11\u65B9\u6CD5\u540D\u53C2\u6570 -#open.error_40001_isv.missing-signature=\u7F3A\u5C11\u7B7E\u540D\u53C2\u6570 -#open.error_40001_isv.missing-signature-type=\u7F3A\u5C11\u7B7E\u540D\u7C7B\u578B\u53C2\u6570 -#open.error_40001_isv.missing-signature-key=\u7F3A\u5C11\u7B7E\u540D\u914D\u7F6E -#open.error_40001_isv.missing-app-id=\u7F3A\u5C11appId\u53C2\u6570 -#open.error_40001_isv.missing-timestamp=\u7F3A\u5C11\u65F6\u95F4\u6233\u53C2\u6570 -#open.error_40001_isv.missing-version=\u7F3A\u5C11\u7248\u672C\u53C2\u6570 -#open.error_40001_isv.decryption-error-missing-encrypt-type=\u89E3\u5BC6\u51FA\u9519, \u672A\u6307\u5B9A\u52A0\u5BC6\u7B97\u6CD5 -# -#open.error_40002=\u975E\u6CD5\u7684\u53C2\u6570 -#open.error_40002_isv.invalid-parameter=\u53C2\u6570\u65E0\u6548 -#open.error_40002_isv.upload-fail=\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25 -#open.error_40002_isv.invalid-file-extension=\u6587\u4EF6\u6269\u5C55\u540D\u65E0\u6548 -#open.error_40002_isv.invalid-file-size={0}\u6587\u4EF6\u5927\u5C0F\u65E0\u6548\uFF0C\u5355\u6587\u4EF6\u4E0D\u5F97\u8D85\u8FC7{1} -#open.error_40002_isv.invalid-method=\u4E0D\u5B58\u5728\u7684\u65B9\u6CD5\u540D -#open.error_40002_isv.invalid-format=\u65E0\u6548\u7684\u6570\u636E\u683C\u5F0F -#open.error_40002_isv.invalid-signature-type=\u65E0\u6548\u7684\u7B7E\u540D\u7C7B\u578B -#open.error_40002_isv.invalid-signature=\u65E0\u6548\u7B7E\u540D -#open.error_40002_isv.invalid-encrypt-type=\u65E0\u6548\u7684\u52A0\u5BC6\u7C7B\u578B -#open.error_40002_isv.invalid-encrypt=\u89E3\u5BC6\u5F02\u5E38 -#open.error_40002_isv.invalid-app-id=\u65E0\u6548\u7684appId\u53C2\u6570 -#open.error_40002_isv.invalid-timestamp=\u975E\u6CD5\u7684\u65F6\u95F4\u6233\u53C2\u6570 -#open.error_40002_isv.invalid-charset=\u5B57\u7B26\u96C6\u9519\u8BEF -#open.error_40002_isv.invalid-digest=\u6458\u8981\u9519\u8BEF -#open.error_40002_isv.decryption-error-not-valid-encrypt-type=\u89E3\u5BC6\u51FA\u9519\uFF0C\u4E0D\u652F\u6301\u7684\u52A0\u5BC6\u7B97\u6CD5 -#open.error_40002_isv.decryption-error-not-valid-encrypt-key=\u89E3\u5BC6\u51FA\u9519, \u672A\u914D\u7F6E\u52A0\u5BC6\u5BC6\u94A5\u6216\u52A0\u5BC6\u5BC6\u94A5\u683C\u5F0F\u9519\u8BEF -#open.error_40002_isv.decryption-error-unknown=\u89E3\u5BC6\u51FA\u9519\uFF0C\u672A\u77E5\u5F02\u5E38 -#open.error_40002_isv.missing-signature-config=\u9A8C\u7B7E\u51FA\u9519, \u672A\u914D\u7F6E\u5BF9\u5E94\u7B7E\u540D\u7B97\u6CD5\u7684\u516C\u94A5\u6216\u8005\u8BC1\u4E66 -#open.error_40002_isv.not-support-app-auth=\u672C\u63A5\u53E3\u4E0D\u652F\u6301\u7B2C\u4E09\u65B9\u4EE3\u7406\u8C03\u7528 -#open.error_40002_isv.suspected-attack=\u53EF\u7591\u7684\u653B\u51FB\u8BF7\u6C42 -#open.error_40002_isv.invalid-content-type=\u65E0\u6548\u7684content-type -# -#open.error_40004=\u4E1A\u52A1\u5904\u7406\u5931\u8D25 -#open.error_40004_=\u4E1A\u52A1\u5904\u7406\u5931\u8D25 -# -#open.error_40006=\u6743\u9650\u4E0D\u8DB3 -#open.error_40006_isv.insufficient-isv-permissions=\u8BF7\u68C0\u67E5\u914D\u7F6E\u7684\u8D26\u6237\u662F\u5426\u6709\u5F53\u524D\u63A5\u53E3\u6743\u9650 -#open.error_40006_isv.insufficient-user-permissions=\u4EE3\u7406\u7684\u5546\u6237\u6CA1\u6709\u5F53\u524D\u63A5\u53E3\u6743\u9650 -#open.error_40006_isv.route-no-permissions=\u6CA1\u6709\u5F53\u524D\u63A5\u53E3\u6743\u9650 -#open.error_40006_isv.access-forbidden=\u65E0\u6743\u8BBF\u95EE - - -open.error_10000=Success - -# \u683C\u5F0F\uFF1A\u524D\u7F00 + \u7F51\u5173\u9519\u8BEF\u7801 + "_"+ \u5B50\u9519\u8BEF\u7801 -# open.error_\uFF08\u524D\u7F00\uFF0920000\uFF08\u7F51\u5173\u9519\u8BEF\u7801\uFF09_isp.unknow-error\uFF08\u5B50\u9519\u8BEF\u7801\uFF09 -open.error_20000=\u670D\u52A1\u4E0D\u53EF\u7528 -open.error_20000_isp.unknown-error=\u670D\u52A1\u6682\u4E0D\u53EF\u7528 -open.error_20000_isp.service-unknown-error=\u670D\u52A1\u4E0D\u53EF\u7528 -open.error_20000_aop.unknown-error=\u670D\u52A1\u6682\u4E0D\u53EF\u7528 -open.error_20000_isp.service-not-available=\u670D\u52A1\u6682\u4E0D\u53EF\u7528 -open.error_20000_isp.gateway-response-timeout=\u7F51\u5173\u54CD\u5E94\u8D85\u65F6 -open.error_20000_isv.service-busy=\u670D\u52A1\u5668\u5FD9 - -open.error_20001=\u6388\u6743\u6743\u9650\u4E0D\u8DB3 -open.error_20001_aop.invalid-auth-token=\u65E0\u6548\u7684\u8BBF\u95EE\u4EE4\u724C -open.error_20001_aop.auth-token-time-out=\u8BBF\u95EE\u4EE4\u724C\u5DF2\u8FC7\u671F -open.error_20001_aop.invalid-app-auth-token=\u65E0\u6548\u7684\u5E94\u7528\u6388\u6743\u4EE4\u724C -open.error_20001_aop.invalid-app-auth-token-no-api=\u5546\u6237\u672A\u6388\u6743\u5F53\u524D\u63A5\u53E3 -open.error_20001_aop.app-auth-token-time-out=\u5E94\u7528\u6388\u6743\u4EE4\u724C\u5DF2\u8FC7\u671F -open.error_20001_aop.no-product-reg-by-partner=\u5546\u6237\u672A\u7B7E\u7EA6\u4EFB\u4F55\u4EA7\u54C1 - -open.error_40001=\u7F3A\u5C11\u5FC5\u9009\u53C2\u6570 -open.error_40001_isv.missing-method=\u7F3A\u5C11\u65B9\u6CD5\u540D\u53C2\u6570 -open.error_40001_isv.missing-signature=\u7F3A\u5C11\u7B7E\u540D\u53C2\u6570 -open.error_40001_isv.missing-signature-type=\u7F3A\u5C11\u7B7E\u540D\u7C7B\u578B\u53C2\u6570 -open.error_40001_isv.missing-signature-key=\u7F3A\u5C11\u7B7E\u540D\u914D\u7F6E -open.error_40001_isv.missing-app-id=\u7F3A\u5C11appId\u53C2\u6570 -open.error_40001_isv.missing-timestamp=\u7F3A\u5C11\u65F6\u95F4\u6233\u53C2\u6570 -open.error_40001_isv.missing-version=\u7F3A\u5C11\u7248\u672C\u53C2\u6570 -open.error_40001_isv.decryption-error-missing-encrypt-type=\u89E3\u5BC6\u51FA\u9519, \u672A\u6307\u5B9A\u52A0\u5BC6\u7B97\u6CD5 - -open.error_40002=\u53c2\u6570\u9519\u8bef -open.error_40002_isv.invalid-parameter=\u53C2\u6570\u65E0\u6548 -open.error_40002_isv.upload-fail=\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25 -open.error_40002_isv.invalid-file-extension=\u6587\u4EF6\u6269\u5C55\u540D\u65E0\u6548 -open.error_40002_isv.invalid-file-size={0}\u6587\u4EF6\u5927\u5C0F\u65E0\u6548\uFF0C\u5355\u6587\u4EF6\u4E0D\u5F97\u8D85\u8FC7{1} -open.error_40002_isv.invalid-method=\u4E0D\u5B58\u5728\u7684\u65B9\u6CD5\u540D -open.error_40002_isv.invalid-format=\u65E0\u6548\u7684\u6570\u636E\u683C\u5F0F -open.error_40002_isv.invalid-signature-type=\u65E0\u6548\u7684\u7B7E\u540D\u7C7B\u578B -open.error_40002_isv.invalid-signature=\u65E0\u6548\u7B7E\u540D -open.error_40002_isv.invalid-encrypt-type=\u65E0\u6548\u7684\u52A0\u5BC6\u7C7B\u578B -open.error_40002_isv.invalid-encrypt=\u89E3\u5BC6\u5F02\u5E38 -open.error_40002_isv.invalid-app-id=\u65E0\u6548\u7684appId\u53C2\u6570 -open.error_40002_isv.invalid-timestamp=\u975E\u6CD5\u7684\u65F6\u95F4\u6233\u53C2\u6570 -open.error_40002_isv.invalid-charset=\u5B57\u7B26\u96C6\u9519\u8BEF -open.error_40002_isv.invalid-digest=\u6458\u8981\u9519\u8BEF -open.error_40002_isv.decryption-error-not-valid-encrypt-type=\u89E3\u5BC6\u51FA\u9519\uFF0C\u4E0D\u652F\u6301\u7684\u52A0\u5BC6\u7B97\u6CD5 -open.error_40002_isv.decryption-error-not-valid-encrypt-key=\u89E3\u5BC6\u51FA\u9519, \u672A\u914D\u7F6E\u52A0\u5BC6\u5BC6\u94A5\u6216\u52A0\u5BC6\u5BC6\u94A5\u683C\u5F0F\u9519\u8BEF -open.error_40002_isv.decryption-error-unknown=\u89E3\u5BC6\u51FA\u9519\uFF0C\u672A\u77E5\u5F02\u5E38 -open.error_40002_isv.missing-signature-config=\u9A8C\u7B7E\u51FA\u9519, \u672A\u914D\u7F6E\u5BF9\u5E94\u7B7E\u540D\u7B97\u6CD5\u7684\u516C\u94A5\u6216\u8005\u8BC1\u4E66 -open.error_40002_isv.not-support-app-auth=\u672C\u63A5\u53E3\u4E0D\u652F\u6301\u7B2C\u4E09\u65B9\u4EE3\u7406\u8C03\u7528 -open.error_40002_isv.suspected-attack=\u53EF\u7591\u7684\u653B\u51FB\u8BF7\u6C42 -open.error_40002_isv.invalid-content-type=\u65E0\u6548\u7684content-type - -open.error_40004=\u4E1A\u52A1\u5904\u7406\u5931\u8D25 -open.error_40004_=\u4E1A\u52A1\u5904\u7406\u5931\u8D25 - -open.error_40006=\u6743\u9650\u4E0D\u8DB3 -open.error_40006_isv.insufficient-isv-permissions=\u8BF7\u68C0\u67E5\u914D\u7F6E\u7684\u8D26\u6237\u662F\u5426\u6709\u5F53\u524D\u63A5\u53E3\u6743\u9650 -open.error_40006_isv.insufficient-user-permissions=\u4EE3\u7406\u7684\u5546\u6237\u6CA1\u6709\u5F53\u524D\u63A5\u53E3\u6743\u9650 -open.error_40006_isv.route-no-permissions=\u6CA1\u6709\u5F53\u524D\u63A5\u53E3\u6743\u9650 -open.error_40006_isv.access-forbidden=\u65E0\u6743\u8BBF\u95EE -open.error_40006_isv.ip-forbidden=IP\u65E0\u6743\u8BBF\u95EE diff --git a/sop-common/sop-gateway-common/src/main/resources/sop/limit.lua b/sop-common/sop-gateway-common/src/main/resources/sop/limit.lua deleted file mode 100644 index cc956e5d..00000000 --- a/sop-common/sop-gateway-common/src/main/resources/sop/limit.lua +++ /dev/null @@ -1,10 +0,0 @@ -local key = KEYS[1] --限流KEY(一秒一个) -local limit = tonumber(ARGV[1]) --限流大小 -local current = tonumber(redis.call("get", key)) or 0 -if current + 1 > limit then --如果超出限流大小 - return 0 -else --请求数+1,并设置n秒过期 - redis.call("INCRBY", key, "1") - redis.call("expire", key, ARGV[2]) - return 1 -end \ No newline at end of file diff --git a/sop-common/sop-service-common/.gitignore b/sop-common/sop-service-common/.gitignore deleted file mode 100644 index c456c4a3..00000000 --- a/sop-common/sop-service-common/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -/build/ diff --git a/sop-common/sop-service-common/pom.xml b/sop-common/sop-service-common/pom.xml deleted file mode 100644 index d8506fe7..00000000 --- a/sop-common/sop-service-common/pom.xml +++ /dev/null @@ -1,313 +0,0 @@ - - - - 4.0.0 - com.gitee.sop - sop-service-common - 5.0.0-SNAPSHOT - jar - - - 1.8 - - - 2.6.15 - - 2021.0.5 - - - 2021.0.5.0 - - - 1.2.3 - - 4.11 - 1.2.73 - 2.5 - 1.3.3 - 3.2.2 - 3.8.1 - 1.11 - 1.2 - 2.0.1.Final - 6.0.13.Final - 1.9.1 - 29.0-jre - 1.5.21 - 3.0.0 - 3.0.0 - 1.16.9 - 5.2.0 - 6.2 - - - - - - com.google.guava - guava - - - - org.apache.commons - commons-lang3 - - - - commons-io - commons-io - - - - commons-fileupload - commons-fileupload - - - - com.alibaba - fastjson - - - - org.hibernate - hibernate-validator - - - - org.ow2.asm - asm - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - true - - - org.springframework - spring-webmvc - true - - - io.springfox - springfox-swagger2 - true - - - io.swagger - swagger-annotations - true - - - junit - junit - 4.12 - test - - - javax.el - javax.el-api - 3.0.0 - test - - - org.glassfish - javax.el - 3.0.0 - test - - - - org.projectlombok - lombok - provided - - - javax.servlet - javax.servlet-api - provided - - - org.springframework.boot - spring-boot-autoconfigure - - - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud.version} - pom - import - - - com.alibaba.cloud - spring-cloud-alibaba-dependencies - ${spring-cloud-alibaba.version} - pom - import - - - - com.google.guava - guava - ${guava.version} - - - - com.alibaba - fastjson - ${fastjson.version} - - - - net.oschina.durcframework - fastmybatis-spring-boot-starter - ${fastmybatis.version} - - - - io.springfox - springfox-spring-web - ${springfox-spring-web.version} - - - io.swagger - swagger-annotations - ${swagger.version} - - - io.springfox - springfox-swagger2 - ${springfox-swagger2.version} - - - - net.oschina.durcframework - easyopen - ${easyopen.version} - - - - com.squareup.okhttp3 - okhttp - 3.14.7 - - - - net.oschina.durcframework - easyopen-spring-boot-starter - ${easyopen.version} - - - - javax.validation - validation-api - ${validation-api.version} - - - org.hibernate - hibernate-validator - ${hibernate-validator.version} - - - - org.ow2.asm - asm - ${asm.version} - - - - ch.qos.logback - logback-classic - ${logback.version} - - - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - - commons-collections - commons-collections - ${commons-collection.version} - - - commons-io - commons-io - ${commons-io.version} - - - commons-codec - commons-codec - ${commons-codec.version} - - - commons-fileupload - commons-fileupload - ${commons-fileupload.version} - - - commons-logging - commons-logging - ${commons-logging.version} - - - - com.github.pagehelper - pagehelper - ${pagehelper.version} - - - - org.projectlombok - lombok - 1.18.4 - - - javax.servlet - javax.servlet-api - 3.1.0 - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12.4 - - true - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - ${java.version} - ${java.version} - UTF-8 - - - - - - diff --git a/sop-common/sop-service-common/readme.md b/sop-common/sop-service-common/readme.md deleted file mode 100644 index 149cc7f7..00000000 --- a/sop-common/sop-service-common/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# sop-service-common - -微服务端公共组件,每个微服务需要依赖这个jar,先把这个工程mvn deploy到maven私服。 diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/BizCode.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/BizCode.java deleted file mode 100644 index c290fd00..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/BizCode.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.sop.servercommon.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 错误码 - * - * @author 六如 - */ -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface BizCode { - - /** - * 错误码 - * - * @return - */ - String code(); - - /** - * 错误描述 - * @return - */ - String msg(); - - /** - * 解决方案 - * @return - */ - String solution() default ""; -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/Open.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/Open.java deleted file mode 100644 index 1548195f..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/Open.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.gitee.sop.servercommon.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author 六如 - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface Open { - - /** - * 接口名,如:member.user.get - */ - String value(); - - /** - * 版本号,默认版本号是""
- * 改默认版本号:ServiceConfig.getInstance().setDefaultVersion("1.0"); - */ - String version() default ""; - - /** - * 忽略验证,业务参数除外 - */ - boolean ignoreValidate() default false; - - /** - * 告诉网关是否对结果进行合并,默认合并。设置为false,客户端将直接收到微服务端的结果。 - */ - boolean mergeResult() default true; - - /** - * 指定接口是否需要授权才能访问,可在admin中进行修改 - */ - boolean permission() default false; - - /** - * 是否需要appAuthToken,设置为true,网关端会校验token是否存在 - */ - boolean needToken() default false; - - /** - * 定义业务错误码,用于文档显示 - */ - BizCode[] bizCode() default {}; -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java deleted file mode 100644 index 50948f17..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.gitee.sop.servercommon.bean; - -import org.apache.commons.lang3.time.DateUtils; - -import java.text.ParseException; -import java.util.Date; -import java.util.Map; - -import static com.gitee.sop.servercommon.bean.ParamNames.API_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.APP_AUTH_TOKEN_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.APP_KEY_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.BIZ_CONTENT_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.CHARSET_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.FORMAT_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.NOTIFY_URL_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.SIGN_TYPE_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.TIMESTAMP_NAME; -import static com.gitee.sop.servercommon.bean.ParamNames.TIMESTAMP_PATTERN; -import static com.gitee.sop.servercommon.bean.ParamNames.VERSION_NAME; - -/** - * 获取开放平台请求参数。 - * - * @author 六如 - */ -public interface OpenContext { - - /** - * 返回所有的请求参数 - * - * @return 返回所有的请求参数 - */ - Map getParameterMap(); - - /** - * 返回业务对象 - * - * @return 业务对象 - */ - Object getBizObject(); - - - /** - * 获取某个参数值 - * - * @param name 参数名称 - * @return 没有返回null - */ - default String getParameter(String name) { - Object value = getParameterMap().get(name); - return value == null ? null : value.toString(); - } - - /** - * 返回appid - * - * @return 返回appId - */ - default String getAppId() { - return getParameter(APP_KEY_NAME); - } - - /** - * 返回业务参数json字符串 - * - * @return 返回字符串业务参数 - */ - default String getBizContent() { - return getParameter(BIZ_CONTENT_NAME); - } - - - /** - * 返回字符编码 - * - * @return 如UTF-8 - */ - default String getCharset() { - return getParameter(CHARSET_NAME); - } - - /** - * 返回接口名 - * - * @return 如:alipay.goods.get - */ - default String getMethod() { - return getParameter(API_NAME); - } - - /** - * 返回版本号 - * - * @return 如:1.0 - */ - default String getVersion() { - return getParameter(VERSION_NAME); - } - - /** - * 返回参数格式化 - * - * @return 如:json - */ - default String getFormat() { - return getParameter(FORMAT_NAME); - } - - /** - * 返回签名类型 - * - * @return 如:RSA2 - */ - default String getSignType() { - return getParameter(SIGN_TYPE_NAME); - } - - /** - * 返回时间戳 - * - * @return - */ - default Date getTimestamp() { - String timestampStr = getParameter(TIMESTAMP_NAME); - try { - return DateUtils.parseDate(timestampStr, TIMESTAMP_PATTERN); - } catch (ParseException e) { - return null; - } - } - - - /** - * 返回token - * - * @return 返回token - */ - default String getAppAuthToken() { - return getParameter(APP_AUTH_TOKEN_NAME); - } - - /** - * 返回回调地址 - * - * @return 返回回调地址 - */ - default String getNotifyUrl() { - return getParameter(NOTIFY_URL_NAME); - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java deleted file mode 100644 index 7ce6612e..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.gitee.sop.servercommon.bean; - -import java.util.Map; - -/** - * @author 六如 - */ -public class OpenContextImpl implements OpenContext { - private final Map requestParams; - private Object bizObject; - - public OpenContextImpl(Map requestParams) { - this.requestParams = requestParams; - } - - public void setBizObject(Object bizObject) { - this.bizObject = bizObject; - } - - @Override - public Map getParameterMap() { - return requestParams; - } - - @Override - public Object getBizObject() { - return bizObject; - } - - @Override - public String toString() { - return requestParams.toString(); - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ParamNames.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ParamNames.java deleted file mode 100644 index 072f3fb1..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ParamNames.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gitee.sop.servercommon.bean; - -/** - * 请求参数名定义 - * - * 参数 类型 是否必填 最大长度 描述 示例值 - * app_id String 是 32 支付宝分配给开发者的应用ID 2014072300007148 - * method String 是 128 接口名称 alipay.trade.fastpay.refund.query - * format String 否 40 仅支持JSON JSON - * charset String 是 10 请求使用的编码格式,如utf-8,gbk,gb2312等 utf-8 - * sign_type String 是 10 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2 - * sign String 是 344 商户请求参数的签名串,详见签名 详见示例 - * timestamp String 是 19 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50 - * version String 是 3 调用的接口版本,固定为:1.0 1.0 - * notify_url String 否 256 支付宝服务器主动通知商户服务器里指定的页面http/https路径。 http://api.test.alipay.net/atinterface/receive_notify.htm - * app_auth_token String 否 40 详见应用授权概述 - * biz_content String 是 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 - * - * @author 六如 - */ -public class ParamNames { - /** 分配给开发者的应用ID */ - public static String APP_KEY_NAME = "app_id"; - /** 接口名称 */ - public static String API_NAME = "method"; - /** 仅支持JSON */ - public static String FORMAT_NAME = "format"; - /** 请求使用的编码格式 */ - public static String CHARSET_NAME = "charset"; - /** 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 */ - public static String SIGN_TYPE_NAME = "sign_type"; - /** 商户请求参数的签名串 */ - public static String SIGN_NAME = "sign"; - /** 发送请求的时间 */ - public static String TIMESTAMP_NAME = "timestamp"; - /** 调用的接口版本 */ - public static String VERSION_NAME = "version"; - /** 开放平台主动通知商户服务器里指定的页面http/https路径 */ - public static String NOTIFY_URL_NAME = "notify_url"; - /** OAuth 2.0授权token */ - public static String APP_AUTH_TOKEN_NAME = "app_auth_token"; - /** 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 */ - public static String BIZ_CONTENT_NAME = "biz_content"; - - /** 时间戳格式 */ - public static String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss"; - - public static String HEADER_VERSION_NAME = "sop-version"; - - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java deleted file mode 100644 index 362c27b3..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.gitee.sop.servercommon.bean; - -import com.gitee.sop.servercommon.route.RouteDefinition; -import lombok.Data; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; -import java.util.Objects; - -/** - * @author 六如 - */ -@Data -public class ServiceApiInfo { - private String serviceId; - private List apis; - private List routeDefinitionList; - - @Getter - @Setter - public static class ApiMeta { - /** 接口名 */ - private String name; - /** 请求path */ - private String path; - /** 版本号 */ - private String version; - /** 是否忽略验证 */ - private int ignoreValidate; - /** 是否合并结果 */ - private int mergeResult; - /** 是否需要授权才能访问 */ - private int permission; - /** 是否需要token */ - private int needToken; - - public ApiMeta() { - } - - public ApiMeta(String name, String path, String version) { - this.name = name; - this.path = path; - this.version = version; - } - - public String fetchNameVersion() { - return this.name + this.version; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ApiMeta apiMeta = (ApiMeta) o; - return name.equals(apiMeta.name) && - Objects.equals(version, apiMeta.version); - } - - @Override - public int hashCode() { - return Objects.hash(name, version); - } - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConfig.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConfig.java deleted file mode 100644 index 80e2a659..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConfig.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.gitee.sop.servercommon.bean; - -import com.gitee.sop.servercommon.param.ApiArgumentResolver; -import com.gitee.sop.servercommon.param.SopHandlerMethodArgumentResolver; -import com.gitee.sop.servercommon.result.DefaultServiceResultBuilder; -import com.gitee.sop.servercommon.result.ServiceResultBuilder; -import lombok.Data; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author 六如 - */ -@Data -public class ServiceConfig { - - private static ServiceConfig instance = new ServiceConfig(); - - private ServiceConfig() { - } - - public static ServiceConfig getInstance() { - return instance; - } - - public static void setInstance(ServiceConfig instance) { - ServiceConfig.instance = instance; - } - - /** - * 错误模块 - */ - private List i18nModules = new ArrayList<>(); - - /** - * 解析业务参数 - */ - private SopHandlerMethodArgumentResolver methodArgumentResolver = new ApiArgumentResolver(); - - /** - * 返回结果处理 - */ - private ServiceResultBuilder serviceResultBuilder = new DefaultServiceResultBuilder(); - - /** - * 默认版本号 - */ - private String defaultVersion = ""; - - /** - * 设置为true,所有接口不进行校验 - */ - private boolean ignoreValidate; - - /** - * 是否对结果进行合并 - */ - private boolean mergeResult = true; - - /** - * 是否需要授权才能访问 - */ - private boolean permission; - - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceContext.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceContext.java deleted file mode 100644 index 41ee02bf..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceContext.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.gitee.sop.servercommon.bean; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Locale; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author 六如 - */ -public class ServiceContext extends ConcurrentHashMap { - - public static final String REQUEST_KEY = "sop-request"; - public static final String RESPONSE_KEY = "sop-response"; - public static final String OPEN_CONTEXT_KEY = "sop-open-context"; - - protected static Class contextClass = ServiceContext.class; - - protected static final ThreadLocal THREAD_LOCAL = new ThreadLocal() { - @Override - protected ServiceContext initialValue() { - try { - return contextClass.newInstance(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - }; - - - public ServiceContext() { - super(); - } - - public void setOpenContext(OpenContext openContext) { - set(OPEN_CONTEXT_KEY, openContext); - } - - public OpenContext getOpenContext() { - return (OpenContext) get(OPEN_CONTEXT_KEY); - } - - public Locale getLocale() { - HttpServletRequest request = getRequest(); - if (request == null) { - return Locale.SIMPLIFIED_CHINESE; - } - return request.getLocale(); - } - - /** - * Override the default ServiceContext - * - * @param clazz - */ - public static void setContextClass(Class clazz) { - contextClass = clazz; - } - - - /** - * Get the current ServiceContext - * - * @return the current ServiceContext - */ - public static ServiceContext getCurrentContext() { - return THREAD_LOCAL.get(); - } - - /** - * Convenience method to return a boolean value for a given key - * - * @param key - * @return true or false depending what was set. default is false - */ - public boolean getBoolean(String key) { - return getBoolean(key, false); - } - - /** - * Convenience method to return a boolean value for a given key - * - * @param key - * @param defaultResponse - * @return true or false depending what was set. default defaultResponse - */ - public boolean getBoolean(String key, boolean defaultResponse) { - Boolean b = (Boolean) get(key); - if (b != null) { - return b.booleanValue(); - } - return defaultResponse; - } - - /** - * sets a key value to Boolen.TRUE - * - * @param key - */ - public void set(String key) { - put(key, Boolean.TRUE); - } - - /** - * puts the key, value into the map. a null value will remove the key from the map - * - * @param key - * @param value - */ - public void set(String key, Object value) { - if (value != null) { - put(key, value); - } else { - remove(key); - } - } - - /** - * @return the HttpServletRequest from the "request" key - */ - public HttpServletRequest getRequest() { - return (HttpServletRequest) get(REQUEST_KEY); - } - - /** - * sets the HttpServletRequest into the "request" key - * - * @param request - */ - public void setRequest(HttpServletRequest request) { - put(REQUEST_KEY, request); - } - - /** - * @return the HttpServletResponse from the "response" key - */ - public HttpServletResponse getResponse() { - return (HttpServletResponse) get(RESPONSE_KEY); - } - - /** - * sets the "response" key to the HttpServletResponse passed in - * - * @param response - */ - public void setResponse(HttpServletResponse response) { - set(RESPONSE_KEY, response); - } - - - /** - * unsets the THREAD_LOCAL context. Done at the end of the request. - */ - public void unset() { - THREAD_LOCAL.remove(); - } - - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java deleted file mode 100644 index 74c9363a..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gitee.sop.servercommon.configuration; - -/** - * 具备支付宝开放平台服务提供能力 - * @author 六如 - */ -public class AlipayServiceConfiguration extends BaseServiceConfiguration { - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java deleted file mode 100644 index 8aba0cac..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.servercommon.configuration; - -import lombok.extern.slf4j.Slf4j; - -/** - * @author 六如 - */ -@Slf4j -public class BaseServiceConfiguration extends ServiceConfiguration { - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java deleted file mode 100644 index 2d1abb68..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.gitee.sop.servercommon.configuration; - -import com.gitee.sop.servercommon.bean.ServiceConfig; -import com.gitee.sop.servercommon.exception.ServiceException; -import com.gitee.sop.servercommon.result.ServiceResultBuilder; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.util.UriUtils; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.nio.charset.StandardCharsets; - -/** - * 全局异常处理,不在使用,目的是不与原有的全局异常冲突。替代方案见sop-story中的 - * - * com.gitee.sop.storyweb.StoryGlobalExceptionHandler - * - * @author 六如 - */ -@Deprecated -@ControllerAdvice -@Slf4j -public class GlobalExceptionHandler { - - - /** - * 与网关约定好的状态码,表示业务出错 - */ - private static final int BIZ_ERROR_CODE = 4000; - - /** - * 与网关约定好的系统错误状态码 - */ - private static final int SYSTEM_ERROR_CODE = 5050; - - /** - * header中的错误code - */ - private static final String X_SERVICE_ERROR_HEADER_NAME = "x-service-error-code"; - - /** - * header中的错误信息 - */ - private static final String X_SERVICE_ERROR_MESSAGE = "x-service-error-message"; - - /** - * 捕获手动抛出的异常 - * - * @param request request - * @param response response - * @param exception 异常信息 - * @return 返回提示信息 - */ - @ExceptionHandler(ServiceException.class) - @ResponseBody - public Object serviceExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { - response.addHeader(X_SERVICE_ERROR_HEADER_NAME, String.valueOf(BIZ_ERROR_CODE)); - return this.processError(request, response, exception); - } - - /** - * 捕获未知异常 - * - * @param request request - * @param response response - * @param exception 异常信息 - * @return 返回提示信息 - */ - @ExceptionHandler(Exception.class) - @ResponseBody - public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { - log.error("系统错误", exception); - StringBuilder msg = new StringBuilder(); - msg.append(exception.getMessage()); - StackTraceElement[] stackTrace = exception.getStackTrace(); - // 取5行错误内容 - int lineCount = 5; - for (int i = 0; i < stackTrace.length && i < lineCount; i++) { - StackTraceElement stackTraceElement = stackTrace[i]; - msg.append("\n at ").append(stackTraceElement.toString()); - } - // 需要设置两个值,这样网关会收到错误信息 - // 并且会统计到监控当中 - response.setHeader(X_SERVICE_ERROR_HEADER_NAME, String.valueOf(SYSTEM_ERROR_CODE)); - response.setHeader(X_SERVICE_ERROR_MESSAGE, UriUtils.encode(msg.toString(), StandardCharsets.UTF_8)); - return this.processError(request, response, new ServiceException("系统繁忙")); - } - - /** - * 处理异常 - * - * @param request request - * @param response response - * @param exception 异常信息 - * @return 返回最终结果 - */ - protected Object processError(HttpServletRequest request, HttpServletResponse response, Exception exception) { - ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder(); - return serviceResultBuilder.buildError(request, response, exception); - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/ServiceConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/ServiceConfiguration.java deleted file mode 100644 index 4b831101..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/ServiceConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.gitee.sop.servercommon.configuration; - -import com.alibaba.cloud.nacos.NacosDiscoveryProperties; -import com.alibaba.cloud.nacos.NacosServiceManager; -import com.alibaba.cloud.nacos.discovery.NacosWatch; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.core.env.Environment; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -import java.util.Map; - -/** - * @author 六如 - */ -@Slf4j -public class ServiceConfiguration extends SpringmvcConfiguration { - - @Bean - @ConditionalOnMissingBean - @ConditionalOnProperty("spring.cloud.nacos.discovery.server-addr") - public NacosWatch nacosWatch( - NacosServiceManager nacosServiceManager, - NacosDiscoveryProperties nacosDiscoveryProperties, - ObjectProvider taskScheduler, - Environment environment - ) { - Map metadata = nacosDiscoveryProperties.getMetadata(); - String contextPath = environment.getProperty(METADATA_SERVER_CONTEXT_PATH); - // 将context-path信息加入到metadata中 - if (contextPath != null) { - metadata.put(METADATA_SERVER_CONTEXT_PATH, contextPath); - } - // 在元数据中新增启动时间,不能修改这个值,不然网关拉取接口会有问题 - // 如果没有这个值,网关会忽略这个服务 - metadata.put("server.startup-time", String.valueOf(System.currentTimeMillis())); - return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties); - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringmvcConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringmvcConfiguration.java deleted file mode 100644 index a589a706..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringmvcConfiguration.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.gitee.sop.servercommon.configuration; - -import com.gitee.sop.servercommon.bean.ServiceConfig; -import com.gitee.sop.servercommon.interceptor.ServiceContextInterceptor; -import com.gitee.sop.servercommon.message.ServiceErrorFactory; -import com.gitee.sop.servercommon.param.SopHandlerMethodArgumentResolver; -import com.gitee.sop.servercommon.route.ServiceRouteController; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.StringHttpMessageConverter; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; - -import javax.annotation.PostConstruct; -import java.nio.charset.StandardCharsets; -import java.util.List; - -/** - * @author 六如 - */ -@Slf4j -public class SpringmvcConfiguration implements WebMvcConfigurer, BeanPostProcessor { - - public static final String METADATA_SERVER_CONTEXT_PATH = "server.servlet.context-path"; - - static { - ServiceConfig.getInstance().setDefaultVersion("1.0"); - } - - public SpringmvcConfiguration() { - ServiceConfig.getInstance().getI18nModules().add("i18n/isp/bizerror"); - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof RequestMappingHandlerAdapter) { - RequestMappingHandlerAdapter requestMappingHandlerAdapter = (RequestMappingHandlerAdapter) bean; - SopHandlerMethodArgumentResolver sopHandlerMethodArgumentResolver = ServiceConfig.getInstance().getMethodArgumentResolver(); - sopHandlerMethodArgumentResolver.setRequestMappingHandlerAdapter(requestMappingHandlerAdapter); - } - return bean; - } - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - // 支持swagger-bootstrap-ui首页 - registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); - // 支持默认swagger - registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); - registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); - } - - @Override - public void extendMessageConverters(List> converters) { - // 解决controller返回字符串中文乱码问题 - for (HttpMessageConverter converter : converters) { - if (converter instanceof StringHttpMessageConverter) { - ((StringHttpMessageConverter)converter).setDefaultCharset(StandardCharsets.UTF_8); - } - } - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - // 添加拦截器 - registry.addInterceptor(new ServiceContextInterceptor()); - } - - @Bean - @ConditionalOnMissingBean - ServiceRouteController serviceRouteInfoHandler() { - return new ServiceRouteController(); - } - - @PostConstruct - public final void after() { - log.info("-----spring容器加载完毕-----"); - initMessage(); - doAfter(); - } - - - /** - * spring容器加载完毕后执行 - */ - protected void doAfter() { - - } - - - protected void initMessage() { - ServiceErrorFactory.initMessageSource(ServiceConfig.getInstance().getI18nModules()); - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ExceptionHolder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ExceptionHolder.java deleted file mode 100644 index 1dc08386..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ExceptionHolder.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.gitee.sop.servercommon.exception; - -import com.alibaba.fastjson.JSON; -import com.gitee.sop.servercommon.bean.ServiceConfig; -import com.gitee.sop.servercommon.result.ServiceResultBuilder; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.util.UriUtils; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.nio.charset.StandardCharsets; - -/** - * @author 六如 - */ -@Slf4j -public class ExceptionHolder { - /** - * 与网关约定好的状态码,表示业务出错 - */ - private static final int BIZ_ERROR_CODE = 4000; - - /** - * 与网关约定好的系统错误状态码 - */ - private static final int SYSTEM_ERROR_CODE = 5050; - - /** - * header中的错误code - */ - private static final String X_SERVICE_ERROR_HEADER_NAME = "x-service-error-code"; - - /** - * header中的错误信息 - */ - private static final String X_SERVICE_ERROR_MESSAGE = "x-service-error-message"; - - /** - * header中的返回信息 - */ - private static final String X_SERVICE_ERROR_RESPONSE = "x-service-error-response"; - - /** - * 处理微服务异常信息,做到不与原系统的错误处理相冲突 - * @param request request - * @param response response - * @param exception exception - */ - public static void hold(HttpServletRequest request, HttpServletResponse response, Exception exception) { - log.error("系统错误", exception); - int code = exception instanceof ServiceException ? BIZ_ERROR_CODE : SYSTEM_ERROR_CODE; - // 需要设置两个值,这样网关会收到错误信息 - // 并且会统计到监控当中 - response.setHeader(X_SERVICE_ERROR_HEADER_NAME, String.valueOf(code)); - String responseBody = buildResponse(request, response, exception); - response.setHeader(X_SERVICE_ERROR_RESPONSE, UriUtils.encode(responseBody, StandardCharsets.UTF_8)); - - // 如果是未知错误,还需要收集异常信息 - if (code == SYSTEM_ERROR_CODE) { - StringBuilder msg = new StringBuilder(); - msg.append(exception.getMessage()); - StackTraceElement[] stackTrace = exception.getStackTrace(); - // 取5行错误内容 - int lineCount = 5; - for (int i = 0; i < stackTrace.length && i < lineCount; i++) { - StackTraceElement stackTraceElement = stackTrace[i]; - msg.append("\n at ").append(stackTraceElement.toString()); - } - response.setHeader(X_SERVICE_ERROR_MESSAGE, UriUtils.encode(msg.toString(), StandardCharsets.UTF_8)); - } - } - - /** - * 处理异常 - * - * @param request request - * @param response response - * @param exception 异常信息 - * @return 返回最终结果 - */ - private static String buildResponse(HttpServletRequest request, HttpServletResponse response, Exception exception) { - ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder(); - Object result = serviceResultBuilder.buildError(request, response, exception); - return JSON.toJSONString(result); - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ServiceException.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ServiceException.java deleted file mode 100644 index 0c600ffc..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ServiceException.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.servercommon.exception; - -import com.gitee.sop.servercommon.message.ServiceError; -import com.gitee.sop.servercommon.message.ServiceErrorEnum; -import com.gitee.sop.servercommon.message.ServiceErrorImpl; - -/** - * @author 六如 - */ -public class ServiceException extends RuntimeException { - - private final transient ServiceError error; - - public ServiceException(String subCode, String subMsg) { - super(subMsg); - this.error = new ServiceErrorImpl(subCode, subMsg); - } - - public ServiceException(String subMsg) { - super(subMsg); - String subCode = ServiceErrorEnum.ISV_COMMON_ERROR.getErrorMeta().getError().getSub_code(); - this.error = new ServiceErrorImpl(subCode, subMsg); - } - - public ServiceException(ServiceError error) { - super(error.toString()); - this.error = error; - } - - public ServiceError getError() { - return error; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java deleted file mode 100644 index 13c46233..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.gitee.sop.servercommon.interceptor; - -import com.alibaba.fastjson.JSONObject; -import com.gitee.sop.servercommon.annotation.Open; -import com.gitee.sop.servercommon.bean.OpenContextImpl; -import com.gitee.sop.servercommon.bean.ServiceContext; -import com.gitee.sop.servercommon.util.OpenUtil; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author 六如 - */ -public class ServiceContextInterceptor extends HandlerInterceptorAdapter { - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - ServiceContext context = ServiceContext.getCurrentContext(); - context.setRequest(request); - context.setResponse(response); - this.initOpenContextWithNoParam(context, request, handler); - return true; - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { - ServiceContext.getCurrentContext().unset(); - } - - /** - * 修复没有参数的情况下无法获取OpenContext - * @param context ServiceContext - * @param request HttpServletRequest - * @param handler HandlerMethod - */ - private void initOpenContextWithNoParam(ServiceContext context, HttpServletRequest request, Object handler) { - if (handler instanceof HandlerMethod) { - HandlerMethod handlerMethod = (HandlerMethod) handler; - Open open = handlerMethod.getMethodAnnotation(Open.class); - if (open != null && getArrayLength(handlerMethod.getMethodParameters()) == 0) { - JSONObject requestParams = OpenUtil.getRequestParams(request); - OpenContextImpl openContext = new OpenContextImpl(requestParams); - context.setOpenContext(openContext); - } - } - } - - private static int getArrayLength(T[] arr) { - return arr == null ? 0 : arr.length; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceError.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceError.java deleted file mode 100644 index 54a8a2e7..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceError.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.sop.servercommon.message; - -/** - * 定义错误返回 - * sub_code(明细返回码) - * sub_msg(明细返回码描述) - * 解决方案 - * @author 六如 - */ -public interface ServiceError { - - /** - * sub_code(明细返回码) - * @return sub_code(明细返回码) - */ - String getSub_code(); - - /** - * sub_msg(明细返回码描述) - * @return sub_msg(明细返回码描述) - */ - String getSub_msg(); - - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java deleted file mode 100644 index 8d42dcbf..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.sop.servercommon.message; - -/** - * @author 六如 - */ -public enum ServiceErrorEnum { - /** 未知错误 */ - ISP_UNKNOWN_ERROR("isp.service-unknown-error"), - /** 参数错误 */ - ISV_PARAM_ERROR("isv.invalid-parameter"), - /** 通用错误 */ - ISV_COMMON_ERROR("isv.common-error"), - ; - private ServiceErrorMeta errorMeta; - - ServiceErrorEnum(String subCode) { - this.errorMeta = new ServiceErrorMeta("isp.error_", subCode); - } - - public ServiceErrorMeta getErrorMeta() { - return errorMeta; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorFactory.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorFactory.java deleted file mode 100644 index 8cc1c5e9..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorFactory.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.gitee.sop.servercommon.message; - - -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.context.support.ResourceBundleMessageSource; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * 负责构建错误消息 - * - * @author 六如 - */ -@Slf4j -public class ServiceErrorFactory { - - private ServiceErrorFactory(){} - - public static final String SYS_ERR = "系统错误"; - - private static final String I18N_OPEN_ERROR = "i18n/isp/error"; - - private static Set noModuleCache = new HashSet<>(); - - private static Map errorCache = new HashMap<>(64); - - /** - * 错误信息的国际化信息 - */ - private static MessageSourceAccessor errorMessageSourceAccessor; - - /** - * 设置国际化资源信息 - */ - public static void initMessageSource(List isvModules) { - HashSet baseNamesSet = new HashSet<>(); - baseNamesSet.add(I18N_OPEN_ERROR); - - if (!isvModules.isEmpty()) { - baseNamesSet.addAll(isvModules); - } - - String[] totalBaseNames = baseNamesSet.toArray(new String[0]); - - log.info("加载错误码国际化资源:{}", StringUtils.arrayToCommaDelimitedString(totalBaseNames)); - ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource(); - bundleMessageSource.setBasenames(totalBaseNames); - MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(bundleMessageSource); - setErrorMessageSourceAccessor(messageSourceAccessor); - } - - /** - * 通过ErrorMeta,Locale,params构建国际化错误消息 - * - * @param errorMeta 错误信息 - * @param locale 本地化 - * @param params 参数 - * @return 如果没有配置国际化消息,则直接返回errorMeta中的信息 - */ - public static ServiceError getError(ServiceErrorMeta errorMeta, Locale locale, Object... params) { - String key = errorMeta.getModulePrefix() + errorMeta.getSubCode() + locale.toString(); - ServiceError error = errorCache.get(key); - if (error == null) { - Assert.notNull(locale, "未设置Locale"); - String modulePrefix = errorMeta.getModulePrefix(); - String subCode = errorMeta.getSubCode(); - // isp.error_sp.unknow-error=Service is temporarily unavailable - String subMsg = getErrorMessage(modulePrefix + subCode, locale, params); - if (StringUtils.isEmpty(subMsg)) { - subMsg = SYS_ERR; - } - error = new ServiceErrorImpl(subCode, subMsg); - errorCache.put(key, error); - } - return error; - } - - - public static void setErrorMessageSourceAccessor(MessageSourceAccessor errorMessageSourceAccessor) { - ServiceErrorFactory.errorMessageSourceAccessor = errorMessageSourceAccessor; - } - - /** - * 返回本地化信息 - * - * @param module 错误模块 - * @param locale 本地化 - * @param params 参数 - * @return 返回信息 - */ - public static String getErrorMessage(String module, Locale locale, Object... params) { - if (noModuleCache.contains(module)) { - return null; - } - try { - return errorMessageSourceAccessor.getMessage(module, params, locale); - } catch (Exception e) { - noModuleCache.add(module); - return null; - } - } - - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorImpl.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorImpl.java deleted file mode 100644 index e194b280..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorImpl.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.gitee.sop.servercommon.message; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class ServiceErrorImpl implements ServiceError { - private String sub_code; - private String sub_msg; - - public ServiceErrorImpl() { - } - - public ServiceErrorImpl(String sub_code, String sub_msg) { - this.sub_code = sub_code; - this.sub_msg = sub_msg; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorMeta.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorMeta.java deleted file mode 100644 index e851c5f2..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorMeta.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.sop.servercommon.message; - -import com.gitee.sop.servercommon.bean.ServiceContext; -import com.gitee.sop.servercommon.exception.ServiceException; -import lombok.Getter; - -/** - * 错误对象 - * - * @author 六如 - */ -@Getter -public class ServiceErrorMeta { - - private String modulePrefix; - private String subCode; - - public ServiceErrorMeta(String modulePrefix, String subCode) { - this.modulePrefix = modulePrefix; - this.subCode = subCode; - } - - public ServiceError getError() { - return ServiceErrorFactory.getError(this, ServiceContext.getCurrentContext().getLocale()); - } - - /** - * 返回网关exception - * - * @param params 参数 - * @return 返回exception - */ - public ServiceException getException(Object... params) { - ServiceError error = ServiceErrorFactory.getError(this, ServiceContext.getCurrentContext().getLocale(), params); - return new ServiceException(error); - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/AbstractValidationAnnotationBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/AbstractValidationAnnotationBuilder.java deleted file mode 100644 index 68409966..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/AbstractValidationAnnotationBuilder.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import org.springframework.core.annotation.AnnotationUtils; - -import java.lang.annotation.Annotation; -import java.util.Map; -import java.util.Set; - -/** - * @author 六如 - */ -public abstract class AbstractValidationAnnotationBuilder implements ValidationAnnotationBuilder { - @Override - public ValidationAnnotationDefinition build(T jsr303Annotation) { - ValidationAnnotationDefinition validationAnnotationDefinition = new ValidationAnnotationDefinition(); - - validationAnnotationDefinition.setAnnotationClass(jsr303Annotation.annotationType()); - Map properties = AnnotationUtils.getAnnotationAttributes(jsr303Annotation); - properties = this.formatProperties(properties); - validationAnnotationDefinition.setProperties(properties); - return validationAnnotationDefinition; - } - - protected Map formatProperties(Map properties) { - Set> entrySet = properties.entrySet(); - for (Map.Entry entry : entrySet) { - Object value = entry.getValue(); - if (value.getClass().isArray()) { - Object[] arr = (Object[])value; - if (arr.length == 0) { - properties.put(entry.getKey(), null); - } - } - } - return properties; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java deleted file mode 100644 index 1b879ddc..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java +++ /dev/null @@ -1,274 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.gitee.sop.servercommon.annotation.Open; -import com.gitee.sop.servercommon.bean.OpenContextImpl; -import com.gitee.sop.servercommon.bean.ParamNames; -import com.gitee.sop.servercommon.bean.ServiceContext; -import com.gitee.sop.servercommon.util.OpenUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.MethodParameter; -import org.springframework.http.HttpMethod; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.context.request.ServletWebRequest; -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.method.support.ModelAndViewContainer; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; -import org.springframework.web.multipart.MultipartRequest; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; -import org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.security.Principal; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 解析request参数中的业务参数,绑定到方法参数上 - * - * @author 六如 - */ -@Slf4j -public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver { - - private static final String HAS_INIT_OPEN_CONTEXT = "hasInitOpenContext"; - private final Map argumentResolverCache = new ConcurrentHashMap<>(256); - - private static List NEED_INIT_OPEN_CONTEXT = new ArrayList<>(16); - - private ParamValidator paramValidator = new ServiceParamValidator(); - - private static Class pushBuilder; - - private RequestMappingHandlerAdapter requestMappingHandlerAdapter; - - static { - try { - pushBuilder = ClassUtils.forName("javax.servlet.http.PushBuilder", - ServletRequestMethodArgumentResolver.class.getClassLoader()); - } catch (ClassNotFoundException ex) { - // Servlet 4.0 PushBuilder not found - not supported for injection - pushBuilder = null; - } - } - - @Override - public void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) { - List argumentResolversNew = new ArrayList<>(64); - // 先加自己,确保在第一个位置 - argumentResolversNew.add(this); - argumentResolversNew.addAll(requestMappingHandlerAdapter.getArgumentResolvers()); - requestMappingHandlerAdapter.setArgumentResolvers(argumentResolversNew); - this.requestMappingHandlerAdapter = requestMappingHandlerAdapter; - } - - @Override - public boolean supportsParameter(MethodParameter methodParameter) { - Open open = methodParameter.getMethodAnnotation(Open.class); - if (open == null) { - return false; - } - Class paramType = methodParameter.getParameterType(); - // 特殊参数 - boolean special = ( - WebRequest.class.isAssignableFrom(paramType) || - ServletRequest.class.isAssignableFrom(paramType) || - MultipartRequest.class.isAssignableFrom(paramType) || - HttpSession.class.isAssignableFrom(paramType) || - (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) || - Principal.class.isAssignableFrom(paramType) || - InputStream.class.isAssignableFrom(paramType) || - Reader.class.isAssignableFrom(paramType) || - HttpMethod.class == paramType || - Locale.class == paramType || - TimeZone.class == paramType || - ZoneId.class == paramType || - ServletResponse.class.isAssignableFrom(paramType) || - OutputStream.class.isAssignableFrom(paramType) || - Writer.class.isAssignableFrom(paramType) - ); - // 特殊参数只需要初始化OpenContext - if (special) { - NEED_INIT_OPEN_CONTEXT.add(methodParameter); - } - return true; - } - - - private boolean isOnlyInitOpenContext(MethodParameter methodParameter) { - return NEED_INIT_OPEN_CONTEXT.contains(methodParameter); - } - - @Override - public Object resolveArgument( - MethodParameter methodParameter - , ModelAndViewContainer modelAndViewContainer - , NativeWebRequest nativeWebRequest - , WebDataBinderFactory webDataBinderFactory - ) throws Exception { - // 特殊参数只需要初始化OpenContext - if (isOnlyInitOpenContext(methodParameter)) { - this.initOpenContextImpl(nativeWebRequest); - HandlerMethodArgumentResolver resolver = getOtherArgumentResolver(methodParameter); - if (resolver != null) { - return resolver.resolveArgument( - methodParameter - , modelAndViewContainer - , nativeWebRequest - , webDataBinderFactory - ); - } - return null; - } - nativeWebRequest = new SopServletWebRequest( - (HttpServletRequest) nativeWebRequest.getNativeRequest(), - (HttpServletResponse) nativeWebRequest.getNativeResponse() - ); - Object paramObj = this.getParamObject(methodParameter, nativeWebRequest); - if (paramObj != null) { - // JSR-303验证 - paramValidator.validateBizParam(paramObj); - return paramObj; - } - HandlerMethodArgumentResolver resolver = getOtherArgumentResolver(methodParameter); - if (resolver != null) { - return resolver.resolveArgument( - methodParameter - , modelAndViewContainer - , nativeWebRequest - , webDataBinderFactory - ); - } - return null; - } - - private OpenContextImpl initOpenContextImpl(NativeWebRequest nativeWebRequest) { - HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest(); - Object hasInit = request.getAttribute(HAS_INIT_OPEN_CONTEXT); - if (hasInit != null) { - return (OpenContextImpl) ServiceContext.getCurrentContext().getOpenContext(); - } - ServiceContext currentContext = ServiceContext.getCurrentContext(); - JSONObject requestParams = OpenUtil.getRequestParams(request); - OpenContextImpl openContext = new OpenContextImpl(requestParams); - currentContext.setOpenContext(openContext); - request.setAttribute(HAS_INIT_OPEN_CONTEXT, true); - return openContext; - } - - /** - * 获取参数对象,将request中的参数绑定到实体对象中去 - * - * @param methodParameter 方法参数 - * @param nativeWebRequest request - * @return 返回参数绑定的对象,没有返回null - */ - protected Object getParamObject(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) { - HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest(); - ServiceContext currentContext = ServiceContext.getCurrentContext(); - OpenContextImpl openContext = (OpenContextImpl)currentContext.getOpenContext(); - if (openContext == null) { - openContext = initOpenContextImpl(nativeWebRequest); - } - Map requestParams = openContext.getParameterMap(); - Object bizObj = requestParams.get(ParamNames.BIZ_CONTENT_NAME); - String bizContent = bizObj == null ? null : bizObj.toString(); - if (StringUtils.isEmpty(bizContent)) { - return null; - } - // 方法参数类型 - Class parameterType = methodParameter.getParameterType(); - Object param; - try { - param = JSON.parseObject(bizContent, parameterType); - } catch (Exception e) { - log.error("非json参数, \nbiz_content:{}\nrequestParams:{}", bizContent, requestParams, e); - // 否则认为是 aa=1&bb=33 形式 - Map query = OpenUtil.parseQueryToMap(bizContent); - param = new JSONObject(query).toJavaObject(parameterType); - } - openContext.setBizObject(param); - this.bindUploadFile(param, request); - return param; - } - - - /** - * 将上传文件对象绑定到属性中 - * - * @param param 业务参数 - * @param httpServletRequest - */ - protected void bindUploadFile(Object param, HttpServletRequest httpServletRequest) { - if (httpServletRequest instanceof MyServletRequestWrapper) { - httpServletRequest = (HttpServletRequest)((MyServletRequestWrapper) httpServletRequest).getRequest(); - } - if (param == null) { - return; - } - if (OpenUtil.isMultipart(httpServletRequest)) { - MultipartHttpServletRequest request = (MultipartHttpServletRequest) httpServletRequest; - Class bizClass = param.getClass(); - ReflectionUtils.doWithFields(bizClass, field -> { - ReflectionUtils.makeAccessible(field); - String name = field.getName(); - MultipartFile multipartFile = request.getFile(name); - ReflectionUtils.setField(field, param, multipartFile); - }, field -> field.getType() == MultipartFile.class); - } - } - - /** - * 获取其它的参数解析器 - * - * @param parameter 业务参数 - * @return 返回合适参数解析器,没有返回null - */ - protected HandlerMethodArgumentResolver getOtherArgumentResolver(MethodParameter parameter) { - HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); - if (result == null) { - List argumentResolvers = this.requestMappingHandlerAdapter.getArgumentResolvers(); - for (HandlerMethodArgumentResolver methodArgumentResolver : argumentResolvers) { - if (methodArgumentResolver instanceof SopHandlerMethodArgumentResolver) { - continue; - } - if (methodArgumentResolver.supportsParameter(parameter)) { - result = methodArgumentResolver; - this.argumentResolverCache.put(parameter, result); - break; - } - } - } - return result; - } - - public void setParamValidator(ParamValidator paramValidator) { - this.paramValidator = paramValidator; - } - - private static final class SopServletWebRequest extends ServletWebRequest { - public SopServletWebRequest(HttpServletRequest request, HttpServletResponse response) { - super(new MyServletRequestWrapper(request), response); - } - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/BaseValidationAnnotationBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/BaseValidationAnnotationBuilder.java deleted file mode 100644 index b70b15a3..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/BaseValidationAnnotationBuilder.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import com.gitee.sop.servercommon.util.ReflectionUtil; - -import java.lang.annotation.Annotation; - -/** - * @author 六如 - */ -public abstract class BaseValidationAnnotationBuilder extends AbstractValidationAnnotationBuilder { - public BaseValidationAnnotationBuilder() { - Class clazz = ReflectionUtil.getSuperClassGenricType(getClass(), 0); - ValidationAnnotationDefinitionFactory.addBuilder(clazz, this); - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ByteArrayStreamWrapper.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ByteArrayStreamWrapper.java deleted file mode 100644 index d422aa13..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ByteArrayStreamWrapper.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import java.io.IOException; - -/** - * byte数组流包装 - * - * @author 六如 - */ -public class ByteArrayStreamWrapper extends ServletInputStream { - - private byte[] data; - private int idx = 0; - - /** - * Creates a new ByteArrayStreamWrapper instance. - * - * @param data a byte[] value - */ - public ByteArrayStreamWrapper(byte[] data) { - if (data == null) { - data = new byte[0]; - } - this.data = data; - } - - @Override - public int read() throws IOException { - if (idx == data.length) { - return -1; - } - // I have to AND the byte with 0xff in order to ensure that it is returned as an unsigned integer - // the lack of this was causing a weird bug when manually unzipping gzipped request bodies - return data[idx++] & 0xff; - } - - @Override - public boolean isFinished() { - return true; - } - - @Override - public boolean isReady() { - return true; - } - - @Override - public void setReadListener(ReadListener readListener) { - - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/MyServletRequestWrapper.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/MyServletRequestWrapper.java deleted file mode 100644 index 25539ac6..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/MyServletRequestWrapper.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; - -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.Map; - -/** - * 包装HttpServletRequest - */ -@Slf4j -public class MyServletRequestWrapper extends HttpServletRequestWrapper { - - private final byte[] body; - private final Map parameterMap; - - public MyServletRequestWrapper(HttpServletRequest request) { - super(request); - // 先缓存请求参数,不然获取不到,原因: - // 调用getInputStream方法时,usingInputStream会变成true - // 之后再调用request.getParameterMap(),就一直返回空map - // 因此要先调用request.getParameterMap()缓存起来 - // 详见org.apache.catalina.connector.Request.parseParameters()中 - // if (usingInputStream || usingReader) { - // success = true; - // return; - // } - parameterMap = request.getParameterMap(); - try { - body = IOUtils.toByteArray(super.getInputStream()); - } catch (IOException e) { - log.error("cache body error, url:{}",request.getRequestURI(), e); - throw new RuntimeException("cache body error", e); - } - } - - @Override - public Map getParameterMap() { - return parameterMap; - } - - @Override - public BufferedReader getReader() throws IOException { - return new BufferedReader(new InputStreamReader(getInputStream())); - } - - @Override - public ServletInputStream getInputStream() throws IOException { - return new ByteArrayStreamWrapper(body); - } - - /** - * byte数组流包装 - * - * @author 六如 - */ - public static class ByteArrayStreamWrapper extends ServletInputStream { - - private byte[] data; - private int idx = 0; - - /** - * Creates a new ByteArrayStreamWrapper instance. - * - * @param data a byte[] value - */ - public ByteArrayStreamWrapper(byte[] data) { - if (data == null) { - data = new byte[0]; - } - this.data = data; - } - - @Override - public int read() throws IOException { - if (idx == data.length) { - return -1; - } - // I have to AND the byte with 0xff in order to ensure that it is returned as an unsigned integer - // the lack of this was causing a weird bug when manually unzipping gzipped request bodies - return data[idx++] & 0xff; - } - - @Override - public boolean isFinished() { - return true; - } - - @Override - public boolean isReady() { - return true; - } - - @Override - public void setReadListener(ReadListener readListener) { - - } - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ParamValidator.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ParamValidator.java deleted file mode 100644 index e03ec01d..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ParamValidator.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gitee.sop.servercommon.param; - -/** - * @author 六如 - */ -public interface ParamValidator { - /** - * 验证业务参数 - * @param obj - */ - void validateBizParam(Object obj); -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java deleted file mode 100644 index 88dac354..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import com.gitee.sop.servercommon.bean.ServiceContext; -import com.gitee.sop.servercommon.exception.ServiceException; -import com.gitee.sop.servercommon.message.ServiceErrorEnum; -import com.gitee.sop.servercommon.message.ServiceErrorFactory; -import com.gitee.sop.servercommon.param.validation.ValidationGroupSequence; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ReflectionUtils; - -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.ValidatorFactory; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * 参数验证,JSR-303 - * @author 六如 - */ -public class ServiceParamValidator implements ParamValidator { - private static final String LEFT_TOKEN = "{"; - private static final String RIGHT_TOKEN = "}"; - private static final String EQ = "="; - private static final String COMMA = ","; - private static Object[] EMPTY_OBJ_ARRAY = {}; - - private static final List SYSTEM_PACKAGE_LIST = Arrays.asList( - "java.lang", "java.math", "java.util", "sun.util" - ,"org.springframework", "javax.servlet" - ); - - private static javax.validation.Validator validator; - - static { - ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); - validator = factory.getValidator(); - } - - @Override - public void validateBizParam(Object obj) { - if (obj == null) { - return; - } - // 先校验属性对象 - List fields = listObjectField(obj); - if (!fields.isEmpty()) { - fields.forEach(this::validateBizParam); - } - Set> set = validator.validate(obj, ValidationGroupSequence.class); - if (!CollectionUtils.isEmpty(set)) { - ConstraintViolation oneError = set.iterator().next(); - String errorMsg = oneError.getMessage(); - throw this.getValidateBizParamException(errorMsg); - } - } - - private List listObjectField(Object object) { - List ret = new ArrayList<>(); - ReflectionUtils.doWithFields(object.getClass(), field -> { - ReflectionUtils.makeAccessible(field); - ret.add(field.get(object)); - }, this::isMatchField); - return ret; - } - - /** - * 匹配校验字段。 - * - * 1. 不为基本类型; - * 2. 不为java自带的类型; - * 3. 不为枚举 - * 4. 不为Map - * @param field field - * @return true,是自定义的 - */ - private boolean isMatchField(Field field) { - Class fieldType = field.getType(); - if (fieldType.isPrimitive()) { - return false; - } - if (Map.class.isAssignableFrom(fieldType)) { - return false; - } - Class declaringClass = field.getDeclaringClass(); - boolean isSame = declaringClass == fieldType; - boolean isAssignableFrom = declaringClass.isAssignableFrom(fieldType) - || fieldType.isAssignableFrom(declaringClass); - // 如果是相同类,或者有继承关系不校验。 - if (isSame || isAssignableFrom) { - return false; - } - Package aPackage = fieldType.getPackage(); - if (aPackage == null) { - return false; - } - String packageName = aPackage.getName(); - for (String prefix : SYSTEM_PACKAGE_LIST) { - if (packageName.startsWith(prefix)) { - return false; - } - } - return true; - } - - private RuntimeException getValidateBizParamException(String errorMsg) { - String subCode = ServiceErrorEnum.ISV_PARAM_ERROR.getErrorMeta().getSubCode(); - String[] msgToken = errorMsg.split(EQ); - String msg = msgToken[0]; - if (msg.startsWith(LEFT_TOKEN) && msg.endsWith(RIGHT_TOKEN)) { - String module = msg.substring(1, msg.length() - 1); - Object[] params = this.buildParams(msgToken); - String error = ServiceErrorFactory.getErrorMessage(module, ServiceContext.getCurrentContext().getLocale(), params); - return new ServiceException(subCode, error); - } else { - return new ServiceException(subCode, errorMsg); - } - } - - private Object[] buildParams(String[] msgToken) { - if (msgToken.length == 2) { - return msgToken[1].split(COMMA); - } else { - return EMPTY_OBJ_ARRAY; - } - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SopHandlerMethodArgumentResolver.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SopHandlerMethodArgumentResolver.java deleted file mode 100644 index 96e07eb1..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SopHandlerMethodArgumentResolver.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; - -/** - * @author 六如 - */ -public interface SopHandlerMethodArgumentResolver extends HandlerMethodArgumentResolver { - - /** - * 设置requestMappingHandlerAdapter - * - * @param requestMappingHandlerAdapter requestMappingHandlerAdapter - */ - void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter); -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationBuilder.java deleted file mode 100644 index 345e0dee..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationBuilder.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import java.lang.annotation.Annotation; - -/** - * @author 六如 - */ -public interface ValidationAnnotationBuilder { - /** - * 构建验证注解 - * - * @param jsr303Annotation JSR-303注解 - * @return 返回注解定义 - */ - ValidationAnnotationDefinition build(T jsr303Annotation); -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinition.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinition.java deleted file mode 100644 index d82cc659..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinition.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import java.util.Map; - -/** - * @author 六如 - */ -public class ValidationAnnotationDefinition { - private Class annotationClass; - private Map properties; - - public Class getAnnotationClass() { - return annotationClass; - } - - public void setAnnotationClass(Class annotationClass) { - this.annotationClass = annotationClass; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinitionFactory.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinitionFactory.java deleted file mode 100644 index 02393e7e..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinitionFactory.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.gitee.sop.servercommon.param; - -import io.swagger.annotations.ApiModelProperty; -import org.hibernate.validator.constraints.CodePointLength; -import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.CreditCardNumber; -import org.hibernate.validator.constraints.Currency; -import org.hibernate.validator.constraints.EAN; -import org.hibernate.validator.constraints.ISBN; -import org.hibernate.validator.constraints.Length; -import org.hibernate.validator.constraints.LuhnCheck; -import org.hibernate.validator.constraints.Mod10Check; -import org.hibernate.validator.constraints.Mod11Check; -import org.hibernate.validator.constraints.ParameterScriptAssert; -import org.hibernate.validator.constraints.Range; -import org.hibernate.validator.constraints.SafeHtml; -import org.hibernate.validator.constraints.ScriptAssert; -import org.hibernate.validator.constraints.URL; -import org.hibernate.validator.constraints.UniqueElements; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.validation.constraints.AssertFalse; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.DecimalMax; -import javax.validation.constraints.DecimalMin; -import javax.validation.constraints.Digits; -import javax.validation.constraints.Email; -import javax.validation.constraints.Future; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.Negative; -import javax.validation.constraints.NegativeOrZero; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Null; -import javax.validation.constraints.Past; -import javax.validation.constraints.PastOrPresent; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; -import javax.validation.constraints.Size; -import java.lang.annotation.Annotation; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author 六如 - */ -public class ValidationAnnotationDefinitionFactory { - - private static final Map, ValidationAnnotationBuilder> store = new HashMap<>(64); - - private static final List> excludeList = Arrays.asList(RequestParam.class, PathVariable.class); - - static { - new BaseValidationAnnotationBuilder(){}; - - // validation-api-2.0.1.Final.jar下javax.validation.constraints - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - - // hibernate-validator-6.0.10.Final.jar下org.hibernate.validator.constraints - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - new BaseValidationAnnotationBuilder(){}; - } - - /** - * 添加注解对应的构建器 - * - * @param annoClass - * @param builder - */ - public static void addBuilder(Class annoClass, ValidationAnnotationBuilder builder) { - store.put(annoClass, builder); - } - - public static ValidationAnnotationDefinition build(Annotation annotation) { - Class jsr303Anno = annotation.annotationType(); - if (excludeList.contains(jsr303Anno)) { - return null; - } - ValidationAnnotationBuilder validationAnnotationBuilder = store.get(jsr303Anno); - if (validationAnnotationBuilder == null) { - return null; - } - return validationAnnotationBuilder.build(annotation); - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group1.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group1.java deleted file mode 100644 index 72724c55..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group1.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gitee.sop.servercommon.param.validation; - -/** - * @author 六如 - */ -public interface Group1 { -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group2.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group2.java deleted file mode 100644 index 3562359c..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group2.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gitee.sop.servercommon.param.validation; - -/** - * @author 六如 - */ -public interface Group2 { -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group3.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group3.java deleted file mode 100644 index 66ee1871..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group3.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gitee.sop.servercommon.param.validation; - -/** - * @author 六如 - */ -public interface Group3 { -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group4.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group4.java deleted file mode 100644 index 6de23f6e..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group4.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gitee.sop.servercommon.param.validation; - -/** - * @author 六如 - */ -public interface Group4 { -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group5.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group5.java deleted file mode 100644 index 80cf2ecc..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/Group5.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gitee.sop.servercommon.param.validation; - -/** - * @author 六如 - */ -public interface Group5 { -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/ValidationGroupSequence.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/ValidationGroupSequence.java deleted file mode 100644 index 555e6b4e..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/validation/ValidationGroupSequence.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.gitee.sop.servercommon.param.validation; - -import javax.validation.GroupSequence; - -/** - * @author 六如 - */ -@GroupSequence({ - // 默认的必须加上,不然没有指定groups的注解不会生效 - javax.validation.groups.Default.class, - Group1.class, - Group2.class, - Group3.class, - Group4.class, - Group5.class, - -}) -public interface ValidationGroupSequence { -} - diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java deleted file mode 100644 index ff800cef..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.gitee.sop.servercommon.result; - -import com.alibaba.fastjson.annotation.JSONField; -import com.gitee.sop.servercommon.exception.ServiceException; -import com.gitee.sop.servercommon.message.ServiceError; -import com.gitee.sop.servercommon.message.ServiceErrorEnum; -import com.gitee.sop.servercommon.message.ServiceErrorMeta; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * 处理业务返回结果 - * - * @author 六如 - */ -@Slf4j -public class DefaultServiceResultBuilder implements ServiceResultBuilder { - - @Override - public Object buildError(HttpServletRequest request, HttpServletResponse response, Throwable throwable) { - ServiceError error; - if (throwable instanceof ServiceException) { - ServiceException serviceException = (ServiceException) throwable; - error = serviceException.getError(); - } else { - ServiceErrorMeta errorMeta = ServiceErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta(); - error = errorMeta.getError(); - } - return this.buildError(error.getSub_code(), error.getSub_msg()); - } - - @Override - public Object buildError(String subCode, String subMsg) { - AlipayResult result = new AlipayResult(); - result.setSub_code(subCode); - result.setSub_msg(subMsg); - return result; - } - - @Data - public static class AlipayResult { - @JSONField(ordinal = 1) - private String sub_code; - @JSONField(ordinal = 2) - private String sub_msg; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/ServiceResultBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/ServiceResultBuilder.java deleted file mode 100644 index e9da6f65..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/ServiceResultBuilder.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gitee.sop.servercommon.result; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author 六如 - */ -public interface ServiceResultBuilder { - - /** - * 构建错误返回结果 - * @param request - * @param response - * @param throwable - * @return 返回最终结果 - */ - Object buildError(HttpServletRequest request, HttpServletResponse response, Throwable throwable); - - - /** - * 构建错误返回结果 - * @param subCode - * @param subMsg - * @return 返回最终结果 - */ - Object buildError(String subCode, String subMsg); - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ApiMetaBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ApiMetaBuilder.java deleted file mode 100644 index a171d8f0..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ApiMetaBuilder.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.gitee.sop.servercommon.route; - -import com.gitee.sop.servercommon.annotation.Open; -import com.gitee.sop.servercommon.bean.ServiceApiInfo; -import com.gitee.sop.servercommon.bean.ServiceConfig; -import org.apache.commons.lang3.BooleanUtils; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author 六如 - */ -public class ApiMetaBuilder { - - /** - * 接口名规则:允许字母、数字、点、下划线 - */ - private static final String REGEX_API_NAME = "^[a-zA-Z0-9\\.\\_\\-]+$"; - - public ServiceApiInfo getServiceApiInfo(String serviceId, RequestMappingHandlerMapping requestMappingHandlerMapping) { - if (serviceId == null) { - throw new IllegalArgumentException("请在application.properties中指定spring.application.name属性"); - } - List apis = this.buildApiMetaList(requestMappingHandlerMapping); - // 排序 - apis.sort(Comparator.comparing(ServiceApiInfo.ApiMeta::fetchNameVersion)); - - ServiceApiInfo serviceApiInfo = new ServiceApiInfo(); - serviceApiInfo.setServiceId(serviceId); - serviceApiInfo.setApis(apis); - - return serviceApiInfo; - } - - protected List buildApiMetaList(RequestMappingHandlerMapping requestMappingHandlerMapping) { - Map handlerMethods = requestMappingHandlerMapping.getHandlerMethods(); - Set requestMappingInfos = handlerMethods.keySet(); - List store = new ArrayList<>(); - List apis = new ArrayList<>(requestMappingInfos.size()); - - for (Map.Entry handlerMethodEntry : handlerMethods.entrySet()) { - ServiceApiInfo.ApiMeta apiMeta = this.buildApiMeta(handlerMethodEntry); - if (apiMeta == null) { - continue; - } - String key = apiMeta.fetchNameVersion(); - if (store.contains(key)) { - throw new IllegalArgumentException("重复申明接口,请检查path和version,path:" + apiMeta.getPath() + ", version:" + apiMeta.getVersion()); - } else { - store.add(key); - } - apis.add(apiMeta); - } - return apis; - } - - protected ServiceApiInfo.ApiMeta buildApiMeta(Map.Entry handlerMethodEntry) { - RequestMappingInfo requestMappingInfo = handlerMethodEntry.getKey(); - Set patterns = requestMappingInfo.getPatternsCondition().getPatterns(); - Open open = handlerMethodEntry.getValue().getMethodAnnotation(Open.class); - ServiceApiInfo.ApiMeta apiMeta = null; - if (open != null) { - String name = open.value(); - String version = open.version(); - if ("".equals(version)) { - version = ServiceConfig.getInstance().getDefaultVersion(); - } - // 方法完整的path,如: /goods/listGoods,/users/user/get - String path = patterns.iterator().next(); - this.checkApiName(name); - apiMeta = new ServiceApiInfo.ApiMeta(name, path, version); - apiMeta.setIgnoreValidate(BooleanUtils.toInteger(open.ignoreValidate())); - apiMeta.setMergeResult(BooleanUtils.toInteger(open.mergeResult())); - apiMeta.setPermission(BooleanUtils.toInteger(open.permission())); - apiMeta.setNeedToken(BooleanUtils.toInteger(open.needToken())); - } - return apiMeta; - } - - - protected void checkApiName(String name) { - if (!name.matches(REGEX_API_NAME)) { - throw new IllegalArgumentException("接口名称只允许【字母、数字、点(.)、下划线(_)、减号(-)】,错误接口:" + name); - } - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/GatewayFilterDefinition.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/GatewayFilterDefinition.java deleted file mode 100644 index e5e5801d..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/GatewayFilterDefinition.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.servercommon.route; - -import lombok.Data; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * @author 六如 - */ -@Data -public class GatewayFilterDefinition { - /** Filter Name */ - private String name; - /** 对应的路由规则 */ - private Map args = new LinkedHashMap<>(); -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/GatewayPredicateDefinition.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/GatewayPredicateDefinition.java deleted file mode 100644 index 577606df..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/GatewayPredicateDefinition.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gitee.sop.servercommon.route; - -import lombok.Data; -import org.springframework.util.StringUtils; - -import javax.validation.ValidationException; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * @author 六如 - */ -@Data -public class GatewayPredicateDefinition { - public static final String GEN_KEY = "_genkey_"; - /** 断言对应的Name */ - private String name; - /** 配置的断言规则 */ - private Map args = new LinkedHashMap<>(); - - public GatewayPredicateDefinition() { - } - - public GatewayPredicateDefinition(String text) { - int eqIdx = text.indexOf(61); - if (eqIdx <= 0) { - throw new ValidationException("Unable to parse GatewayPredicateDefinition text '" + text + "', must be of the form name=value"); - } else { - this.setName(text.substring(0, eqIdx)); - String[] params = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ","); - - for(int i = 0; i < params.length; ++i) { - this.args.put(generateName(i), params[i]); - } - - } - } - - public static String generateName(int i) { - return GEN_KEY + i; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/RouteDefinition.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/RouteDefinition.java deleted file mode 100644 index f7d72977..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/RouteDefinition.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.gitee.sop.servercommon.route; - -import lombok.Data; - -import java.util.List; - -/** - * @author 六如 - */ -@Data -public class RouteDefinition { - /** - * 路由的Id(接口名+版本号),确保此id全局唯一 - */ - private String id; - - /** - * 接口名 - */ - private String name; - - /** - * 版本号 - */ - private String version; - - /** - * 路由断言集合配置 - */ - private List predicates; - - /** - * 路由过滤器集合配置 - */ - private List filters; - - /** - * 路由规则转发的目标uri - */ - private String uri; - - /** - * uri后面跟的path - */ - private String path; - - /** - * 路由执行的顺序 - */ - private int order = 0; - - /** - * 是否忽略验证,业务参数验证除外 - */ - private int ignoreValidate; - - /** - * 状态,0:待审核,1:启用,2:禁用 - */ - private int status = 1; - - /** - * 是否合并结果 - */ - private int mergeResult; - - /** - * 是否需要授权才能访问 - */ - private int permission; - - /** - * 是否需要token - */ - private int needToken; - - /** - * 是否是兼容模式,即使用了@ApiAbility注解 - */ - private int compatibleMode; -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteController.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteController.java deleted file mode 100644 index 1c5e3aea..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteController.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.gitee.sop.servercommon.route; - -import com.gitee.sop.servercommon.bean.ServiceApiInfo; -import com.gitee.sop.servercommon.util.OpenUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author 六如 - */ -@Slf4j -@RestController -public class ServiceRouteController { - - private static final String SECRET = "a3d9sf!1@odl90zd>fkASwq"; - - private static final ApiMetaBuilder apiMetaBuilder = new ApiMetaBuilder(); - - @Autowired - private RequestMappingHandlerMapping requestMappingHandlerMapping; - - @Autowired - private Environment environment; - - - @RequestMapping("/sop/routes") - public ServiceRouteInfo listRoutes(HttpServletRequest request, HttpServletResponse response) { - if (!OpenUtil.validateSimpleSign(request, SECRET)) { - log.error("签名验证失败, params:{}", request.getQueryString()); - return null; - } - return getServiceRouteInfo(request, response); - } - - protected ServiceRouteInfo getServiceRouteInfo(HttpServletRequest request, HttpServletResponse response) { - String serviceId = environment.getProperty("spring.application.name"); - if (serviceId == null) { - throw new IllegalArgumentException("未指定spring.application.name参数"); - } - ServiceApiInfo serviceApiInfo = apiMetaBuilder.getServiceApiInfo(serviceId, requestMappingHandlerMapping); - ServiceRouteInfoBuilder serviceRouteInfoBuilder = new ServiceRouteInfoBuilder(environment); - return serviceRouteInfoBuilder.build(serviceApiInfo); - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java deleted file mode 100644 index cf93c582..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.sop.servercommon.route; - -import lombok.Data; - -import java.util.Date; -import java.util.List; - -/** - * @author 六如 - */ -@Data -public class ServiceRouteInfo { - - /** - * 服务名称,对应spring.application.name - */ - private String serviceId; - - private Date createTime = new Date(); - - private Date updateTime = new Date(); - - private String description; - - private List routeDefinitionList; - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfoBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfoBuilder.java deleted file mode 100644 index 3382f93d..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfoBuilder.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.gitee.sop.servercommon.route; - -import com.gitee.sop.servercommon.bean.ServiceApiInfo; -import com.gitee.sop.servercommon.route.RouteDefinition; -import com.gitee.sop.servercommon.route.ServiceRouteInfo; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.springframework.core.env.Environment; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author 六如 - */ -@Slf4j -public class ServiceRouteInfoBuilder { - - /** - * 网关对应的LoadBalance协议 - */ - private static final String PROTOCOL_LOAD_BALANCE = "lb://"; - - private static final String PATH_SPLIT = "/"; - - private static final String DEFAULT_CONTEXT_PATH = "/"; - - private final Environment environment; - - public ServiceRouteInfoBuilder(Environment environment) { - this.environment = environment; - } - - public ServiceRouteInfo build(ServiceApiInfo serviceApiInfo) { - return this.buildServiceGatewayInfo(serviceApiInfo); - } - - /** - * 构建接口信息,符合spring cloud gateway的格式 - * - * @param serviceApiInfo 服务接口信息 - * @return 返回服务路由信息 - */ - protected ServiceRouteInfo buildServiceGatewayInfo(ServiceApiInfo serviceApiInfo) { - List apis = serviceApiInfo.getApis(); - List routeDefinitionList = new ArrayList<>(apis.size()); - for (ServiceApiInfo.ApiMeta apiMeta : apis) { - RouteDefinition routeDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta); - routeDefinitionList.add(routeDefinition); - } - ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo(); - serviceRouteInfo.setServiceId(serviceApiInfo.getServiceId()); - serviceRouteInfo.setRouteDefinitionList(routeDefinitionList); - return serviceRouteInfo; - } - - - protected RouteDefinition buildGatewayRouteDefinition(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) { - RouteDefinition routeDefinition = new RouteDefinition(); - // 唯一id规则:接口名 + 版本号 - String routeId = apiMeta.fetchNameVersion(); - BeanUtils.copyProperties(apiMeta, routeDefinition); - routeDefinition.setId(routeId); - routeDefinition.setFilters(Collections.emptyList()); - routeDefinition.setPredicates(Collections.emptyList()); - String uri = this.buildUri(serviceApiInfo, apiMeta); - String path = this.buildServletPath(serviceApiInfo, apiMeta); - routeDefinition.setUri(uri); - routeDefinition.setPath(path); - return routeDefinition; - } - - protected String buildUri(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) { - return PROTOCOL_LOAD_BALANCE + serviceApiInfo.getServiceId(); - } - - protected String buildServletPath(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) { - String contextPath = getContextPathCompatibility(); - String servletPath = apiMeta.getPath(); - if (servletPath == null) { - servletPath = ""; - } - if (!servletPath.startsWith(PATH_SPLIT)) { - servletPath = PATH_SPLIT + servletPath; - } - if (DEFAULT_CONTEXT_PATH.equals(contextPath)) { - return servletPath; - } else { - return contextPath + servletPath; - } - } - - /** - * 获取context-path,兼容老版本 - * @return 返回context-path,没有返回"/" - */ - private String getContextPathCompatibility() { - return environment.getProperty("server.servlet.context-path", - environment.getProperty("server.context-path", DEFAULT_CONTEXT_PATH) - ); - } - - private void checkPath(String path, String errorMsg) { - if (path.contains(PATH_SPLIT)) { - throw new IllegalArgumentException(errorMsg); - } - } - - public Environment getEnvironment() { - return environment; - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/CustomSwaggerParameterBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/CustomSwaggerParameterBuilder.java deleted file mode 100644 index 4e702d02..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/CustomSwaggerParameterBuilder.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.gitee.sop.servercommon.swagger; - -import com.google.common.base.Function; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; -import io.swagger.annotations.ApiModelProperty; -import io.swagger.annotations.ApiParam; -import org.hibernate.validator.constraints.Length; -import springfox.documentation.builders.ParameterBuilder; -import springfox.documentation.service.AllowableListValues; -import springfox.documentation.service.AllowableValues; -import springfox.documentation.service.StringVendorExtension; -import springfox.documentation.service.VendorExtension; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spi.schema.EnumTypeDeterminer; -import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin; -import springfox.documentation.spi.service.contexts.ParameterExpansionContext; -import springfox.documentation.spring.web.DescriptionResolver; -import springfox.documentation.swagger.common.SwaggerPluginSupport; -import springfox.documentation.swagger.readers.parameter.Examples; -import springfox.documentation.swagger.schema.ApiModelProperties; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER; - -/** - * Created by wujie on 2019/2/16. - * 自定义ExpandedParameterBuilderPlugin,主要是修正源码query传入请求参数postion无效 - * 这里,将postion赋值给order - * - * https://blog.csdn.net/qq_38316721/article/details/103908793 - */ -public class CustomSwaggerParameterBuilder implements ExpandedParameterBuilderPlugin { - - private final DescriptionResolver descriptions; - private final EnumTypeDeterminer enumTypeDeterminer; - - public CustomSwaggerParameterBuilder( - DescriptionResolver descriptions, - EnumTypeDeterminer enumTypeDeterminer) { - this.descriptions = descriptions; - this.enumTypeDeterminer = enumTypeDeterminer; - } - - @Override - public void apply(ParameterExpansionContext context) { - Optional apiModelPropertyOptional = context.findAnnotation(ApiModelProperty.class); - if (apiModelPropertyOptional.isPresent()) { - fromApiModelProperty(context, apiModelPropertyOptional.get()); - } - Optional apiParamOptional = context.findAnnotation(ApiParam.class); - if (apiParamOptional.isPresent()) { - fromApiParam(context, apiParamOptional.get()); - } - } - - @Override - public boolean supports(DocumentationType delimiter) { - return SwaggerPluginSupport.pluginDoesApply(delimiter); - } - - private void fromApiParam(ParameterExpansionContext context, ApiParam apiParam) { - String allowableProperty = Strings.emptyToNull(apiParam.allowableValues()); - AllowableValues allowable = allowableValues( - Optional.ofNullable(allowableProperty), - context.getFieldType().getErasedType()); - - maybeSetParameterName(context, apiParam.name()) - .description(descriptions.resolve(apiParam.value())) - .defaultValue(apiParam.defaultValue()) - .required(apiParam.required()) - .allowMultiple(apiParam.allowMultiple()) - .allowableValues(allowable) - .parameterAccess(apiParam.access()) - .hidden(apiParam.hidden()) - .scalarExample(apiParam.example()) - .complexExamples(Examples.examples(apiParam.examples())) - .order(SWAGGER_PLUGIN_ORDER) - // 添加额外属性 - .vendorExtensions(this.getVendorExtension(context)) - .build(); - } - - private void fromApiModelProperty(ParameterExpansionContext context, ApiModelProperty apiModelProperty) { - String allowableProperty = Strings.emptyToNull(apiModelProperty.allowableValues()); - AllowableValues allowable = allowableValues( - Optional.ofNullable(allowableProperty), - context.getFieldType().getErasedType()); - - maybeSetParameterName(context, apiModelProperty.name()) - .description(descriptions.resolve(apiModelProperty.value())) - .required(apiModelProperty.required()) - .allowableValues(allowable) - .parameterAccess(apiModelProperty.access()) - .hidden(apiModelProperty.hidden()) - .scalarExample(apiModelProperty.example()) - //源码这里是: SWAGGER_PLUGIN_ORDER,需要修正 - .order(apiModelProperty.position()) - // 添加额外属性 - .vendorExtensions(this.getVendorExtension(context)) - .build(); - } - - private List getVendorExtension(ParameterExpansionContext context) { - List vendorExtensions = new ArrayList<>(4); - Optional annotation = context.findAnnotation(Length.class); - if (annotation.isPresent()) { - Length length = annotation.get(); - vendorExtensions.add(new StringVendorExtension("maxLength", String.valueOf(length.max()))); - vendorExtensions.add(new StringVendorExtension("minLength", String.valueOf(length.min()))); - } - return vendorExtensions; - } - - private ParameterBuilder maybeSetParameterName(ParameterExpansionContext context, String parameterName) { - if (!Strings.isNullOrEmpty(parameterName)) { - context.getParameterBuilder().name(parameterName); - } - return context.getParameterBuilder(); - } - - private AllowableValues allowableValues(final Optional optionalAllowable, Class fieldType) { - - AllowableValues allowable = null; - if (enumTypeDeterminer.isEnum(fieldType)) { - allowable = new AllowableListValues(getEnumValues(fieldType), "LIST"); - } else if (optionalAllowable.isPresent()) { - allowable = ApiModelProperties.allowableValueFromString(optionalAllowable.get()); - } - return allowable; - } - - private List getEnumValues(final Class subject) { - return Lists.transform(Arrays.asList(subject.getEnumConstants()), new Function() { - @Override - public String apply(final Object input) { - return input.toString(); - } - }); - } -} - \ No newline at end of file diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/DocumentationPluginsManagerExt.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/DocumentationPluginsManagerExt.java deleted file mode 100644 index 7f9d4b73..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/DocumentationPluginsManagerExt.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.gitee.sop.servercommon.swagger; - -import com.alibaba.fastjson.JSON; -import com.fasterxml.classmate.ResolvedType; -import com.fasterxml.classmate.members.RawField; -import com.gitee.sop.servercommon.annotation.BizCode; -import com.gitee.sop.servercommon.annotation.Open; -import com.gitee.sop.servercommon.bean.ServiceConfig; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiModelProperty; -import io.swagger.annotations.ApiOperation; -import lombok.AllArgsConstructor; -import lombok.Data; -import org.hibernate.validator.constraints.Length; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.annotation.Order; -import springfox.documentation.service.Operation; -import springfox.documentation.service.StringVendorExtension; -import springfox.documentation.service.VendorExtension; -import springfox.documentation.spi.service.contexts.OperationContext; -import springfox.documentation.spring.web.plugins.DocumentationPluginsManager; - -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * @author 六如 - */ -public class DocumentationPluginsManagerExt extends DocumentationPluginsManager { - - private static final String SOP_NAME = "sop_name"; - private static final String SOP_VERSION = "sop_version"; - private static final String MODULE_ORDER = "module_order"; - private static final String API_ORDER = "api_order"; - private static final String BIZ_CODE = "biz_code"; - - @Override - public Operation operation(OperationContext operationContext) { - Operation operation = super.operation(operationContext); - this.setVendorExtension(operation, operationContext); - return operation; - } - - private void setVendorExtension(Operation operation, OperationContext operationContext) { - List vendorExtensions = operation.getVendorExtensions(); - Optional mappingOptional = operationContext.findAnnotation(Open.class); - if (mappingOptional.isPresent()) { - Open open = mappingOptional.get(); - String name = open.value(); - String version = buildVersion(open.version()); - vendorExtensions.add(new StringVendorExtension(SOP_NAME, name)); - vendorExtensions.add(new StringVendorExtension(SOP_VERSION, version)); - this.setBizCode(open, vendorExtensions); - this.setResultExtProperties(operationContext); - } - Optional apiOptional = operationContext.findControllerAnnotation(Api.class); - int order = 0; - if (apiOptional.isPresent()) { - order = apiOptional.get().position(); - } else { - Optional orderOptional = operationContext.findControllerAnnotation(Order.class); - if (orderOptional.isPresent()) { - order = orderOptional.get().value(); - } - } - vendorExtensions.add(new StringVendorExtension(MODULE_ORDER, String.valueOf(order))); - Optional apiOperationOptional = operationContext.findAnnotation(ApiOperation.class); - int methodOrder = 0; - if (apiOperationOptional.isPresent()) { - methodOrder = apiOperationOptional.get().position(); - } - vendorExtensions.add(new StringVendorExtension(API_ORDER, String.valueOf(methodOrder))); - } - - /** - * 设置返回结果额外属性,如最大长度 - * @param operationContext - */ - private void setResultExtProperties(OperationContext operationContext) { - List vendorExtensions = operationContext.getDocumentationContext().getVendorExtentions(); - ResolvedType returnType = operationContext.getReturnType(); - Class erasedType = returnType.getErasedType(); - String className = erasedType.getSimpleName(); - boolean exist = vendorExtensions.stream().anyMatch(p -> Objects.equals(p.getName(), className)); - if (!exist) { - List memberFields = returnType.getMemberFields(); - Map> fieldProperties = new HashMap<>(16); - for (RawField memberField : memberFields) { - String key = memberField.getName(); - Length length = AnnotationUtils.findAnnotation(memberField.getRawMember(), Length.class); - if (length != null) { - Map properties = fieldProperties.computeIfAbsent(key, k -> new HashMap<>(16)); - properties.computeIfAbsent("maxLength", k -> length.max()); - properties.computeIfAbsent("minLength", k -> length.max()); - } - ApiModelProperty apiModelProperty = AnnotationUtils.findAnnotation(memberField.getRawMember(), ApiModelProperty.class); - if (apiModelProperty != null) { - Map properties = fieldProperties.computeIfAbsent(key, k -> new HashMap<>(16)); - boolean required = apiModelProperty.required(); - // 只有在必填的情况下设置 - if (required) { - properties.put("required", required); - } - } - } - vendorExtensions.add(new StringVendorExtension(className, JSON.toJSONString(fieldProperties))); - } - } - - private Class getGenericType(Field curField) { - // 当前集合的泛型类型 - Type genericType = curField.getGenericType(); - if (genericType instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) genericType; - // 得到泛型里的class类型对象 - return (Class) pt.getActualTypeArguments()[0]; - } - return null; - } - - /** - * 设置业务错误码 - * @param open - * @param vendorExtensions - */ - private void setBizCode(Open open, List vendorExtensions) { - BizCode[] bizCodes = open.bizCode(); - List codeObjList = Stream.of(bizCodes) - .map(bizCode -> new BizCodeObj(bizCode.code(), bizCode.msg(), bizCode.solution())) - .collect(Collectors.toList()); - vendorExtensions.add(new StringVendorExtension(BIZ_CODE, JSON.toJSONString(codeObjList))); - } - - @Data - @AllArgsConstructor - private static class BizCodeObj { - private String code; - private String msg; - private String solution; - } - - private String buildVersion(String version) { - if ("".equals(version)) { - return ServiceConfig.getInstance().getDefaultVersion(); - } else { - return version; - } - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSecurityFilter.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSecurityFilter.java deleted file mode 100644 index 2c5afedd..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSecurityFilter.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.gitee.sop.servercommon.swagger; - -import lombok.extern.slf4j.Slf4j; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * @author 六如 - */ -@Slf4j -public class SwaggerSecurityFilter implements Filter { - - protected List urlFilters = new ArrayList<>(); - - { - urlFilters.add(".*?/doc\\.html.*"); - urlFilters.add(".*?/v2/api-docs.*"); - urlFilters.add(".*?/v2/api-docs-ext.*"); - urlFilters.add(".*?/swagger-resources.*"); - urlFilters.add(".*?/swagger-ui\\.html.*"); - urlFilters.add(".*?/swagger-resources/configuration/ui.*"); - urlFilters.add(".*?/swagger-resources/configuration/security.*"); - } - - private SwaggerValidator swaggerValidator; - - public SwaggerSecurityFilter(boolean swaggerAccessProtected) { - this.swaggerValidator = new SwaggerValidator(swaggerAccessProtected); - } - - protected boolean match(String uri) { - boolean match = false; - if (uri != null) { - for (String regex : urlFilters) { - if (uri.matches(regex)) { - match = true; - break; - } - } - } - return match; - } - - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - - } - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - if (!swaggerValidator.swaggerAccessProtected()) { - filterChain.doFilter(servletRequest, servletResponse); - return; - } - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - String uri = request.getRequestURI(); - // 没有匹配到,直接放行 - if (!match(uri)) { - filterChain.doFilter(servletRequest, servletResponse); - } else { - if (swaggerValidator.validate(request)) { - filterChain.doFilter(servletRequest, servletResponse); - } else { - swaggerValidator.writeForbidden(response); - } - - } - } - - - @Override - public void destroy() { - - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java deleted file mode 100644 index 701e91bc..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.gitee.sop.servercommon.swagger; - -import io.swagger.annotations.ApiOperation; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spi.schema.EnumTypeDeterminer; -import springfox.documentation.spring.web.DescriptionResolver; -import springfox.documentation.spring.web.plugins.Docket; - -/** - * @author 六如 - */ -public abstract class SwaggerSupport { - - /** - * 获取文档标题 - * @return 返回文档标题 - */ - protected abstract String getDocTitle(); - - @Bean - @Primary - public DocumentationPluginsManagerExt documentationPluginsManagerExt() { - return new DocumentationPluginsManagerExt(); - } - - @Bean - @Primary - public CustomSwaggerParameterBuilder customSwaggerParameterBuilder( - DescriptionResolver descriptions, - EnumTypeDeterminer enumTypeDeterminer) { - return new CustomSwaggerParameterBuilder(descriptions, enumTypeDeterminer); - } - - @Bean - public Docket createRestApi() { - return getDocket(); - } - - protected Docket getDocket() { - return new Docket(DocumentationType.SWAGGER_2) - .apiInfo(apiInfo()) - .select() - .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) - .paths(PathSelectors.any()) - .build(); - } - - @Bean - public SwaggerSecurityFilter swaggerSecurityFilter() { - return new SwaggerSecurityFilter(swaggerAccessProtected()); - } - - /** - * swagger访问是否加密保护 - * @return - */ - protected boolean swaggerAccessProtected() { - return true; - } - - protected ApiInfo apiInfo() { - return new ApiInfoBuilder() - .title(getDocTitle()) - .description("文档描述") - .termsOfServiceUrl("文档") - .version("1.0") - .build(); - } - - - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerValidator.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerValidator.java deleted file mode 100644 index cc6f2a57..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerValidator.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gitee.sop.servercommon.swagger; - -import com.gitee.sop.servercommon.util.OpenUtil; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; - -/** - * @author 六如 - */ -public class SwaggerValidator { - - private final String secret = "b749a2ec000f4f29"; - - private boolean swaggerAccessProtected = true; - - public SwaggerValidator(boolean swaggerAccessProtected) { - this.swaggerAccessProtected = swaggerAccessProtected; - } - - /** - * swagger访问是否加密保护 - * @return - */ - public boolean swaggerAccessProtected() { - return swaggerAccessProtected; - } - - public boolean validate(HttpServletRequest request) { - return OpenUtil.validateSimpleSign(request, secret); - } - - public void writeForbidden(HttpServletResponse response) throws IOException { - response.setContentType("text/palin;charset=UTF-8"); - response.setStatus(403); - PrintWriter printWriter = response.getWriter(); - printWriter.write("access forbidden"); - printWriter.flush(); - } -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/OpenUtil.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/OpenUtil.java deleted file mode 100644 index 8d7b8ddc..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/OpenUtil.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.gitee.sop.servercommon.util; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.MediaType; -import org.springframework.util.DigestUtils; - -import javax.servlet.http.HttpServletRequest; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * @author 六如 - */ -@Slf4j -public class OpenUtil { - - public static final String MULTIPART = "multipart/"; - - /** - * 将get类型的参数转换成map, - * - * @param query charset=utf-8&biz_content=xxx - * @return 返回map参数 - */ - public static Map parseQueryToMap(String query) { - if (query == null) { - return Collections.emptyMap(); - } - String[] queryList = StringUtils.split(query, '&'); - Map params = new HashMap<>(16); - for (String param : queryList) { - String[] paramArr = param.split("\\="); - if (paramArr.length == 2) { - try { - params.put(paramArr[0], URLDecoder.decode(paramArr[1], "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } else if (paramArr.length == 1) { - params.put(paramArr[0], ""); - } - } - return params; - } - - /** - * 获取request中的参数 - * - * @param request request对象 - * @return 返回JSONObject - */ - public static JSONObject getRequestParams(HttpServletRequest request) { - String contentType = request.getContentType(); - if (contentType == null) { - contentType = ""; - } - contentType = contentType.toLowerCase(); - JSONObject jsonObject; - if (StringUtils.containsAny(contentType, MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE)) { - try { - String requestJson = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8); - if (StringUtils.isBlank(requestJson)) { - jsonObject = new JSONObject(); - } else { - jsonObject = JSON.parseObject(requestJson); - } - } catch (Exception e) { - jsonObject = new JSONObject(); - log.error("获取文本数据流失败", e); - } - } else { - Map params = convertRequestParamsToMap(request); - jsonObject = new JSONObject(params); - } - return jsonObject; - } - - - /** - * request中的参数转换成map - * - * @param request request对象 - * @return 返回参数键值对 - */ - public static Map convertRequestParamsToMap(HttpServletRequest request) { - Map paramMap = request.getParameterMap(); - if (paramMap == null || paramMap.isEmpty()) { - return Collections.emptyMap(); - } - Map retMap = new HashMap(paramMap.size()); - - Set> entrySet = paramMap.entrySet(); - - for (Map.Entry entry : entrySet) { - String name = entry.getKey(); - String[] values = entry.getValue(); - if (values.length >= 1) { - retMap.put(name, values[0]); - } - } - - return retMap; - } - - public static boolean validateSimpleSign(HttpServletRequest request, String secret) { - String time = request.getParameter("time"); - String sign = request.getParameter("sign"); - if (StringUtils.isAnyBlank(time, sign)) { - return false; - } - String source = secret + time + secret; - String serverSign = DigestUtils.md5DigestAsHex(source.getBytes()); - return serverSign.equals(sign); - } - - /** - * 是否是文件上传请求 - * - * @param request 请求 - * @return true:是 - */ - public static boolean isMultipart(HttpServletRequest request) { - String contentType = request.getContentType(); - // Don't use this filter on GET method - if (contentType == null) { - return false; - } - return contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART); - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/ReflectionUtil.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/ReflectionUtil.java deleted file mode 100644 index f62ac41b..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/ReflectionUtil.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.gitee.sop.servercommon.util; - -import org.springframework.beans.FatalBeanException; -import org.springframework.context.ApplicationContext; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 反射相关 - * @author 六如 - */ -public class ReflectionUtil { - - public static final String PREFIX_SET = "set"; - - private static final String[] EMPTY_STRING_ARRAY = {}; - - private static Map> classGenricTypeCache = new HashMap<>(16); - - /** key:obj.getClass().getName() + genericClass.getName() */ - private static Map genericTypeFieldCache = new HashMap<>(); - - /** - * 设置某个字段的值 - * @param target 实体类,必须有字段的set方法 - * @param fieldName 字段名 - * @param val 值 - */ - public static void invokeFieldValue(Object target,String fieldName, Object val) { - String setMethodName = getSetMethodName(fieldName); - Method[] methods = target.getClass().getDeclaredMethods(); - for (Method method : methods) { - String methodName = method.getName(); - Class[] methodParams = method.getParameterTypes(); - - if (setMethodName.equals(methodName)) { - // 能否拷贝 - boolean canCopy = - // 并且只有一个参数 - methodParams.length == 1 - // val是methodParams[0]或他的子类 - && methodParams[0].isInstance(val) || Number.class.isInstance(val); - - if (canCopy) { - try { - if (!Modifier.isPublic(method.getDeclaringClass().getModifiers())) { - method.setAccessible(true); - } - method.invoke(target, val); - break; - } catch (Throwable ex) { - throw new FatalBeanException( - "Could not set property '" + fieldName + "' value to target", ex); - } - } - } - } - } - - /** - * 返回实体类中具有指定泛型的字段 - * @param obj 实体类 - * @param genericClass 指定泛型 - * @return 没有返回null - */ - public static Field getListFieldWithGeneric(Object obj, Class genericClass) { - Class objClass = obj.getClass(); - String key = objClass.getName() + genericClass.getName(); - Field value = genericTypeFieldCache.get(key); - if (value != null) { - return value; - } - Field[] fields = objClass.getDeclaredFields(); - for (Field field : fields) { - Type genericType = getListGenericType(field); - if (genericType == genericClass) { - genericTypeFieldCache.put(key, field); - return field; - } - } - return null; - } - - /** - * 返回集合字段的泛型类型。
- * 如:List<User> list;返回User.class - * - * @param field - * 类中的一个属性 - * @return 返回类型 - */ - public static Type getListGenericType(Field field) { - if (isListType(field.getType())) { - Type genericType = field.getGenericType(); - - if (genericType instanceof ParameterizedType) { - Type[] params = ((ParameterizedType) genericType).getActualTypeArguments(); - if (params.length == 1) { - return params[0]; - } - } - } - return Object.class; - } - - public static boolean isListType(Type type) { - return type == List.class; - } - - /** - * 返回set方法名。name -> setName - * @param fieldName - * @return 返回方法名 - */ - public static String getSetMethodName(String fieldName) { - return PREFIX_SET + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); - } - - /** - * 构建字段名称 - * @param methodName 根据get或set方法返回字段名称 - * @return 字段名称 - */ - public static String buildFieldName(String methodName) { - return methodName.substring(3, 4).toLowerCase() + methodName.substring(4); - } - - /** - * 返回定义类时的泛型参数的类型.
- * 如:定义一个BookManager类
- * {@literal public BookManager extends GenricManager}{...} - *
- * 调用getSuperClassGenricType(getClass(),0)将返回Book的Class类型
- * 调用getSuperClassGenricType(getClass(),1)将返回Address的Class类型 - * - * @param clazz 从哪个类中获取 - * @param index 泛型参数索引,从0开始 - * @return 返回泛型参数类型 - */ - public static Class getSuperClassGenricType(Class clazz, int index) throws IndexOutOfBoundsException { - String cacheKey = clazz.getName() + index; - Class cachedClass = classGenricTypeCache.get(cacheKey); - if (cachedClass != null) { - return cachedClass; - } - - Type genType = clazz.getGenericSuperclass(); - - // 没有泛型参数 - if (!(genType instanceof ParameterizedType)) { - throw new RuntimeException("class " + clazz.getName() + " 没有指定父类泛型"); - } else { - Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); - - if (index >= params.length || index < 0) { - throw new RuntimeException("泛型索引不正确,index:" + index); - } - if (!(params[index] instanceof Class)) { - throw new RuntimeException(params[index] + "不是Class类型"); - } - - Class retClass = (Class) params[index]; - // 缓存起来 - classGenricTypeCache.put(cacheKey, retClass); - - return retClass; - } - } - - /** - * 找到所有被注解标记的类名 - * @param ctx ApplicationContext - * @param annotationClass 注解class - * @return 返回类名称数组,没有返回空数组 - */ - public static String[] findBeanNamesByAnnotationClass(ApplicationContext ctx, Class annotationClass) { - String[] beans = ctx.getBeanNamesForAnnotation(annotationClass); - // 如果没找到,去父容器找 - if (beans == null || beans.length == 0) { - ApplicationContext parentCtx = ctx.getParent(); - if (parentCtx != null) { - beans = parentCtx.getBeanNamesForAnnotation(annotationClass); - } - } - if (beans == null) { - beans = EMPTY_STRING_ARRAY; - } - return beans; - } - -} diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/UploadUtil.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/UploadUtil.java deleted file mode 100644 index 63837ae8..00000000 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/UploadUtil.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.gitee.sop.servercommon.util; - -import org.springframework.util.MultiValueMap; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; - -import javax.servlet.http.HttpServletRequest; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * 文件上传工具类 - * - * @author 六如 - */ -public class UploadUtil { - - /** - * 获取上传文件 - * - * @param request - * @return - */ - public static Collection getUploadFiles(HttpServletRequest request) { - MultiValueMap fileMap = null; - //检查form中是否有enctype="multipart/form-data" - String contentType = request.getContentType(); - if (contentType != null && contentType.toLowerCase().contains("multipart")) { - //将request变成多部分request - MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; - fileMap = multiRequest.getMultiFileMap(); - } - return Optional.ofNullable(fileMap) - .map(Map::entrySet) - .map(entry -> entry.stream() - .flatMap(e -> e.getValue().stream()) - .collect(Collectors.toList())) - .orElse(Collections.emptyList()); - } -} diff --git a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_en.properties b/sop-common/sop-service-common/src/main/resources/i18n/isp/error_en.properties deleted file mode 100644 index 9034e15d..00000000 --- a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_en.properties +++ /dev/null @@ -1,5 +0,0 @@ -# \u9519\u8BEF\u914D\u7F6E - -isp.error_isp.service-unknown-error=Service not unavailable -isp.error_isv.invalid-parameter=Invalid parameter -isp.error_isv.common-error=Service error \ No newline at end of file diff --git a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_zh_CN.properties b/sop-common/sop-service-common/src/main/resources/i18n/isp/error_zh_CN.properties deleted file mode 100644 index ceb09ae9..00000000 --- a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_zh_CN.properties +++ /dev/null @@ -1,6 +0,0 @@ -# \u9519\u8BEF\u914D\u7F6E - -isp.error_isp.service-unknown-error=\u672A\u77E5\u9519\u8BEF -# \u53C2\u6570\u65E0\u6548 -isp.error_isv.invalid-parameter=\u53C2\u6570\u65E0\u6548 -isp.error_isv.common-error=\u7CFB\u7EDF\u9519\u8BEF \ No newline at end of file diff --git a/sop-common/sop-service-common/src/test/java/com/gitee/sop/servercommon/ValidatorTest.java b/sop-common/sop-service-common/src/test/java/com/gitee/sop/servercommon/ValidatorTest.java deleted file mode 100644 index 89917feb..00000000 --- a/sop-common/sop-service-common/src/test/java/com/gitee/sop/servercommon/ValidatorTest.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.gitee.sop.servercommon; - -import com.gitee.sop.servercommon.param.ServiceParamValidator; -import com.gitee.sop.servercommon.param.validation.Group1; -import com.gitee.sop.servercommon.param.validation.Group2; -import com.gitee.sop.servercommon.param.validation.Group3; -import junit.framework.TestCase; -import lombok.AllArgsConstructor; -import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import javax.validation.constraints.Min; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import java.math.BigDecimal; - -/** - * @author 六如 - */ -public class ValidatorTest extends TestCase { - - private ServiceParamValidator serviceParamValidator = new ServiceParamValidator(); - - /** - * 测试JSR-303注解校验顺序,校验顺序: Group1~GroupN - */ - public void testValidate() { - serviceParamValidator.validateBizParam(new User("Jim", 30)); - } - - - public void testField() { - Sub sub = new Sub("sub", Type.ONE); - Manager manager = new Manager("Jim", 22, Type.TWO, Status.OK, sub); - Store store = new Store("仓库A", manager, Type.ONE); - Goods goods = new Goods("Apple", new BigDecimal(50000), store); - serviceParamValidator.validateBizParam(goods); - } - - @Data - @AllArgsConstructor - private static class User { - - // 如果字段为空,无论如何都会命中这个 - @NotBlank(message = "NotBlank", groups = Group1.class) - // 优先校验Group2 - // 可交换下面Group2,Group3,看下校验顺序 - @Length(min = 2, max = 20, message = "length must 2~20", groups = Group2.class) - @Pattern(regexp = "[a-zA-Z]*", message = "name must letters", groups = Group3.class) - private String name; - - @Min(value = 1, message = "min 1") - private int age; - - } - - @Data - @AllArgsConstructor - static class Goods { - @NotBlank(message = "商品名称不能为空") - private String goodsName; - - @Min(value = 1, message = "商品价格最小值为1") - private BigDecimal price; - - @NotNull(message = "仓库不能为空") - private Store store; - } - - @Data - @AllArgsConstructor - static class Store { - @NotBlank(message = "库存名称不能为空") - private String storeName; - - @NotNull(message = "管理员不能为空") - private Manager manager; - - @NotNull(message = "Store.type不能为空") - private Type type; - } - - @Data - @AllArgsConstructor - static class Manager { - @NotBlank(message = "管理员姓名不能为空") - private String name; - - private int age; - - @NotNull(message = "Manager.type不能为空") - private Type type; - - @NotNull(message = "Manager.status不能为空") - private Status status; - - @NotNull(message = "sub不能为空") - private Sub sub; - } - - @Data - @AllArgsConstructor - static class Sub { - @NotBlank(message = "管理员姓名不能为空") - private String name; - - @NotNull(message = "Sub.type不能为空") - private Type type; - } - - enum Type { - ONE,TWO - } - - enum SubType { - OK,ERR - } - - enum Status { - OK, ERROR - } - -} diff --git a/sop-common/src/main/java/com/gitee/sop/common/bean/SpringContext.java b/sop-common/src/main/java/com/gitee/sop/common/bean/SpringContext.java deleted file mode 100644 index 92ae9da6..00000000 --- a/sop-common/src/main/java/com/gitee/sop/common/bean/SpringContext.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gitee.sop.common.bean; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationEvent; - -/** - * @author 六如 - */ -public class SpringContext { - - private static ApplicationContext ctx; - - public static T getBean(Class clazz) { - return ctx.getBean(clazz); - } - - public static Object getBean(String beanName) { - return ctx.getBean(beanName); - } - - public static void setApplicationContext(ApplicationContext ctx) { - SpringContext.ctx = ctx; - } - - public static ApplicationContext getApplicationContext() { - return ctx; - } - - public static void publishEvent(ApplicationEvent event) { - ctx.publishEvent(event); - } -} diff --git a/sop-common/src/main/java/com/gitee/sop/common/enums/StatusEnum.java b/sop-common/src/main/java/com/gitee/sop/common/enums/StatusEnum.java deleted file mode 100644 index b54926fc..00000000 --- a/sop-common/src/main/java/com/gitee/sop/common/enums/StatusEnum.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.common.enums; - -import lombok.Getter; - -import java.util.Objects; - -/** - * @author 六如 - */ -@Getter -public enum StatusEnum { - DISABLED((byte)2), - ENABLE((byte)1), - SET_PWD((byte)3), - ; - - private final int status; - - public static StatusEnum of(Integer value) { - for (StatusEnum statusEnum : StatusEnum.values()) { - if (Objects.equals(statusEnum.status, value)) { - return statusEnum; - } - } - return DISABLED; - } - - StatusEnum(byte style) { - this.status = style; - } - - -} diff --git a/sop-common/src/main/java/com/gitee/sop/common/req/IdParam.java b/sop-common/src/main/java/com/gitee/sop/common/req/IdParam.java deleted file mode 100644 index 5a3eeb09..00000000 --- a/sop-common/src/main/java/com/gitee/sop/common/req/IdParam.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gitee.sop.common.req; - -import lombok.Data; - -import javax.validation.constraints.NotNull; - -/** - * @author 六如 - */ -@Data -public class IdParam { - @NotNull(message = "id不能为空") - private Long id; -} diff --git a/sop-common/src/main/java/com/gitee/sop/common/req/StatusUpdateParam.java b/sop-common/src/main/java/com/gitee/sop/common/req/StatusUpdateParam.java deleted file mode 100644 index 1efe954b..00000000 --- a/sop-common/src/main/java/com/gitee/sop/common/req/StatusUpdateParam.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.common.req; - -import com.gitee.sop.adminbackend.common.req.IdParam; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -/** - * @author 六如 - */ -@Data -public class StatusUpdateParam extends IdParam { - - @NotNull(message = "状态不能为空") - private Integer status; - -} diff --git a/sop-common/src/main/java/com/gitee/sop/common/resp/Result.java b/sop-common/src/main/java/com/gitee/sop/common/resp/Result.java deleted file mode 100644 index f502c112..00000000 --- a/sop-common/src/main/java/com/gitee/sop/common/resp/Result.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gitee.sop.common.resp; - -import lombok.Data; - -import java.util.Objects; - -/** - * @author thc - */ -@Data -public class Result { - - private static final Result RESULT = new Result<>(); - - private String code = "0"; - private T data; - private String msg = "success"; - - public static Result ok() { - return RESULT; - } - - public static Result ok(E obj) { - Result result = new Result<>(); - result.setData(obj); - return result; - } - - public static Result err(String msg) { - Result result = new Result<>(); - result.setCode("1"); - result.setMsg(msg); - return result; - } - - public static Result err(String code, String msg) { - Result result = new Result<>(); - result.setCode(code); - result.setMsg(msg); - return result; - } - - public boolean getSuccess() { - return Objects.equals("0", code); - } -} diff --git a/sop-gateway_old/.gitignore b/sop-gateway_old/.gitignore deleted file mode 100644 index c456c4a3..00000000 --- a/sop-gateway_old/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -/build/ diff --git a/sop-gateway_old/pom.xml b/sop-gateway_old/pom.xml deleted file mode 100644 index dae11134..00000000 --- a/sop-gateway_old/pom.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - com.gitee.sop - sop-parent - 5.0.0-SNAPSHOT - ../pom.xml - - - 4.0.0 - sop-gateway - - - 1.8 - - - - - - com.gitee.sop - sop-bridge-nacos - - ${project.version} - - - - org.springframework.cloud - spring-cloud-starter-gateway - - - org.springframework.boot - spring-boot-starter-webflux - - - org.springframework.boot - spring-boot-starter-actuator - - - - net.oschina.durcframework - fastmybatis-spring-boot-starter - - - mysql - mysql-connector-java - runtime - - - javax.servlet - javax.servlet-api - - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.projectlombok - lombok - 1.18.4 - provided - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12.4 - - true - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - maven2 - maven2 - https://repo1.maven.org/maven2 - - - - diff --git a/sop-gateway_old/readme.md b/sop-gateway_old/readme.md deleted file mode 100644 index c0790aa6..00000000 --- a/sop-gateway_old/readme.md +++ /dev/null @@ -1,12 +0,0 @@ -# sop-gateway - -网关入口,默认使用的是Spring Cloud Gateway,如果要使用Zuul,修改pom.xml - -```xml - - com.gitee.sop - - sop-bridge-zuul - version - -``` \ No newline at end of file diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java deleted file mode 100644 index 2b536bd1..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.gitee.sop.gateway; - -import org.mybatis.spring.annotation.MapperScan; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@MapperScan(basePackages = { - "com.gitee.sop.gateway.mapper" -}) -@SpringBootApplication(scanBasePackages = "com.gitee.sop") -public class SopGatewayApplication { - - public static void main(String[] args) { - SpringApplication.run(SopGatewayApplication.class, args); - } - -} - diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/config/MyConfig.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/config/MyConfig.java deleted file mode 100644 index d2c5e42a..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/config/MyConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.sop.gateway.config; - -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -@Component -public class MyConfig { - - @PostConstruct - public void after() { - ApiConfig.getInstance().setTokenValidator(apiParam -> { - // 获取客户端传递过来的token - String token = apiParam.fetchAccessToken(); - return !StringUtils.isBlank(token); - // TODO: 校验token有效性,可以从redis中读取 - - // 返回true表示这个token真实、有效 - }); - } -} \ No newline at end of file diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigGray.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigGray.java deleted file mode 100644 index 5b85d8dc..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigGray.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:config_gray - * 备注:服务灰度配置 - * - * @author 六如 - */ -@Table(name = "config_gray",pk = @Pk(name = "id")) -@Data -public class ConfigGray { - /** 数据库字段:id */ - private Long id; - - /** 数据库字段:service_id */ - private String serviceId; - - /** 用户key,多个用引文逗号隔开, 数据库字段:user_key_content */ - private String userKeyContent; - - /** 需要灰度的接口,goods.get1.0=1.2,多个用英文逗号隔开, 数据库字段:name_version_content */ - private String nameVersionContent; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigGrayInstance.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigGrayInstance.java deleted file mode 100644 index d6fcbc4c..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigGrayInstance.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:config_gray_instance - * - * @author 六如 - */ -@Table(name = "config_gray_instance",pk = @Pk(name = "id")) -@Data -public class ConfigGrayInstance { - /** 数据库字段:id */ - private Long id; - - /** instance_id, 数据库字段:instance_id */ - private String instanceId; - - /** service_id, 数据库字段:service_id */ - private String serviceId; - - /** 0:禁用,1:启用, 数据库字段:status */ - private Byte status; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigLimit.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigLimit.java deleted file mode 100644 index 55a0551f..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigLimit.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:config_limit - * 备注:限流配置 - * - * @author 六如 - */ -@Table(name = "config_limit",pk = @Pk(name = "id")) -@Data -public class ConfigLimit { - /** 数据库字段:id */ - private Long id; - - /** 路由id, 数据库字段:route_id */ - private String routeId; - - /** 数据库字段:app_key */ - private String appKey; - - /** 限流ip,多个用英文逗号隔开, 数据库字段:limit_ip */ - private String limitIp; - - - /** 服务id, 数据库字段:service_id */ - private String serviceId; - - /** 限流策略,1:窗口策略,2:令牌桶策略, 数据库字段:limit_type */ - private Byte limitType; - - /** 每秒可处理请求数, 数据库字段:exec_count_per_second */ - private Integer execCountPerSecond; - - /** 返回的错误码, 数据库字段:limit_code */ - private String limitCode; - - /** 返回的错误信息, 数据库字段:limit_msg */ - private String limitMsg; - - /** 令牌桶容量, 数据库字段:token_bucket_count */ - private Integer tokenBucketCount; - - /** 限流开启状态,1:开启,0关闭, 数据库字段:limit_status */ - private Byte limitStatus; - - /** 顺序,值小的优先执行, 数据库字段:order_index */ - private Integer orderIndex; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteBase.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteBase.java deleted file mode 100644 index 2b4fe8a5..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteBase.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; - - -/** - * 表名:config_route_base - * 备注:路由配置表 - * - * @author 六如 - */ -@Table(name = "config_route_base",pk = @Pk(name = "id")) -@Data -public class ConfigRouteBase { - /** 数据库字段:id */ - private Long id; - - /** 路由id, 数据库字段:route_id */ - private String routeId; - - /** 状态,1:启用,2:禁用, 数据库字段:status */ - private Byte status; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java deleted file mode 100644 index 671f6163..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:config_service_route - * 备注:路由配置 - * - * @author 六如 - */ -@Table(name = "config_service_route",pk = @Pk(name = "id")) -@Data -public class ConfigServiceRoute { - /** 数据库字段:id */ - private String id; - - /** 数据库字段:service_id */ - private String serviceId; - - /** 接口名, 数据库字段:name */ - private String name; - - /** 版本号, 数据库字段:version */ - private String version; - - /** 路由断言(SpringCloudGateway专用), 数据库字段:predicates */ - private String predicates; - - /** 路由过滤器(SpringCloudGateway专用), 数据库字段:filters */ - private String filters; - - /** 路由规则转发的目标uri, 数据库字段:uri */ - private String uri; - - /** uri后面跟的path, 数据库字段:path */ - private String path; - - /** 路由执行的顺序, 数据库字段:order_index */ - private Integer orderIndex; - - /** 是否忽略验证,业务参数验证除外, 数据库字段:ignore_validate */ - private Byte ignoreValidate; - - /** 状态,0:待审核,1:启用,2:禁用, 数据库字段:status */ - private Byte status; - - /** 是否合并结果, 数据库字段:merge_result */ - private Byte mergeResult; - - /** 是否需要授权才能访问, 数据库字段:permission */ - private Byte permission; - - /** 是否需要token, 数据库字段:need_token */ - private Byte needToken; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/IsvDetailDTO.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/IsvDetailDTO.java deleted file mode 100644 index 08c0acbb..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/IsvDetailDTO.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -/** - * @author 六如 - */ -@Data -public class IsvDetailDTO { - - /** appKey, 数据库字段:app_key */ - private String appKey; - - /** 0启用,1禁用, 数据库字段:status */ - private Byte status; - - /** secret, 数据库字段:secret */ - private String secret; - - /** 开发者生成的公钥, 数据库字段:public_key_isv */ - private String publicKeyIsv; - - /** 平台生成的私钥, 数据库字段:private_key_platform */ - private String privateKeyPlatform; - - /** 签名类型:1:RSA2,2:MD5 */ - private Byte signType; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/IsvInfo.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/IsvInfo.java deleted file mode 100644 index df770aec..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/IsvInfo.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:isv_info - * 备注:isv信息表 - * - * @author 六如 - */ -@Table(name = "isv_info",pk = @Pk(name = "id")) -@Data -public class IsvInfo { - /** 数据库字段:id */ - private Long id; - - /** appKey, 数据库字段:app_key */ - private String appKey; - - /** 1启用,2禁用, 数据库字段:status */ - private Byte status; - - /** 1:RSA2,2:MD5, 数据库字段:sign_type */ - private Byte signType; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/MonitorInfo.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/MonitorInfo.java deleted file mode 100644 index f5b71cb3..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/MonitorInfo.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.time.LocalDateTime; -import java.util.Date; - - -/** - * 表名:monitor_info - * 备注:接口监控信息 - * - * @author 六如 - */ -@Table(name = "monitor_info",pk = @Pk(name = "id")) -@Data -public class MonitorInfo { - /** 数据库字段:id */ - private Long id; - - /** 路由id, 数据库字段:route_id */ - private String routeId; - - /** 接口名, 数据库字段:name */ - private String name; - - /** 版本号, 数据库字段:version */ - private String version; - - /** 数据库字段:service_id */ - private String serviceId; - - /** 数据库字段:instance_id */ - private String instanceId; - - /** 请求耗时最长时间, 数据库字段:max_time */ - private Integer maxTime; - - /** 请求耗时最小时间, 数据库字段:min_time */ - private Integer minTime; - - /** 总时长,毫秒, 数据库字段:total_time */ - private Long totalTime; - - /** 总调用次数, 数据库字段:total_request_count */ - private Long totalRequestCount; - - /** 成功次数, 数据库字段:success_count */ - private Long successCount; - - /** 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败), 数据库字段:error_count */ - private Long errorCount; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/MonitorInfoError.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/MonitorInfoError.java deleted file mode 100644 index f83f93f5..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/MonitorInfoError.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.time.LocalDateTime; -import java.util.Date; - - -/** - * 表名:monitor_info_error - * - * @author 六如 - */ -@Table(name = "monitor_info_error",pk = @Pk(name = "id")) -@Data -public class MonitorInfoError { - /** 数据库字段:id */ - private Long id; - - /** 错误id,md5Hex(instanceId + routeId + errorMsg), 数据库字段:error_id */ - private String errorId; - - /** 实例id, 数据库字段:instance_id */ - private String instanceId; - - /** 数据库字段:route_id */ - private String routeId; - - /** 数据库字段:error_msg */ - private String errorMsg; - - /** http status,非200错误, 数据库字段:error_status */ - private Integer errorStatus; - - /** 错误次数, 数据库字段:count */ - private Integer count; - - /** 数据库字段:is_deleted */ - @com.gitee.fastmybatis.core.annotation.LogicDelete - private Byte isDeleted; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/PermIsvRole.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/PermIsvRole.java deleted file mode 100644 index e6f36030..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/PermIsvRole.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:perm_isv_role - * 备注:isv角色 - * - * @author 六如 - */ -@Table(name = "perm_isv_role",pk = @Pk(name = "id")) -@Data -public class PermIsvRole { - /** 数据库字段:id */ - private Long id; - - /** isv_info表id, 数据库字段:isv_id */ - private Long isvId; - - /** 角色code, 数据库字段:role_code */ - private String roleCode; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/PermRolePermission.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/PermRolePermission.java deleted file mode 100644 index 444c6aae..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/entity/PermRolePermission.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.sop.gateway.entity; - -import lombok.Data; - -import com.gitee.fastmybatis.annotation.Pk; -import com.gitee.fastmybatis.annotation.Table; -import java.util.Date; - - -/** - * 表名:perm_role_permission - * 备注:角色权限表 - * - * @author 六如 - */ -@Table(name = "perm_role_permission",pk = @Pk(name = "id")) -@Data -public class PermRolePermission { - /** 数据库字段:id */ - private Long id; - - /** 角色表code, 数据库字段:role_code */ - private String roleCode; - - /** api_id, 数据库字段:route_id */ - private String routeId; - - /** 数据库字段:gmt_create */ - private Date gmtCreate; - - /** 数据库字段:gmt_modified */ - private Date gmtModified; -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptor.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptor.java deleted file mode 100644 index 2593db03..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptor.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.gitee.sop.gateway.interceptor; - -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.sync.SopAsyncConfigurer; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * 用于收集监控数据 - * - * @author 六如 - */ -@Component -@Slf4j -public class MonitorRouteInterceptor implements RouteInterceptor { - - @Autowired - SopAsyncConfigurer sopAsyncConfigurer; - - @Autowired - MonitorRouteInterceptorService monitorRouteInterceptorService; - - @Override - public void preRoute(RouteInterceptorContext context) { - } - - @Override - public void afterRoute(RouteInterceptorContext context) { - sopAsyncConfigurer.getAsyncExecutor().execute(()-> { - monitorRouteInterceptorService.storeRequestInfo(context); - }); - } - - @Override - public int getOrder() { - return -1000; - } - - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptorService.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptorService.java deleted file mode 100644 index 0e1eb1dc..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MonitorRouteInterceptorService.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.gitee.sop.gateway.interceptor; - -import com.gitee.sop.gateway.manager.DbMonitorInfoManager; -import com.gitee.sop.gatewaycommon.bean.LRUCache; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext; -import com.gitee.sop.gatewaycommon.monitor.MonitorDTO; -import com.gitee.sop.gatewaycommon.monitor.MonitorData; -import com.gitee.sop.gatewaycommon.monitor.MonitorManager; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.sync.MyNamedThreadFactory; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author 六如 - */ -@Service -@Slf4j -public class MonitorRouteInterceptorService { - - /** - * 刷新到数据库时间间隔,秒 - */ - @Value("${sop.monitor.flush-period-seconds:30}") - int flushPeriodSeconds; - - /** - * 定时任务每n秒,执行一次 - */ - @Value("${sop.monitor.schedule-period-seconds:30}") - int schedulePeriodSeconds; - - /** - * 错误数量容量 - */ - @Value("${sop.monitor.error-count-capacity:50}") - int monitorErrorCapacity; - - @Autowired - DbMonitorInfoManager dbMonitorInfoManager; - - @Autowired - MonitorManager monitorManager; - - /** - * 记录接口调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数. - * 需要考虑并发情况。 - */ - public synchronized void storeRequestInfo(RouteInterceptorContext context) { - ApiParam apiParam = context.getApiParam(); - ServiceInstance serviceInstance = context.getServiceInstance(); - String routeId = apiParam.getRouteId(); - int spendTime = (int)(context.getFinishTimeMillis() - context.getBeginTimeMillis()); - // 这步操作是线程安全的,底层调用了ConcurrentHashMap.computeIfAbsent - String key = getMonitorKey(routeId, serviceInstance.getInstanceId()); - MonitorData monitorData = monitorManager.getMonitorInfo(key, (k) -> this.createMonitorInfo(apiParam, serviceInstance)); - monitorData.storeMaxTime(spendTime); - monitorData.storeMinTime(spendTime); - monitorData.getTotalRequestCount().incrementAndGet(); - monitorData.getTotalTime().addAndGet(spendTime); - if (context.isSuccessRequest()) { - monitorData.getSuccessCount().incrementAndGet(); - } else { - monitorData.getErrorCount().incrementAndGet(); - String errorMsg = context.getServiceErrorMsg(); - monitorData.addErrorMsg(errorMsg, context.getResponseStatus()); - } - } - - private String getMonitorKey(String routeId, String instanceId) { - return routeId + instanceId; - } - - /** - * 刷新到数据库 - */ - private synchronized void flushDb() { - Map monitorData = monitorManager.getMonitorData(); - if (monitorData.isEmpty()) { - return; - } - LocalDateTime checkTime = LocalDateTime.now(); - List tobeRemoveKeys = new ArrayList<>(); - List tobeSaveBatch = new ArrayList<>(monitorData.size()); - monitorData.forEach((key, value) -> { - LocalDateTime flushTime = value.getFlushTime(); - if (flushTime.isEqual(checkTime) || flushTime.isBefore(checkTime)) { - log.debug("刷新监控数据到数据库, MonitorData:{}", value); - tobeRemoveKeys.add(key); - MonitorDTO monitorDTO = getMonitorDTO(value); - tobeSaveBatch.add(monitorDTO); - } - }); - dbMonitorInfoManager.saveMonitorInfoBatch(tobeSaveBatch); - - for (String key : tobeRemoveKeys) { - monitorData.remove(key); - } - } - - private MonitorDTO getMonitorDTO(MonitorData monitorData) { - MonitorDTO monitorDTO = new MonitorDTO(); - monitorDTO.setRouteId(monitorData.getRouteId()); - monitorDTO.setName(monitorData.getName()); - monitorDTO.setVersion(monitorData.getVersion()); - monitorDTO.setServiceId(monitorData.getServiceId()); - monitorDTO.setInstanceId(monitorData.getInstanceId()); - monitorDTO.setMaxTime(monitorData.getMaxTime()); - monitorDTO.setMinTime(monitorData.getMinTime()); - monitorDTO.setTotalTime(monitorData.getTotalTime().longValue()); - monitorDTO.setTotalRequestCount(monitorData.getTotalRequestCount().longValue()); - monitorDTO.setSuccessCount(monitorData.getSuccessCount().longValue()); - monitorDTO.setErrorCount(monitorData.getErrorCount().longValue()); - monitorDTO.setErrorMsgList(monitorData.getMonitorErrorMsgMap().values()); - return monitorDTO; - } - - private MonitorData createMonitorInfo(ApiParam apiParam, ServiceInstance serviceInstance) { - MonitorData monitorData = new MonitorData(); - monitorData.setRouteId(apiParam.getRouteId()); - monitorData.setName(apiParam.fetchName()); - monitorData.setVersion(apiParam.fetchVersion()); - monitorData.setServiceId(apiParam.fetchServiceId()); - monitorData.setInstanceId(serviceInstance.getInstanceId()); - monitorData.setTotalTime(new AtomicInteger()); - monitorData.setMaxTime(0); - monitorData.setMinTime(0); - monitorData.setSuccessCount(new AtomicInteger()); - monitorData.setTotalRequestCount(new AtomicInteger()); - monitorData.setErrorCount(new AtomicInteger()); - monitorData.setFlushTime(getFlushTime()); - monitorData.setMonitorErrorMsgMap(new LRUCache<>(monitorErrorCapacity)); - return monitorData; - } - - private LocalDateTime getFlushTime() { - return LocalDateTime.now() - .plusSeconds(flushPeriodSeconds); - } - - - @PostConstruct - public void after() { - // 每隔schedulePeriodSeconds秒执行一次 - ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1, new MyNamedThreadFactory("monitorSchedule")); - // 延迟执行,随机5~14秒 - int delay = 5 + new Random().nextInt(10); - scheduledThreadPoolExecutor.scheduleWithFixedDelay(this::flushDb, delay, schedulePeriodSeconds, TimeUnit.SECONDS); - - } -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java deleted file mode 100644 index 972ea905..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.gitee.sop.gateway.interceptor; - -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor; -import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.server.ServerWebExchange; - -import java.net.URI; - -/** - * 演示拦截器 - * - * @author 六如 - */ -@Slf4j -@Component -public class MyRouteInterceptor implements RouteInterceptor { - - @Override - public void preRoute(RouteInterceptorContext context) { - ApiParam apiParam = context.getApiParam(); - ServerWebExchange exchange = (ServerWebExchange) context.getRequestContext(); - URI uri = exchange.getRequest().getURI(); - log.info("请求URL:{}, 请求接口:{}, request_id:{}, app_id:{}, ip:{}", - uri, - apiParam.fetchNameVersion(), - apiParam.fetchRequestId(), - apiParam.fetchAppKey(), - apiParam.fetchIp() - ); - } - - @Override - public void afterRoute(RouteInterceptorContext context) { - String serviceErrorMsg = context.getServiceErrorMsg(); - if (StringUtils.hasText(serviceErrorMsg)) { - log.error("错误信息:{}", serviceErrorMsg); - } - } - - @Override - public int getOrder() { - return 0; - } - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbEnvGrayManager.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbEnvGrayManager.java deleted file mode 100644 index 3f69138a..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbEnvGrayManager.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.gitee.fastmybatis.core.query.Query; -import com.gitee.sop.gateway.entity.ConfigGray; -import com.gitee.sop.gateway.entity.ConfigGrayInstance; -import com.gitee.sop.gateway.mapper.ConfigGrayInstanceMapper; -import com.gitee.sop.gateway.mapper.ConfigGrayMapper; -import com.gitee.sop.gatewaycommon.bean.ChannelMsg; -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.bean.ServiceGrayDefinition; -import com.gitee.sop.gatewaycommon.manager.DefaultEnvGrayManager; -import com.gitee.sop.gatewaycommon.route.RegistryEvent; -import com.gitee.sop.gatewaycommon.loadbalancer.ServiceGrayConfig; -import com.google.common.collect.Sets; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * 存放用户key,这里放在本机内容,如果灰度发布保存的用户id数量偏多,可放在redis中 - * - * @author 六如 - */ -@Slf4j -@Service -public class DbEnvGrayManager extends DefaultEnvGrayManager implements RegistryEvent { - - private static final int STATUS_ENABLE = 1; - - private static final Function FUNCTION_KEY = arr -> arr[0]; - private static final Function FUNCTION_VALUE = arr -> arr[1]; - - @Autowired - private ConfigGrayMapper configGrayMapper; - - @Autowired - private ConfigGrayInstanceMapper configGrayInstanceMapper; - - @Override - public void onRegistry(InstanceDefinition instanceDefinition) { - String instanceId = instanceDefinition.getInstanceId(); - ConfigGrayInstance grayInstance = configGrayInstanceMapper.getByColumn("instance_id", instanceId); - if (grayInstance != null && grayInstance.getStatus() == STATUS_ENABLE) { - log.info("实例[{}]开启灰度发布", grayInstance.getInstanceId()); - this.openGray(grayInstance.getInstanceId(), grayInstance.getServiceId()); - } - } - - @Override - public void onRemove(String serviceId) { - - } - - @Override - public void load() { - List list = configGrayMapper.list(new Query()); - for (ConfigGray configGray : list) { - this.setServiceGrayConfig(configGray); - } - } - - /** - * 设置用户key - * - * @param configGray 灰度配置 - */ - public void setServiceGrayConfig(ConfigGray configGray) { - if (configGray == null) { - return; - } - String userKeyData = configGray.getUserKeyContent(); - String nameVersionContent = configGray.getNameVersionContent(); - String[] userKeys = StringUtils.split(userKeyData, ','); - String[] nameVersionList = StringUtils.split(nameVersionContent, ','); - log.info("灰度配置,userKeys.length:{}, nameVersionList:{}", userKeys.length, Arrays.toString(nameVersionList)); - - Set userKeySet = Stream.of(userKeys) - .collect(Collectors.toCollection(Sets::newConcurrentHashSet)); - - Map grayNameVersionMap = Stream.of(nameVersionList) - .map(nameVersion -> StringUtils.split(nameVersion, '=')) - .collect(Collectors.toConcurrentMap(FUNCTION_KEY, FUNCTION_VALUE)); - - ServiceGrayConfig serviceGrayConfig = new ServiceGrayConfig(); - serviceGrayConfig.setServiceId(configGray.getServiceId()); - serviceGrayConfig.setUserKeys(userKeySet); - serviceGrayConfig.setGrayNameVersion(grayNameVersionMap); - this.saveServiceGrayConfig(serviceGrayConfig); - } - - @Override - public void process(ChannelMsg channelMsg) { - ServiceGrayDefinition userKeyDefinition = channelMsg.toObject(ServiceGrayDefinition.class); - String serviceId = userKeyDefinition.getServiceId(); - switch (channelMsg.getOperation()) { - case "set": - ConfigGray configGray = configGrayMapper.getByColumn("service_id", serviceId); - setServiceGrayConfig(configGray); - break; - case "open": - openGray(userKeyDefinition.getInstanceId(), serviceId); - break; - case "close": - closeGray(userKeyDefinition.getInstanceId()); - break; - default: - } - } - - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIPBlacklistManager.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIPBlacklistManager.java deleted file mode 100644 index a2ea4a58..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIPBlacklistManager.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.gitee.sop.gateway.mapper.IPBlacklistMapper; -import com.gitee.sop.gatewaycommon.bean.ChannelMsg; -import com.gitee.sop.gatewaycommon.manager.DefaultIPBlacklistManager; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 限流配置管理 - * - * @author 六如 - */ -@Slf4j -@Service -public class DbIPBlacklistManager extends DefaultIPBlacklistManager { - - @Autowired - private IPBlacklistMapper ipBlacklistMapper; - - @Override - public void load() { - List ipList = ipBlacklistMapper.listAllIP(); - log.info("加载IP黑名单, size:{}", ipList.size()); - ipList.forEach(this::add); - - } - - @Override - public void process(ChannelMsg channelMsg) { - final IPDto ipDto = channelMsg.toObject(IPDto.class); - String ip = ipDto.getIp(); - switch (channelMsg.getOperation()) { - case "add": - log.info("添加IP黑名单,ip:{}", ip); - add(ip); - break; - case "delete": - log.info("移除IP黑名单,ip:{}", ip); - remove(ip); - break; - default: - } - } - - - @Data - private static class IPDto { - private String ip; - } -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIsvManager.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIsvManager.java deleted file mode 100644 index 08b4563b..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIsvManager.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.gitee.sop.gateway.entity.IsvDetailDTO; -import com.gitee.sop.gateway.mapper.IsvInfoMapper; -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.ChannelMsg; -import com.gitee.sop.gatewaycommon.bean.IsvDefinition; -import com.gitee.sop.gatewaycommon.secret.CacheIsvManager; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * @author 六如 - */ -@Slf4j -@Service -public class DbIsvManager extends CacheIsvManager { - - public DbIsvManager() { - ApiConfig.getInstance().setIsvManager(this); - } - - @Autowired - private IsvInfoMapper isvInfoMapper; - - @Override - public void load() { - List isvInfoList = isvInfoMapper.listIsvDetail(); - isvInfoList - .forEach(isvInfo -> { - IsvDefinition isvDefinition = new IsvDefinition(); - BeanUtils.copyProperties(isvInfo, isvDefinition); - this.getIsvCache().put(isvDefinition.getAppKey(), isvDefinition); - }); - } - - @Override - public void process(ChannelMsg channelMsg) { - final IsvDefinition isvDefinition = channelMsg.toObject(IsvDefinition.class); - switch (channelMsg.getOperation()) { - case "update": - log.info("更新ISV信息,isvDefinition:{}", isvDefinition); - update(isvDefinition); - break; - case "remove": - log.info("删除ISV,isvDefinition:{}", isvDefinition); - remove(isvDefinition.getAppKey()); - break; - default: - - } - } - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIsvRoutePermissionManager.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIsvRoutePermissionManager.java deleted file mode 100644 index d019d98e..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbIsvRoutePermissionManager.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.alibaba.fastjson.JSON; -import com.gitee.fastmybatis.core.query.Query; -import com.gitee.sop.gateway.entity.IsvInfo; -import com.gitee.sop.gateway.entity.PermIsvRole; -import com.gitee.sop.gateway.entity.PermRolePermission; -import com.gitee.sop.gateway.mapper.IsvInfoMapper; -import com.gitee.sop.gateway.mapper.PermIsvRoleMapper; -import com.gitee.sop.gateway.mapper.PermRolePermissionMapper; -import com.gitee.sop.gatewaycommon.bean.ChannelMsg; -import com.gitee.sop.gatewaycommon.bean.IsvRoutePermission; -import com.gitee.sop.gatewaycommon.manager.DefaultIsvRoutePermissionManager; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import static java.util.stream.Collectors.mapping; -import static java.util.stream.Collectors.toList; - -/** - * 从数据库中读取路由权限信息 - * - * @author 六如 - */ -@Slf4j -@Service -public class DbIsvRoutePermissionManager extends DefaultIsvRoutePermissionManager { - - @Autowired - Environment environment; - - @Autowired - PermIsvRoleMapper permIsvRoleMapper; - - @Autowired - PermRolePermissionMapper permRolePermissionMapper; - - @Autowired - IsvInfoMapper isvInfoMapper; - - - @Override - public void load() { - // key: appKey, value: roleCodeList - Map> appKeyRoleCodeMap = this.getIsvRoleCode(); - - for (Map.Entry> entry : appKeyRoleCodeMap.entrySet()) { - this.loadIsvRoutePermission(entry.getKey(), entry.getValue()); - } - } - - public void loadIsvRoutePermission(String appKey, List roleCodeList) { - Collections.sort(roleCodeList); - List routeIdList = this.getRouteIdList(roleCodeList); - String roleCodeListMd5 = DigestUtils.md5Hex(JSON.toJSONString(routeIdList)); - IsvRoutePermission isvRoutePermission = new IsvRoutePermission(); - isvRoutePermission.setAppKey(appKey); - isvRoutePermission.setRouteIdList(routeIdList); - isvRoutePermission.setRouteIdListMd5(roleCodeListMd5); - this.update(isvRoutePermission); - } - - /** - * 获取ISV对应的角色 - * @return 返回ISV角色信息,key:appId,value:角色code列表 - */ - public Map> getIsvRoleCode() { - Query query = new Query(); - List permIsvRoles = permIsvRoleMapper.list(query); - Map> appKeyRoleCodeMap = permIsvRoles.stream() - .map(permIsvRole -> { - IsvInfo isvInfo = isvInfoMapper.getById(permIsvRole.getIsvId()); - if (isvInfo == null) { - return null; - } - IsvRole isvRole = new IsvRole(); - isvRole.appKey = isvInfo.getAppKey(); - isvRole.roleCode = permIsvRole.getRoleCode(); - return isvRole; - }) - .filter(Objects::nonNull) - .collect(Collectors.groupingBy(IsvRole::getAppKey, - mapping(IsvRole::getRoleCode, toList())) - ); - return appKeyRoleCodeMap; - } - - /** - * 获取角色对应的路由 - * - * @param roleCodeList - * @return - */ - public List getRouteIdList(List roleCodeList) { - if (CollectionUtils.isEmpty(roleCodeList)) { - return Collections.emptyList(); - } - Query query = new Query(); - query.in("role_code", roleCodeList); - List rolePermissionList = permRolePermissionMapper.list(query); - return rolePermissionList.stream() - .map(PermRolePermission::getRouteId) - .sorted() - .collect(Collectors.toList()); - } - - @Override - public void process(ChannelMsg channelMsg) { - final IsvRoutePermission isvRoutePermission = channelMsg.toObject(IsvRoutePermission.class); - switch (channelMsg.getOperation()) { - case "reload": - log.info("重新加载路由权限信息,isvRoutePermission:{}", isvRoutePermission); - try { - load(); - } catch (Exception e) { - log.error("重新加载路由权限失败, channelMsg:{}", channelMsg, e); - } - break; - case "update": - log.info("更新ISV路由权限信息,isvRoutePermission:{}", isvRoutePermission); - update(isvRoutePermission); - break; - case "remove": - log.info("删除ISV路由权限信息,isvRoutePermission:{}", isvRoutePermission); - remove(isvRoutePermission.getAppKey()); - break; - default: - - } - } - - - @Data - static class IsvRole { - private String appKey; - private String roleCode; - } -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java deleted file mode 100644 index fa282ec6..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.gitee.fastmybatis.core.query.Query; -import com.gitee.sop.gateway.entity.ConfigLimit; -import com.gitee.sop.gateway.mapper.ConfigLimitMapper; -import com.gitee.sop.gatewaycommon.bean.ChannelMsg; -import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; -import com.gitee.sop.gatewaycommon.manager.DefaultLimitConfigManager; -import com.gitee.sop.gatewaycommon.util.MyBeanUtil; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - * 限流配置管理 - * @author 六如 - */ -@Slf4j -@Service -public class DbLimitConfigManager extends DefaultLimitConfigManager { - - @Autowired - ConfigLimitMapper configLimitMapper; - - @Override - public void load(String serviceId) { - Query query = new Query(); - if (StringUtils.isNotBlank(serviceId)) { - query.eq("service_id", serviceId); - } - configLimitMapper.list(query) - .forEach(this::putVal); - - } - - protected void putVal(ConfigLimit object) { - ConfigLimitDto configLimitDto = new ConfigLimitDto(); - MyBeanUtil.copyPropertiesIgnoreNull(object, configLimitDto); - this.update(configLimitDto); - } - - @Override - public void process(ChannelMsg channelMsg) { - final ConfigLimitDto configLimitDto = channelMsg.toObject(ConfigLimitDto.class); - switch (channelMsg.getOperation()) { - case "reload": - log.info("重新加载限流配置信息,configLimitDto:{}", configLimitDto); - load(configLimitDto.getServiceId()); - break; - case "update": - log.info("更新限流配置信息,configLimitDto:{}", configLimitDto); - update(configLimitDto); - break; - default: - } - } - - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbMonitorInfoManager.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbMonitorInfoManager.java deleted file mode 100644 index 8c19c788..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbMonitorInfoManager.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.gitee.sop.gateway.mapper.MonitorInfoErrorMapper; -import com.gitee.sop.gateway.mapper.MonitorInfoMapper; -import com.gitee.sop.gatewaycommon.monitor.MonitorDTO; -import com.gitee.sop.gatewaycommon.monitor.MonitorErrorMsg; -import com.gitee.sop.gatewaycommon.monitor.RouteErrorCount; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * @author 六如 - */ -@Service -public class DbMonitorInfoManager { - - @Autowired - private MonitorInfoMapper monitorInfoMapper; - - @Autowired - private MonitorInfoErrorMapper monitorInfoErrorMapper; - - @Value("${sop.monitor.error-count-capacity:50}") - int limitCount; - - public void saveMonitorInfoBatch(List list) { - if (CollectionUtils.isEmpty(list)) { - return; - } - monitorInfoMapper.saveMonitorInfoBatch(list); - this.saveMonitorInfoErrorBatch(list); - } - - private void saveMonitorInfoErrorBatch(List list) { - List routeErrorCounts = monitorInfoErrorMapper.listRouteErrorCountAll(); - // 路由id对应的错误次数,key:routeId,value:错误次数 - Map routeErrorCountsMap = routeErrorCounts.stream() - .collect(Collectors.toMap(RouteErrorCount::getRouteId, RouteErrorCount::getCount)); - List monitorErrorMsgList = list.stream() - .filter(monitorDTO -> CollectionUtils.isNotEmpty(monitorDTO.getErrorMsgList())) - .flatMap(monitorDTO -> { - int limit = limitCount - routeErrorCountsMap.getOrDefault(monitorDTO.getRouteId(), 0); - // 容量已满 - if (limit <= 0) { - return null; - } - // 截取剩余 - return monitorDTO.getErrorMsgList().stream().limit(limit); - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - if (CollectionUtils.isNotEmpty(monitorErrorMsgList)) { - monitorInfoErrorMapper.saveMonitorInfoErrorBatch(monitorErrorMsgList); - } - } - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java deleted file mode 100644 index 69053af4..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.gitee.sop.gateway.mapper.ConfigRouteMapper; -import com.gitee.sop.gatewaycommon.bean.ChannelMsg; -import com.gitee.sop.gatewaycommon.bean.RouteConfig; -import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * @author 六如 - */ -@Slf4j -@Service -public class DbRouteConfigManager extends DefaultRouteConfigManager { - - @Autowired - private ConfigRouteMapper configRouteMapper; - - @Autowired - private Environment environment; - - @Override - public void load(String serviceId) { - List routeConfigs = StringUtils.isBlank(serviceId) ? configRouteMapper.listAllRouteConfig() - : configRouteMapper.listRouteConfig(serviceId); - routeConfigs.forEach(this::save); - } - - @Override - public void process(ChannelMsg channelMsg) { - final RouteConfig routeConfig = channelMsg.toObject(RouteConfig.class); - switch (channelMsg.getOperation()) { - case "reload": - log.info("重新加载路由配置信息,routeConfigDto:{}", routeConfig); - load(null); - break; - case "update": - log.info("更新路由配置信息,routeConfigDto:{}", routeConfig); - update(routeConfig); - break; - default: - } - } - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java deleted file mode 100644 index d4f69e70..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.gitee.sop.gateway.manager; - -import com.alibaba.fastjson.JSON; -import com.gitee.fastmybatis.core.query.Query; -import com.gitee.sop.gateway.entity.ConfigServiceRoute; -import com.gitee.sop.gateway.mapper.ConfigServiceRouteMapper; -import com.gitee.sop.gateway.mapper.SystemLockMapper; -import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; -import com.gitee.sop.gatewaycommon.route.RoutesProcessor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author 六如 - */ -@Slf4j -@Component -public class DbRoutesProcessor implements RoutesProcessor { - - @Autowired - private ConfigServiceRouteMapper configServiceRouteMapper; - - @Autowired - private SystemLockMapper systemLockMapper; - - @Override - public void removeAllRoutes(String serviceId) { - // 删除serviceId下所有的路由 - Query delServiceQuery = new Query().eq("service_id", serviceId); - configServiceRouteMapper.deleteByQuery(delServiceQuery); - } - - @Transactional(rollbackFor = Exception.class) - @Override - public synchronized void saveRoutes(ServiceRouteInfo serviceRouteInfo, InstanceDefinition instance) { - // 抢锁,没抢到阻塞在这里 - systemLockMapper.lock(); - String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")); - int result = systemLockMapper.insert(time + serviceRouteInfo.getMd5()); - // 抢到锁,插入失败,表示其它实例已经处理完毕,这里直接返回 - if (result == 0) { - return; - } - log.info("保存路由信息到数据库,instance: {}", instance); - String serviceId = serviceRouteInfo.getServiceId(); - List configServiceRoutes = serviceRouteInfo - .getRouteDefinitionList() - .parallelStream() - .map(routeDefinition -> { - ConfigServiceRoute configServiceRoute = new ConfigServiceRoute(); - configServiceRoute.setId(routeDefinition.getId()); - configServiceRoute.setName(routeDefinition.getName()); - configServiceRoute.setVersion(routeDefinition.getVersion()); - configServiceRoute.setUri(routeDefinition.getUri()); - configServiceRoute.setPath(routeDefinition.getPath()); - configServiceRoute.setFilters(JSON.toJSONString(routeDefinition.getFilters())); - configServiceRoute.setPredicates(JSON.toJSONString(routeDefinition.getPredicates())); - configServiceRoute.setIgnoreValidate((byte) routeDefinition.getIgnoreValidate()); - configServiceRoute.setMergeResult((byte) routeDefinition.getMergeResult()); - configServiceRoute.setStatus((byte) routeDefinition.getStatus()); - configServiceRoute.setPermission((byte) routeDefinition.getPermission()); - configServiceRoute.setOrderIndex(routeDefinition.getOrder()); - configServiceRoute.setNeedToken((byte)routeDefinition.getNeedToken()); - configServiceRoute.setServiceId(serviceId); - return configServiceRoute; - }) - .collect(Collectors.toList()); - - // 删除serviceId下所有的路由 - this.removeAllRoutes(serviceId); - - if (CollectionUtils.isNotEmpty(configServiceRoutes)) { - // 批量保存 - configServiceRouteMapper.saveBatch(configServiceRoutes); - } - } -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigGrayInstanceMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigGrayInstanceMapper.java deleted file mode 100644 index 8aaf65d1..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigGrayInstanceMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.ConfigGrayInstance; - - -/** - * @author 六如 - */ -public interface ConfigGrayInstanceMapper extends CrudMapper { -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigGrayMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigGrayMapper.java deleted file mode 100644 index c78d20d4..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigGrayMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.ConfigGray; - - -/** - * @author 六如 - */ -public interface ConfigGrayMapper extends CrudMapper { -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigLimitMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigLimitMapper.java deleted file mode 100644 index 3fa93cf9..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigLimitMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.ConfigLimit; - - -/** - * @author 六如 - */ -public interface ConfigLimitMapper extends CrudMapper { -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigRouteBaseMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigRouteBaseMapper.java deleted file mode 100644 index 28ce284f..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigRouteBaseMapper.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.ConfigRouteBase; - - -/** - * @author 六如 - */ -public interface ConfigRouteBaseMapper extends CrudMapper { - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigRouteMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigRouteMapper.java deleted file mode 100644 index dbbc3a5a..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigRouteMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.sop.gatewaycommon.bean.RouteConfig; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; - -import java.util.List; - -/** - * @author 六如 - */ -@Mapper -public interface ConfigRouteMapper { - - @Select("SELECT t.id AS routeId, t2.status " + - "FROM config_service_route t " + - "LEFT JOIN config_route_base t2 ON t.id=t2.route_id " + - "WHERE t.service_id=#{serviceId}") - List listRouteConfig(@Param("serviceId") String serviceId); - - @Select("SELECT t.id AS routeId, t2.status " + - "FROM config_service_route t " + - "LEFT JOIN config_route_base t2 ON t.id=t2.route_id ") - List listAllRouteConfig(); - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigServiceRouteMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigServiceRouteMapper.java deleted file mode 100644 index b55a8923..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/ConfigServiceRouteMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.ConfigServiceRoute; - - -/** - * @author 六如 - */ -public interface ConfigServiceRouteMapper extends CrudMapper { -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/IPBlacklistMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/IPBlacklistMapper.java deleted file mode 100644 index f18a9c16..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/IPBlacklistMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Select; - -import java.util.List; - -/** - * IP黑名单 - * @author 六如 - */ -@Mapper -public interface IPBlacklistMapper { - - /** - * 获取所有IP - * @return - */ - @Select("SELECT ip FROM config_ip_blacklist") - List listAllIP(); - -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/IsvInfoMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/IsvInfoMapper.java deleted file mode 100644 index 2203bac7..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/IsvInfoMapper.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.IsvDetailDTO; -import com.gitee.sop.gateway.entity.IsvInfo; -import org.apache.ibatis.annotations.Select; - -import java.util.List; - - -/** - * @author 六如 - */ -public interface IsvInfoMapper extends CrudMapper { - - /** - * 获取所有的isv信息 - * @return 所有的isv信息 - */ - @Select("SELECT " + - " t.app_key appKey " + - " ,t.status " + - " ,t2.sign_type signType " + - " ,t2.secret " + - " ,t2.public_key_isv publicKeyIsv " + - " ,t2.private_key_platform privateKeyPlatform " + - "FROM isv_info t " + - "INNER JOIN isv_keys t2 ON t.app_key = t2.app_key") - List listIsvDetail(); -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoErrorMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoErrorMapper.java deleted file mode 100644 index 5e5fea47..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoErrorMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.MonitorInfoError; -import com.gitee.sop.gatewaycommon.monitor.MonitorErrorMsg; -import com.gitee.sop.gatewaycommon.monitor.RouteErrorCount; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; -import org.apache.ibatis.annotations.Update; - -import java.util.List; - - -/** - * @author 六如 - */ -public interface MonitorInfoErrorMapper extends CrudMapper { - - @Update("UPDATE monitor_info_error " + - "SET is_deleted=0 " + - ",count=count + 1 " + - "WHERE route_id=#{routeId} AND error_id=#{errorId}") - int updateError(@Param("routeId") String routeId,@Param("errorId") String errorId); - - int saveMonitorInfoErrorBatch(@Param("list") List list); - - @Select("SELECT route_id routeId, count(*) `count` FROM monitor_info_error \n" + - "WHERE is_deleted=0 \n" + - "GROUP BY route_id") - List listRouteErrorCount(); - - @Select("SELECT route_id routeId, count(*) `count` FROM monitor_info_error \n" + - "WHERE is_deleted=0 \n" + - "GROUP BY route_id") - List listRouteErrorCountAll(); -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoMapper.java deleted file mode 100644 index 05e9568c..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/MonitorInfoMapper.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.MonitorInfo; -import com.gitee.sop.gatewaycommon.monitor.MonitorDTO; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Update; - -import java.util.List; - - -/** - * @author 六如 - */ -public interface MonitorInfoMapper extends CrudMapper { - - /** - * 更新监控状态 - * - * @return 返回影响行数 - */ - @Update("UPDATE monitor_info " + - "set max_time=case when max_time < #{maxTime} then #{maxTime} else max_time end " + - ",min_time=case when min_time > #{minTime} then #{minTime} else min_time end " + - ",total_request_count=total_request_count + #{totalRequestCount} " + - ",total_time=total_time + #{totalTime} " + - ",success_count=success_count + #{successCount} " + - ",error_count=error_count + #{errorCount} " + - "where route_id=#{routeId}") - int updateMonitorInfo(MonitorDTO monitorDTO); - - - /** - * 批量插入监控数据 - * @param list 监控数据 - * @return 返回影响行数 - */ - int saveMonitorInfoBatch(@Param("list") List list); -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/PermIsvRoleMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/PermIsvRoleMapper.java deleted file mode 100644 index 5380cfaf..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/PermIsvRoleMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.PermIsvRole; - - -/** - * @author 六如 - */ -public interface PermIsvRoleMapper extends CrudMapper { -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/PermRolePermissionMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/PermRolePermissionMapper.java deleted file mode 100644 index 3dbd6154..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/PermRolePermissionMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import com.gitee.fastmybatis.core.mapper.CrudMapper; -import com.gitee.sop.gateway.entity.PermRolePermission; - - -/** - * @author 六如 - */ -public interface PermRolePermissionMapper extends CrudMapper { -} diff --git a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/SystemLockMapper.java b/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/SystemLockMapper.java deleted file mode 100644 index d1978162..00000000 --- a/sop-gateway_old/src/main/java/com/gitee/sop/gateway/mapper/SystemLockMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.sop.gateway.mapper; - -import org.apache.ibatis.annotations.Insert; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.ResultType; -import org.apache.ibatis.annotations.Select; - -/** - * @author 六如 - */ -@Mapper -public interface SystemLockMapper { - - /** - * 插入唯一值 - * @param content 唯一值 - * @return 1:返回成功,0:插入失败 - */ - @Insert("INSERT IGNORE INTO system_lock(content) VALUES (#{content})") - @ResultType(int.class) - int insert(@Param("content") String content); - - @Select("SELECT id FROM system_lock WHERE id=1 FOR UPDATE") - @ResultType(long.class) - long lock(); -} diff --git a/sop-gateway_old/src/main/resources/META-INF/gateway.properties b/sop-gateway_old/src/main/resources/META-INF/gateway.properties deleted file mode 100644 index 7da11c4d..00000000 --- a/sop-gateway_old/src/main/resources/META-INF/gateway.properties +++ /dev/null @@ -1,47 +0,0 @@ -# \u56FA\u5B9A\u4E0D\u53D8\uFF0C\u4E0D\u80FD\u6539 -spring.application.name=sop-gateway -# \u4E0D\u7528\u6539\uFF0C\u5982\u679C\u8981\u6539\uFF0C\u8BF7\u5168\u5C40\u66FF\u6362\u4FEE\u6539 -sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC - -# \u6587\u4EF6\u4E0A\u4F20\u914D\u7F6E -spring.servlet.multipart.enabled=true -spring.servlet.multipart.max-file-size=10MB -# \u4E0D\u9650\u5236gateway\u8BF7\u6C42\u4F53\u5927\u5C0F -spring.codec.max-in-memory-size=-1 - -# \u7F51\u5173\u5165\u53E3 -sop.gateway-index-path=/ - -# \u5F00\u542Frestful\u8BF7\u6C42\uFF0C\u9ED8\u8BA4\u5F00\u542F -sop.restful.enable=true - -# restful\u8BF7\u6C42\u524D\u7F00 -sop.restful.path=/rest - -# nacos cloud\u914D\u7F6E -spring.cloud.nacos.discovery.server-addr=${register.url} - -# eureka\u5730\u5740 -eureka.client.serviceUrl.defaultZone=${register.url} - -# \u6570\u636E\u5E93\u914D\u7F6E -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai -spring.datasource.username=${mysql.username} -spring.datasource.password=${mysql.password} - -#\u8FDE\u63A5\u6C60 -spring.datasource.hikari.pool-name=HikariCP -spring.datasource.hikari.max-lifetime=500000 - -spring.cloud.gateway.discovery.locator.lower-case-service-id=true -#spring.cloud.gateway.discovery.locator.enabled=true -# \u54CD\u5E94\u8D85\u65F6\uFF0C\u9ED8\u8BA410\u79D2\uFF0810000\uFF09 -spring.cloud.gateway.httpclient.response-timeout=10000 -spring.cloud.gateway.httpclient.pool.max-idle-time=60000 - -# \u4E0D\u7528\u6539 -mybatis.mapper-locations=classpath:mybatis/mapper/*.xml -mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create -mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified - diff --git a/sop-gateway_old/src/main/resources/META-INF/spring.factories b/sop-gateway_old/src/main/resources/META-INF/spring.factories deleted file mode 100644 index d2bfd76a..00000000 --- a/sop-gateway_old/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,4 +0,0 @@ -# 自定义自动配置类,如果有多个类,使用逗号(,)分隔,\正斜杠标示换行还可以读取到指定的类 -org.springframework.boot.env.EnvironmentPostProcessor=com.gitee.sop.gatewaycommon.config.SopGatewayEnvironmentPostProcessor -org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gitee.sop.gatewaycommon.config.SopGatewayAutoConfiguration,\ - com.gitee.sop.bridge.SopRegisterAutoConfiguration diff --git a/sop-gateway_old/src/main/resources/application-dev.properties b/sop-gateway_old/src/main/resources/application-dev.properties deleted file mode 100644 index 8afebce4..00000000 --- a/sop-gateway_old/src/main/resources/application-dev.properties +++ /dev/null @@ -1,18 +0,0 @@ -# \u66F4\u591A\u914D\u7F6E\uFF0C\u89C1\uFF1AMETA-INF/gateway.properties -server.port=8081 - -# mysql\u6570\u636E\u5E93\u914D\u7F6E -mysql.host=localhost:3306 -mysql.username=root -mysql.password=root - -# nacos\u6CE8\u518C\u4E2D\u5FC3\u5730\u5740 -register.url=127.0.0.1:8848 - -# \u4E0A\u4F20\u6587\u4EF6\u6700\u5927\u5BB9\u91CF\uFF0C\u9ED8\u8BA410MB -spring.servlet.multipart.max-file-size=10MB - -# \u54CD\u5E94\u8D85\u65F6\uFF0C\u9ED8\u8BA410\u79D2\uFF0810000\uFF09 -#spring.cloud.gateway.httpclient.response-timeout=10000 - -logging.level.com.gitee=debug diff --git a/sop-gateway_old/src/main/resources/application.properties b/sop-gateway_old/src/main/resources/application.properties deleted file mode 100644 index 257b3064..00000000 --- a/sop-gateway_old/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.profiles.active=dev \ No newline at end of file diff --git a/sop-gateway_old/src/main/resources/mybatis/mapper/MonitorInfoErrorMapper.xml b/sop-gateway_old/src/main/resources/mybatis/mapper/MonitorInfoErrorMapper.xml deleted file mode 100644 index 1655177b..00000000 --- a/sop-gateway_old/src/main/resources/mybatis/mapper/MonitorInfoErrorMapper.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - INSERT INTO `monitor_info_error` ( - `error_id`, - `instance_id`, - `route_id`, - `error_msg`, - `error_status`, - `count`) - VALUES - - (#{data.errorId}, - #{data.instanceId}, - #{data.routeId}, - #{data.errorMsg}, - #{data.errorStatus}, - #{data.count}) - - ON DUPLICATE KEY UPDATE - error_msg = VALUES(error_msg) - , error_status = VALUES(error_status) - , `count`= `count` + VALUES(count) - , is_deleted = 0 - - - diff --git a/sop-gateway_old/src/main/resources/mybatis/mapper/MonitorInfoMapper.xml b/sop-gateway_old/src/main/resources/mybatis/mapper/MonitorInfoMapper.xml deleted file mode 100644 index b485dbf0..00000000 --- a/sop-gateway_old/src/main/resources/mybatis/mapper/MonitorInfoMapper.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - INSERT INTO `monitor_info` ( - `route_id`, - `name`, - `version`, - `service_id`, - `instance_id`, - `max_time`, - `min_time`, - `total_time`, - `total_request_count`, - `success_count`, - `error_count`) - VALUES - - (#{data.routeId}, - #{data.name}, - #{data.version}, - #{data.serviceId}, - #{data.instanceId}, - #{data.maxTime}, - #{data.minTime}, - #{data.totalTime}, - #{data.totalRequestCount}, - #{data.successCount}, - #{data.errorCount}) - - VALUES(min_time) then VALUES(min_time) else min_time end - ,total_time = total_time + VALUES(total_time) - ,total_request_count = total_request_count + VALUES(total_request_count) - ,success_count = success_count + VALUES(success_count) - ,error_count = error_count + VALUES(error_count) - ]]> - - diff --git a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/ExcludeTest.java b/sop-gateway_old/src/test/java/com/gitee/sop/gateway/ExcludeTest.java deleted file mode 100644 index 40c7da12..00000000 --- a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/ExcludeTest.java +++ /dev/null @@ -1,22 +0,0 @@ -//package com.gitee.sop.gateway; -// -//import junit.framework.TestCase; -//import org.apache.commons.lang3.StringUtils; -// -///** -// * @author 六如 -// */ -//public class ExcludeTest extends TestCase { -// public void testRegex() { -// String serviceId = "com.aaa.bbb.story-service"; -// String sopServiceExcludeRegex = "com\\..*;story\\-.*"; -// if (StringUtils.isNotBlank(sopServiceExcludeRegex)) { -// String[] regexArr = sopServiceExcludeRegex.split(";"); -// for (String regex : regexArr) { -// if (serviceId.matches(regex)) { -// System.out.println("111"); -// } -// } -// } -// } -//} diff --git a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/RoundRobinTest.java b/sop-gateway_old/src/test/java/com/gitee/sop/gateway/RoundRobinTest.java deleted file mode 100644 index 6cf4d51a..00000000 --- a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/RoundRobinTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.gitee.sop.gateway; - -import com.gitee.sop.gatewaycommon.util.LoadBalanceUtil; -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * 轮询选择一台机器。 - * - * @author 六如 - */ -public class RoundRobinTest extends TestCase { - - public void testDo() { - String serviceId = "order-service"; - List serverList = new ArrayList<>(Arrays.asList("server1", "server2", "server3")); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - } - - public void testAdd() { - String serviceId = "order-service"; - List serverList = new ArrayList<>(Arrays.asList("server1", "server2", "server3")); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - // 中途添加一个服务器 - serverList.add("server4"); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - } - - public void testRemove() { - String serviceId = "order-service"; - List serverList = new ArrayList<>(Arrays.asList("server1", "server2", "server3")); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - // 中途减少一台服务器 - serverList.remove(2); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - System.out.println(chooseRoundRobinServer(serviceId, serverList)); - } - - /** - * 轮询选择一台机器。 - * 假设有N台服务器:S = {S1, S2, …, Sn},一个指示变量i表示上一次选择的服务器ID。变量i被初始化为N-1。 - * - * @param serviceId serviceId - * @param servers 服务器列表 - * @return 返回一台服务器实例 - */ - private T chooseRoundRobinServer(String serviceId, List servers) { - return LoadBalanceUtil.chooseByRoundRobin(serviceId, servers); - } -} diff --git a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/SopGatewayApplicationTests.java b/sop-gateway_old/src/test/java/com/gitee/sop/gateway/SopGatewayApplicationTests.java deleted file mode 100644 index 8db99012..00000000 --- a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/SopGatewayApplicationTests.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.sop.gateway; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class SopGatewayApplicationTests { - - @Test - public void contextLoads() { - } - -} - diff --git a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/UrlPatternTest.java b/sop-gateway_old/src/test/java/com/gitee/sop/gateway/UrlPatternTest.java deleted file mode 100644 index 39440a61..00000000 --- a/sop-gateway_old/src/test/java/com/gitee/sop/gateway/UrlPatternTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.sop.gateway; - -import junit.framework.TestCase; -import org.junit.Assert; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; - -/** - * @author 六如 - */ -public class UrlPatternTest extends TestCase { - - private PathMatcher pathMatcher = new AntPathMatcher(); - - public void testA() { - Assert.assertTrue(match("/food/get/{id}", "/food/get/2")); - Assert.assertTrue(match("/food/get/{id}1.0", "/food/get/21.0")); - } - - /** - * @param pattern food/get/{id} - * @param lookupPath /food/get/2 - * - * @return - */ - public boolean match(String pattern, String lookupPath) { - return this.pathMatcher.match(pattern, lookupPath); - } - -} diff --git a/sop-website/pom.xml b/sop-website/pom.xml deleted file mode 100644 index 8c357f15..00000000 --- a/sop-website/pom.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - com.gitee.sop - sop-parent - 5.0.0-SNAPSHOT - ../pom.xml - - - 4.0.0 - sop-website - pom - - - sop-website-server - - diff --git a/sop-website/sop-portal-vue/README.md b/sop-website/sop-portal-vue/README.md deleted file mode 100644 index d24fe6c6..00000000 --- a/sop-website/sop-portal-vue/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# 门户网站(推荐) - -基于文档:https://www.vuepress.cn/ - -- 初始化 - -npm install - -- 启动 - -npm run docs:dev - -- 部署 - -npm run docs:build - -结果在:`docs/.vuepress/dist` - -将`dist`文件夹放到静态服务器 \ No newline at end of file diff --git a/sop-website/sop-portal-vue/docs/.vuepress/config.js b/sop-website/sop-portal-vue/docs/.vuepress/config.js deleted file mode 100644 index 1391948a..00000000 --- a/sop-website/sop-portal-vue/docs/.vuepress/config.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = { - locales: { - // 键名是该语言所属的子路径 - // 作为特例,默认语言可以使用 '/' 作为其路径。 - '/': { - lang: 'zh-CN', // 将会被设置为 的 lang 属性 - title: 'XX开放平台', - description: 'XX开放平台' - }, - // '/zh/': { - // lang: 'zh-CN', - // title: 'VuePress', - // description: 'Vue 驱动的静态网站生成器' - // } - }, - // 主题配置 - themeConfig: { - logo: '/assets/images/logo.png', - search: false, - searchMaxSuggestions: 10, - // 导航按钮配置 - nav: [ - { text: '登录', link: 'http://localhost:8083/index.html#/login' }, - { text: '免费注册', link: 'http://localhost:8083/index.html#/isvReg' }, - ], - }, - // 派生主题,https://www.vuepress.cn/theme/inheritance.html#%E6%A6%82%E5%BF%B5 - extend: '@vuepress/theme-default', - // 插件 - plugins: ['@vuepress/back-to-top'] -} diff --git a/sop-website/sop-portal-vue/docs/.vuepress/public/assets/images/hero.jpeg b/sop-website/sop-portal-vue/docs/.vuepress/public/assets/images/hero.jpeg deleted file mode 100644 index 99334048..00000000 Binary files a/sop-website/sop-portal-vue/docs/.vuepress/public/assets/images/hero.jpeg and /dev/null differ diff --git a/sop-website/sop-portal-vue/docs/.vuepress/public/assets/images/logo.png b/sop-website/sop-portal-vue/docs/.vuepress/public/assets/images/logo.png deleted file mode 100644 index b201d8c0..00000000 Binary files a/sop-website/sop-portal-vue/docs/.vuepress/public/assets/images/logo.png and /dev/null differ diff --git a/sop-website/sop-portal-vue/docs/.vuepress/public/favicon.ico b/sop-website/sop-portal-vue/docs/.vuepress/public/favicon.ico deleted file mode 100644 index 34b63ac6..00000000 Binary files a/sop-website/sop-portal-vue/docs/.vuepress/public/favicon.ico and /dev/null differ diff --git a/sop-website/sop-portal-vue/docs/.vuepress/styles/palette.styl b/sop-website/sop-portal-vue/docs/.vuepress/styles/palette.styl deleted file mode 100644 index f4615ea8..00000000 --- a/sop-website/sop-portal-vue/docs/.vuepress/styles/palette.styl +++ /dev/null @@ -1,21 +0,0 @@ -// 这里修改主题颜色,详见:https://www.vuepress.cn/config/#palette-styl -// 颜色 -$accentColor = #409EFF -$textColor = #2c3e50 -$borderColor = #eaecef -$codeBgColor = #282c34 -$arrowBgColor = #ccc -$badgeTipColor = #42b983 -$badgeWarningColor = darken(#ffe564, 35%) -$badgeErrorColor = #DA5961 - -// 布局 -$navbarHeight = 3.6rem -$sidebarWidth = 20rem -$contentWidth = 740px -$homePageWidth = 960px - -// 响应式变化点 -$MQNarrow = 959px -$MQMobile = 719px -$MQMobileNarrow = 419px diff --git a/sop-website/sop-portal-vue/docs/.vuepress/theme/components/Footer.vue b/sop-website/sop-portal-vue/docs/.vuepress/theme/components/Footer.vue deleted file mode 100644 index a1aa9840..00000000 --- a/sop-website/sop-portal-vue/docs/.vuepress/theme/components/Footer.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/sop-website/sop-portal-vue/docs/.vuepress/theme/components/Home.vue b/sop-website/sop-portal-vue/docs/.vuepress/theme/components/Home.vue deleted file mode 100644 index 4cc8fd19..00000000 --- a/sop-website/sop-portal-vue/docs/.vuepress/theme/components/Home.vue +++ /dev/null @@ -1,171 +0,0 @@ - - - - - diff --git a/sop-website/sop-portal-vue/docs/.vuepress/theme/index.js b/sop-website/sop-portal-vue/docs/.vuepress/theme/index.js deleted file mode 100644 index bd8dd662..00000000 --- a/sop-website/sop-portal-vue/docs/.vuepress/theme/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - // 继承默认主题,https://www.vuepress.cn/theme/inheritance.html#%E5%8A%A8%E6%9C%BA - extend: '@vuepress/theme-default' -} diff --git a/sop-website/sop-portal-vue/docs/.vuepress/theme/util/index.js b/sop-website/sop-portal-vue/docs/.vuepress/theme/util/index.js deleted file mode 100644 index 92fcd3b3..00000000 --- a/sop-website/sop-portal-vue/docs/.vuepress/theme/util/index.js +++ /dev/null @@ -1,244 +0,0 @@ -export const hashRE = /#.*$/ -export const extRE = /\.(md|html)$/ -export const endingSlashRE = /\/$/ -export const outboundRE = /^[a-z]+:/i - -export function normalize (path) { - return decodeURI(path) - .replace(hashRE, '') - .replace(extRE, '') -} - -export function getHash (path) { - const match = path.match(hashRE) - if (match) { - return match[0] - } -} - -export function isExternal (path) { - return outboundRE.test(path) -} - -export function isMailto (path) { - return /^mailto:/.test(path) -} - -export function isTel (path) { - return /^tel:/.test(path) -} - -export function ensureExt (path) { - if (isExternal(path)) { - return path - } - const hashMatch = path.match(hashRE) - const hash = hashMatch ? hashMatch[0] : '' - const normalized = normalize(path) - - if (endingSlashRE.test(normalized)) { - return path - } - return normalized + '.html' + hash -} - -export function isActive (route, path) { - const routeHash = decodeURIComponent(route.hash) - const linkHash = getHash(path) - if (linkHash && routeHash !== linkHash) { - return false - } - const routePath = normalize(route.path) - const pagePath = normalize(path) - return routePath === pagePath -} - -export function resolvePage (pages, rawPath, base) { - if (isExternal(rawPath)) { - return { - type: 'external', - path: rawPath - } - } - if (base) { - rawPath = resolvePath(rawPath, base) - } - const path = normalize(rawPath) - for (let i = 0; i < pages.length; i++) { - if (normalize(pages[i].regularPath) === path) { - return Object.assign({}, pages[i], { - type: 'page', - path: ensureExt(pages[i].path) - }) - } - } - console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`) - return {} -} - -function resolvePath (relative, base, append) { - const firstChar = relative.charAt(0) - if (firstChar === '/') { - return relative - } - - if (firstChar === '?' || firstChar === '#') { - return base + relative - } - - const stack = base.split('/') - - // remove trailing segment if: - // - not appending - // - appending to trailing slash (last segment is empty) - if (!append || !stack[stack.length - 1]) { - stack.pop() - } - - // resolve relative path - const segments = relative.replace(/^\//, '').split('/') - for (let i = 0; i < segments.length; i++) { - const segment = segments[i] - if (segment === '..') { - stack.pop() - } else if (segment !== '.') { - stack.push(segment) - } - } - - // ensure leading slash - if (stack[0] !== '') { - stack.unshift('') - } - - return stack.join('/') -} - -/** - * @param { Page } page - * @param { string } regularPath - * @param { SiteData } site - * @param { string } localePath - * @returns { SidebarGroup } - */ -export function resolveSidebarItems (page, regularPath, site, localePath) { - const { pages, themeConfig } = site - - const localeConfig = localePath && themeConfig.locales - ? themeConfig.locales[localePath] || themeConfig - : themeConfig - - const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar - if (pageSidebarConfig === 'auto') { - return resolveHeaders(page) - } - - const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar - if (!sidebarConfig) { - return [] - } else { - const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig) - if (config === 'auto') { - return resolveHeaders(page) - } - return config - ? config.map(item => resolveItem(item, pages, base)) - : [] - } -} - -/** - * @param { Page } page - * @returns { SidebarGroup } - */ -function resolveHeaders (page) { - const headers = groupHeaders(page.headers || []) - return [{ - type: 'group', - collapsable: false, - title: page.title, - path: null, - children: headers.map(h => ({ - type: 'auto', - title: h.title, - basePath: page.path, - path: page.path + '#' + h.slug, - children: h.children || [] - })) - }] -} - -export function groupHeaders (headers) { - // group h3s under h2 - headers = headers.map(h => Object.assign({}, h)) - let lastH2 - headers.forEach(h => { - if (h.level === 2) { - lastH2 = h - } else if (lastH2) { - (lastH2.children || (lastH2.children = [])).push(h) - } - }) - return headers.filter(h => h.level === 2) -} - -export function resolveNavLinkItem (linkItem) { - return Object.assign(linkItem, { - type: linkItem.items && linkItem.items.length ? 'links' : 'link' - }) -} - -/** - * @param { Route } route - * @param { Array | Array | [link: string]: SidebarConfig } config - * @returns { base: string, config: SidebarConfig } - */ -export function resolveMatchingConfig (regularPath, config) { - if (Array.isArray(config)) { - return { - base: '/', - config: config - } - } - for (const base in config) { - if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) { - return { - base, - config: config[base] - } - } - } - return {} -} - -function ensureEndingSlash (path) { - return /(\.html|\/)$/.test(path) - ? path - : path + '/' -} - -function resolveItem (item, pages, base, groupDepth = 1) { - if (typeof item === 'string') { - return resolvePage(pages, item, base) - } else if (Array.isArray(item)) { - return Object.assign(resolvePage(pages, item[0], base), { - title: item[1] - }) - } else { - const children = item.children || [] - if (children.length === 0 && item.path) { - return Object.assign(resolvePage(pages, item.path, base), { - title: item.title - }) - } - return { - type: 'group', - path: item.path, - title: item.title, - sidebarDepth: item.sidebarDepth, - initialOpenGroupIndex: item.initialOpenGroupIndex, - children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)), - collapsable: item.collapsable !== false - } - } -} diff --git a/sop-website/sop-portal-vue/docs/README.md b/sop-website/sop-portal-vue/docs/README.md deleted file mode 100644 index 4a3cce55..00000000 --- a/sop-website/sop-portal-vue/docs/README.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -home: true -heroImage: /assets/images/hero.jpeg -heroText: 开放平台 -tagline: 一句话描述你的平台。。 -actionText: 开始使用 -actionLink: http://localhost:8083/index.html#/login -features: -- title: 更快 - details: 开放接口更快。。 -- title: 更高 - details: 开放接口更高。。 -- title: 更强 - details: 开放接口更强。。 -footer: null -search: false ---- diff --git a/sop-website/sop-portal-vue/package.json b/sop-website/sop-portal-vue/package.json deleted file mode 100644 index 5e855633..00000000 --- a/sop-website/sop-portal-vue/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "open-portal", - "version": "1.0.0", - "main": "index.js", - "author": "tanghc", - "license": "MIT", - "devDependencies": { - "@vuepress/plugin-back-to-top": "^1.8.0", - "vuepress": "^1.8.0" - }, - "scripts": { - "docs:dev": "vuepress dev docs", - "docs:build": "vuepress build docs" - } -} diff --git a/sop-website/sop-portal/.babelrc b/sop-website/sop-portal/.babelrc deleted file mode 100644 index 42d28faf..00000000 --- a/sop-website/sop-portal/.babelrc +++ /dev/null @@ -1,22 +0,0 @@ -{ - "plugins": [ - "transform-decorators-legacy", - "transform-class-properties", - "transform-object-rest-spread", - "transform-object-assign", - [ - "transform-runtime", - { - "helpers": false, - "polyfill": false, - "regenerator": true, - "moduleName": "babel-runtime" - } - ] - ], - "presets": [ - "react", - "stage-0", - "es2015" - ] -} diff --git a/sop-website/sop-portal/.docsite b/sop-website/sop-portal/.docsite deleted file mode 100644 index e69de29b..00000000 diff --git a/sop-website/sop-portal/.eslintrc b/sop-website/sop-portal/.eslintrc deleted file mode 100644 index 0e70ce4c..00000000 --- a/sop-website/sop-portal/.eslintrc +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "eslint-config-ali/react", - "parser": "babel-eslint", - "env": { - "browser": true, - "node": true, - "mocha": true - }, - "globals": { - "AK_GLOBAL": true, - "dd": {}, - "_czc": {}, - "dplus": {}, - "salt": {}, - "_": true, - "homePageData": {}, - "Lang": {} - }, - "rules": { - "max-len": 0, - "new-cap": [2, { "newIsCap": true, "properties": false }], - "react/jsx-indent": 0, - "react/jsx-indent-props": 0, - "indent": 0, - "radix": ["error", "as-needed"], - "linebreak-style": [0 ,"error", "windows"] - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/.gitignore b/sop-website/sop-portal/.gitignore deleted file mode 100644 index 4996fc3b..00000000 --- a/sop-website/sop-portal/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -.DS_Store -node_modules/ -dist/ -build/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* -package-lock.json -tests/**/coverage/ - -# Editor directories and files -.idea -.vscode -*.suo -*.ntvs* -*.njsproj -*.sln diff --git a/sop-website/sop-portal/.nojekyll b/sop-website/sop-portal/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/sop-website/sop-portal/404.html b/sop-website/sop-portal/404.html deleted file mode 100644 index 2518eab7..00000000 --- a/sop-website/sop-portal/404.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - diff --git a/sop-website/sop-portal/README.md b/sop-website/sop-portal/README.md deleted file mode 100644 index 02307731..00000000 --- a/sop-website/sop-portal/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# 门户网站(不推荐,请使用sop-portal-vue) - -> 不推荐,请使用`sop-portal-vue` - -纯静态网站,作为开放平台基本介绍使用,可独立部署,logo制作:http://www.uugai.com/ - -# 初始化 - -安装node,建议安装11版本,12版本会有不兼容问题。 - -安装完毕执行: - -npm install - -# 启动 - -`docsite start`(windows系统执行:`npm run start`) - -# 打包 - -首先,需要配置下站点的根路径,修改site_config/site.js中的rootPath字段。规则如下: - -当部署根路径为/,则设置为''空字符串即可。 - -当部署根路径不为/,则设置为具体的根路径,注意需以/开头,但不能有尾/。 - -运行`build.sh` - -将dist内容上传到服务器 - diff --git a/sop-website/sop-portal/blog/en-us/blog1.md b/sop-website/sop-portal/blog/en-us/blog1.md deleted file mode 100644 index 506a3e14..00000000 --- a/sop-website/sop-portal/blog/en-us/blog1.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -key1: hello -key2: world ---- - -# blog1 - -it supports the resolution of meta data,the text between `---`(at least three`-`)written in the format of `key:value`,will be resolved to `md_json/blog.json`,`filename` and `__html` are preserved. - -filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text \ No newline at end of file diff --git a/sop-website/sop-portal/blog/zh-cn/blog1.md b/sop-website/sop-portal/blog/zh-cn/blog1.md deleted file mode 100644 index 9537e4b0..00000000 --- a/sop-website/sop-portal/blog/zh-cn/blog1.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -key1: hello -key2: world ---- - -# 博客1 - -支持元数据的解析,`---`(至少三个`-`)开头之间的数据按照`key:value`的形式,最终会被解析到`md_json/blog.json`中,其中`filename`和`__html`为保留字段,请勿使用。 - -博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充 \ No newline at end of file diff --git a/sop-website/sop-portal/build.sh b/sop-website/sop-portal/build.sh deleted file mode 100644 index 8517b27e..00000000 --- a/sop-website/sop-portal/build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -echo "开始构建..." - -dist_dir="dist" - -if [ ! -d "$dist_dir" ]; then - mkdir $dist_dir -fi - -docsite build - -echo "复制文件到${dist_dir}目录" - -rm -rf $dist_dir/* -cp -r zh-cn/* $dist_dir -cp -r build $dist_dir/build -cp -r img $dist_dir/img - -echo "构建完毕" diff --git a/sop-website/sop-portal/docs/en-us/demo1.md b/sop-website/sop-portal/docs/en-us/demo1.md deleted file mode 100644 index d3baa456..00000000 --- a/sop-website/sop-portal/docs/en-us/demo1.md +++ /dev/null @@ -1,249 +0,0 @@ -# example of footnote - -this is a demo of the usage of footnote. -this is footnote1 [^1]。click it and see what will happen。 - -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. - - - -[^1]: this is the explanation of footnote \ No newline at end of file diff --git a/sop-website/sop-portal/docs/en-us/demo2.md b/sop-website/sop-portal/docs/en-us/demo2.md deleted file mode 100644 index 5c626342..00000000 --- a/sop-website/sop-portal/docs/en-us/demo2.md +++ /dev/null @@ -1,16 +0,0 @@ -# demo for reference of md or image for relative path - -if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. -click here: [demo1.md](./demo1.md)。 - -the same is to image. -![](./img/brhtqqzh.jpeg) - -# demo for math typesetting(katex) - -$E = mc^2$ - -$$x = a_{1}^n + a_{2}^n + a_{3}^n$$ - -$$\sqrt[3]{X}$$ -$$\sqrt{5 - x}$$ \ No newline at end of file diff --git a/sop-website/sop-portal/docs/en-us/dir/demo3.md b/sop-website/sop-portal/docs/en-us/dir/demo3.md deleted file mode 100644 index a54d1405..00000000 --- a/sop-website/sop-portal/docs/en-us/dir/demo3.md +++ /dev/null @@ -1,7 +0,0 @@ -# demo for reference of md or image for relative path, across directory - -if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. -click here: [demo1.md](../demo1.md)。 - -the same is to image. -![](../img/brhtqqzh.jpeg) \ No newline at end of file diff --git a/sop-website/sop-portal/docs/en-us/img/brhtqqzh.jpeg b/sop-website/sop-portal/docs/en-us/img/brhtqqzh.jpeg deleted file mode 100644 index 854369b8..00000000 Binary files a/sop-website/sop-portal/docs/en-us/img/brhtqqzh.jpeg and /dev/null differ diff --git a/sop-website/sop-portal/docs/zh-cn/demo1.md b/sop-website/sop-portal/docs/zh-cn/demo1.md deleted file mode 100644 index ec856921..00000000 --- a/sop-website/sop-portal/docs/zh-cn/demo1.md +++ /dev/null @@ -1,401 +0,0 @@ -# 脚注的使用 - -这是一段示例文字,展示脚注的使用。 -这是脚注1 [^1]。点击脚注滚动至对应脚注处。 - -下面是一大段文字,填充开始。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 - - -[^1]: 这是脚注的说明 \ No newline at end of file diff --git a/sop-website/sop-portal/docs/zh-cn/demo2.md b/sop-website/sop-portal/docs/zh-cn/demo2.md deleted file mode 100644 index cae6a14b..00000000 --- a/sop-website/sop-portal/docs/zh-cn/demo2.md +++ /dev/null @@ -1,17 +0,0 @@ -# 文档之间跳转、图片引用的示例 - -文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 -点击跳转: [demo1.md](./demo1.md)。 - -图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 -![](./img/brhtqqzh.jpeg) - -# 数学公式的使用(katex) - -$E = mc^2$ - -$$x = a_{1}^n + a_{2}^n + a_{3}^n$$ - -$$\sqrt[3]{X}$$ -$$\sqrt{5 - x}$$ - diff --git a/sop-website/sop-portal/docs/zh-cn/dir/demo3.md b/sop-website/sop-portal/docs/zh-cn/dir/demo3.md deleted file mode 100644 index 9d0bddb9..00000000 --- a/sop-website/sop-portal/docs/zh-cn/dir/demo3.md +++ /dev/null @@ -1,8 +0,0 @@ -# 跨目录文档之间跳转、图片引用的示例 - -这是一段文字,将要跳转到demo1。 -文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 -点击跳转: [demo1.md](../demo1.md)。 - -图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 -![](../img/brhtqqzh.jpeg) \ No newline at end of file diff --git a/sop-website/sop-portal/docs/zh-cn/img/brhtqqzh.jpeg b/sop-website/sop-portal/docs/zh-cn/img/brhtqqzh.jpeg deleted file mode 100644 index 854369b8..00000000 Binary files a/sop-website/sop-portal/docs/zh-cn/img/brhtqqzh.jpeg and /dev/null differ diff --git a/sop-website/sop-portal/docsite.config.yml b/sop-website/sop-portal/docsite.config.yml deleted file mode 100644 index 69921ec2..00000000 --- a/sop-website/sop-portal/docsite.config.yml +++ /dev/null @@ -1,35 +0,0 @@ -pages: - # key is the dirname of pages in src/pages - home: - # 首页配置 - zh-cn: - title: 'XX开放平台' - keywords: '开放平台,Open,Api' - description: '一站式开放接口服务平台,可快速搭建起自己的开放平台' - # home config - en-us: - title: 'XX开放平台' - keywords: '开放平台,Open,Api' - description: '一站式开放接口服务平台,可快速搭建起自己的开放平台' - community: - # 社区页配置 - zh-cn: - title: '网页标签title' - keywords: '关键词1,关键词2' - description: '页面内容简介' - # community page config - en-us: - title: 'page title' - keywords: 'keyword1,keyword2' - description: 'page description' - blog: - # 博客列表页配置 - zh-cn: - title: '网页标签title' - keywords: '关键词1,关键词2' - description: '页面内容简介' - en-us: - # blog list page config - title: 'page title' - keywords: 'keyword1,keyword2' - description: 'page description' diff --git a/sop-website/sop-portal/en-us/blog/blog1.html b/sop-website/sop-portal/en-us/blog/blog1.html deleted file mode 100644 index de746ffb..00000000 --- a/sop-website/sop-portal/en-us/blog/blog1.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog1 - - - - -

blog1

-

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

-

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/blog/blog1.json b/sop-website/sop-portal/en-us/blog/blog1.json deleted file mode 100644 index 367933f5..00000000 --- a/sop-website/sop-portal/en-us/blog/blog1.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog1.md", - "__html": "

blog1

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", - "link": "/en-us/blog/blog1.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog2.html b/sop-website/sop-portal/en-us/blog/blog2.html deleted file mode 100644 index cd295cbe..00000000 --- a/sop-website/sop-portal/en-us/blog/blog2.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog2 - - - - -

blog2

-

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

-

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/blog/blog2.json b/sop-website/sop-portal/en-us/blog/blog2.json deleted file mode 100644 index 56fb8512..00000000 --- a/sop-website/sop-portal/en-us/blog/blog2.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog2.md", - "__html": "

blog2

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", - "link": "/en-us/blog/blog2.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog3.html b/sop-website/sop-portal/en-us/blog/blog3.html deleted file mode 100644 index abb7e217..00000000 --- a/sop-website/sop-portal/en-us/blog/blog3.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog3 - - - - -

blog3

-

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

-

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/blog/blog3.json b/sop-website/sop-portal/en-us/blog/blog3.json deleted file mode 100644 index 081163f9..00000000 --- a/sop-website/sop-portal/en-us/blog/blog3.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog3.md", - "__html": "

blog3

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", - "link": "/en-us/blog/blog3.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog4.html b/sop-website/sop-portal/en-us/blog/blog4.html deleted file mode 100644 index 6463eb29..00000000 --- a/sop-website/sop-portal/en-us/blog/blog4.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog4 - - - - -

blog4

-

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

-

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/blog/blog4.json b/sop-website/sop-portal/en-us/blog/blog4.json deleted file mode 100644 index c81ca9b9..00000000 --- a/sop-website/sop-portal/en-us/blog/blog4.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog4.md", - "__html": "

blog4

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", - "link": "/en-us/blog/blog4.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog5.html b/sop-website/sop-portal/en-us/blog/blog5.html deleted file mode 100644 index 532f4817..00000000 --- a/sop-website/sop-portal/en-us/blog/blog5.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog5 - - - - -

blog5

-

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

-

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/blog/blog5.json b/sop-website/sop-portal/en-us/blog/blog5.json deleted file mode 100644 index 9c18fd2d..00000000 --- a/sop-website/sop-portal/en-us/blog/blog5.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog5.md", - "__html": "

blog5

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", - "link": "/en-us/blog/blog5.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog6.html b/sop-website/sop-portal/en-us/blog/blog6.html deleted file mode 100644 index c56d2053..00000000 --- a/sop-website/sop-portal/en-us/blog/blog6.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog6 - - - - -

blog6

-

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

-

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/blog/blog6.json b/sop-website/sop-portal/en-us/blog/blog6.json deleted file mode 100644 index b6fae5ed..00000000 --- a/sop-website/sop-portal/en-us/blog/blog6.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog6.md", - "__html": "

blog6

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", - "link": "/en-us/blog/blog6.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog7.html b/sop-website/sop-portal/en-us/blog/blog7.html deleted file mode 100644 index 97652bf0..00000000 --- a/sop-website/sop-portal/en-us/blog/blog7.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog7 - - - - -

blog7

-

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

-

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/blog/blog7.json b/sop-website/sop-portal/en-us/blog/blog7.json deleted file mode 100644 index ceb0b04f..00000000 --- a/sop-website/sop-portal/en-us/blog/blog7.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog7.md", - "__html": "

blog7

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", - "link": "/en-us/blog/blog7.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/index.html b/sop-website/sop-portal/en-us/blog/index.html deleted file mode 100644 index 2cdcfd77..00000000 --- a/sop-website/sop-portal/en-us/blog/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - page title - - - - - - - - - - - diff --git a/sop-website/sop-portal/en-us/community/index.html b/sop-website/sop-portal/en-us/community/index.html deleted file mode 100644 index 3505c6e1..00000000 --- a/sop-website/sop-portal/en-us/community/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - page title - - - - -
Community

Events & News

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

Eco System

Eco1

Eco System1

Eco2

Eco System2

Talk To Us

Feel free to contact us via the following channel.

Contributor Guide

some description

Mailing List

this is the content

Issue

this is the content

Documents

this is the content

Pull Request

this is the content

- - - - - - diff --git a/sop-website/sop-portal/en-us/docs/demo1.html b/sop-website/sop-portal/en-us/docs/demo1.html deleted file mode 100644 index 813a80cc..00000000 --- a/sop-website/sop-portal/en-us/docs/demo1.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - - - - demo1 - - - - -
Documentation

example of footnote

-

this is a demo of the usage of footnote. -this is footnote1 [1]。click it and see what will happen。

-

this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text. -this is a long text.

-
-
-
    -
  1. this is the explanation of footnote ↩︎

    -
  2. -
-
-
- - - - - - diff --git a/sop-website/sop-portal/en-us/docs/demo1.json b/sop-website/sop-portal/en-us/docs/demo1.json deleted file mode 100644 index 623aadb9..00000000 --- a/sop-website/sop-portal/en-us/docs/demo1.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "filename": "demo1.md", - "__html": "

example of footnote

\n

this is a demo of the usage of footnote.\nthis is footnote1 [1]。click it and see what will happen。

\n

this is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.

\n
\n
\n
    \n
  1. this is the explanation of footnote ↩︎

    \n
  2. \n
\n
\n", - "link": "/en-us/docs/demo1.html", - "meta": {} -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/docs/demo2.html b/sop-website/sop-portal/en-us/docs/demo2.html deleted file mode 100644 index 28922559..00000000 --- a/sop-website/sop-portal/en-us/docs/demo2.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - demo2 - - - - -
Documentation

demo for reference of md or image for relative path

-

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. -click here: demo1.md

-

the same is to image. -

-

demo for math typesetting(katex)

-

E=mc2E = mc^2

-

x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n -

-

X3\sqrt[3]{X} -

-

5x\sqrt{5 - x} -

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/docs/demo2.json b/sop-website/sop-portal/en-us/docs/demo2.json deleted file mode 100644 index 5b03c4c8..00000000 --- a/sop-website/sop-portal/en-us/docs/demo2.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "filename": "demo2.md", - "__html": "

demo for reference of md or image for relative path

\n

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system.\nclick here: demo1.md

\n

the same is to image.\n\"\"

\n

demo for math typesetting(katex)

\n

E=mc2E = mc^2E=mc2

\n

x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n\nx=a1n+a2n+a3n

\n

X3\\sqrt[3]{X}\n3X

\n

5x\\sqrt{5 - x}\n5x

\n", - "link": "/en-us/docs/demo2.html", - "meta": {} -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/docs/dir/demo3.html b/sop-website/sop-portal/en-us/docs/dir/demo3.html deleted file mode 100644 index dd3f1a7a..00000000 --- a/sop-website/sop-portal/en-us/docs/dir/demo3.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - demo3 - - - - -
Documentation

demo for reference of md or image for relative path, across directory

-

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. -click here: demo1.md

-

the same is to image. -

-
- - - - - - diff --git a/sop-website/sop-portal/en-us/docs/dir/demo3.json b/sop-website/sop-portal/en-us/docs/dir/demo3.json deleted file mode 100644 index 6690dcef..00000000 --- a/sop-website/sop-portal/en-us/docs/dir/demo3.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "filename": "demo3.md", - "__html": "

demo for reference of md or image for relative path, across directory

\n

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system.\nclick here: demo1.md

\n

the same is to image.\n\"\"

\n", - "link": "/en-us/docs/dir/demo3.html", - "meta": {} -} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/index.html b/sop-website/sop-portal/en-us/index.html deleted file mode 100644 index e222e7c1..00000000 --- a/sop-website/sop-portal/en-us/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - XX开放平台 - - - - -

brandName

some description of product

Feature List

  • feature1

    feature description

  • feature2

    feature description

  • feature3

    feature description

  • feature4

    feature description

  • feature5

    feature description

  • feature6

    feature description

- - - - - - diff --git a/sop-website/sop-portal/gulpfile.js b/sop-website/sop-portal/gulpfile.js deleted file mode 100644 index bef72348..00000000 --- a/sop-website/sop-portal/gulpfile.js +++ /dev/null @@ -1,60 +0,0 @@ -require('babel-register')(); -const gulp = require('gulp'); -const gutil = require('gulp-util'); -const webpack = require('webpack'); -const opn = require('opn'); -const WebpackDevServer = require('webpack-dev-server'); -const siteConfig = require('./site_config/site').default; -const webpackConfig = require('./webpack.config.js'); - -const port = siteConfig.port || 8080; - -// The development server (the recommended option for development) -gulp.task('default', ['webpack-dev-server']); - -// Production build -gulp.task('build', ['webpack:build']); - -gulp.task('webpack-dev-server', () => { - // modify some webpack config options - const myConfig = Object.create(webpackConfig); - myConfig.plugins.push(new webpack.SourceMapDevToolPlugin({})); - // Start a webpack-dev-server - new WebpackDevServer(webpack(myConfig), { - publicPath: `http://127.0.0.1:${port}/build/`, - stats: { - colors: true, - }, - }).listen(port, '127.0.0.1', err => { - if (err) throw new gutil.PluginError('webpack-dev-server', err); - opn(`http://127.0.0.1:${port}/`); - gutil.log('[webpack-dev-server]', `http://127.0.0.1:${port}/webpack-dev-server/index.html`); - }); -}); - -gulp.task('webpack:build', callback => { - // modify some webpack config options - const myConfig = Object.create(webpackConfig); - myConfig.output.publicPath = `${siteConfig.rootPath}/build/`; - myConfig.plugins = myConfig.plugins.concat( - new webpack.DefinePlugin({ - 'process.env': { - // This has effect on the react lib size - NODE_ENV: JSON.stringify('production'), - }, - }), - new webpack.optimize.UglifyJsPlugin() - ); - - // run webpack - webpack(myConfig, (err, stats) => { - if (err) throw new gutil.PluginError('webpack:build', err); - gutil.log( - '[webpack:build]', - stats.toString({ - colors: true, - }) - ); - callback(); - }); -}); diff --git a/sop-website/sop-portal/img/alibaba.png b/sop-website/sop-portal/img/alibaba.png deleted file mode 100644 index 32f736b2..00000000 Binary files a/sop-website/sop-portal/img/alibaba.png and /dev/null differ diff --git a/sop-website/sop-portal/img/alibaba_hover.png b/sop-website/sop-portal/img/alibaba_hover.png deleted file mode 100644 index bbc81d0e..00000000 Binary files a/sop-website/sop-portal/img/alibaba_hover.png and /dev/null differ diff --git a/sop-website/sop-portal/img/architecture.png b/sop-website/sop-portal/img/architecture.png deleted file mode 100644 index d010dfb0..00000000 Binary files a/sop-website/sop-portal/img/architecture.png and /dev/null differ diff --git a/sop-website/sop-portal/img/brhtqqzh.jpeg b/sop-website/sop-portal/img/brhtqqzh.jpeg deleted file mode 100755 index df36b234..00000000 Binary files a/sop-website/sop-portal/img/brhtqqzh.jpeg and /dev/null differ diff --git a/sop-website/sop-portal/img/docsite.ico b/sop-website/sop-portal/img/docsite.ico deleted file mode 100644 index 224e8d20..00000000 Binary files a/sop-website/sop-portal/img/docsite.ico and /dev/null differ diff --git a/sop-website/sop-portal/img/documents.png b/sop-website/sop-portal/img/documents.png deleted file mode 100644 index 343c0d1b..00000000 Binary files a/sop-website/sop-portal/img/documents.png and /dev/null differ diff --git a/sop-website/sop-portal/img/favicon.ico b/sop-website/sop-portal/img/favicon.ico deleted file mode 100644 index 5d417587..00000000 Binary files a/sop-website/sop-portal/img/favicon.ico and /dev/null differ diff --git a/sop-website/sop-portal/img/feature_hogh.png b/sop-website/sop-portal/img/feature_hogh.png deleted file mode 100644 index e0b1a8b2..00000000 Binary files a/sop-website/sop-portal/img/feature_hogh.png and /dev/null differ diff --git a/sop-website/sop-portal/img/feature_loadbalances.png b/sop-website/sop-portal/img/feature_loadbalances.png deleted file mode 100644 index 05efb58b..00000000 Binary files a/sop-website/sop-portal/img/feature_loadbalances.png and /dev/null differ diff --git a/sop-website/sop-portal/img/feature_maintenance.png b/sop-website/sop-portal/img/feature_maintenance.png deleted file mode 100644 index f1ae0fdb..00000000 Binary files a/sop-website/sop-portal/img/feature_maintenance.png and /dev/null differ diff --git a/sop-website/sop-portal/img/feature_runtime.png b/sop-website/sop-portal/img/feature_runtime.png deleted file mode 100644 index c09b8627..00000000 Binary files a/sop-website/sop-portal/img/feature_runtime.png and /dev/null differ diff --git a/sop-website/sop-portal/img/feature_service.png b/sop-website/sop-portal/img/feature_service.png deleted file mode 100644 index bb5be229..00000000 Binary files a/sop-website/sop-portal/img/feature_service.png and /dev/null differ diff --git a/sop-website/sop-portal/img/feature_transpart.png b/sop-website/sop-portal/img/feature_transpart.png deleted file mode 100644 index bfea1349..00000000 Binary files a/sop-website/sop-portal/img/feature_transpart.png and /dev/null differ diff --git a/sop-website/sop-portal/img/home_1.png b/sop-website/sop-portal/img/home_1.png deleted file mode 100644 index 139a85c1..00000000 Binary files a/sop-website/sop-portal/img/home_1.png and /dev/null differ diff --git a/sop-website/sop-portal/img/home_2.png b/sop-website/sop-portal/img/home_2.png deleted file mode 100644 index d5e7cff7..00000000 Binary files a/sop-website/sop-portal/img/home_2.png and /dev/null differ diff --git a/sop-website/sop-portal/img/home_3.png b/sop-website/sop-portal/img/home_3.png deleted file mode 100644 index 528117c8..00000000 Binary files a/sop-website/sop-portal/img/home_3.png and /dev/null differ diff --git a/sop-website/sop-portal/img/home_5.png b/sop-website/sop-portal/img/home_5.png deleted file mode 100644 index 01969feb..00000000 Binary files a/sop-website/sop-portal/img/home_5.png and /dev/null differ diff --git a/sop-website/sop-portal/img/issue.png b/sop-website/sop-portal/img/issue.png deleted file mode 100644 index e1a026d9..00000000 Binary files a/sop-website/sop-portal/img/issue.png and /dev/null differ diff --git a/sop-website/sop-portal/img/logo.png b/sop-website/sop-portal/img/logo.png deleted file mode 100644 index b201d8c0..00000000 Binary files a/sop-website/sop-portal/img/logo.png and /dev/null differ diff --git a/sop-website/sop-portal/img/logo_old.png b/sop-website/sop-portal/img/logo_old.png deleted file mode 100644 index bf8cda70..00000000 Binary files a/sop-website/sop-portal/img/logo_old.png and /dev/null differ diff --git a/sop-website/sop-portal/img/logo_old_white.png b/sop-website/sop-portal/img/logo_old_white.png deleted file mode 100644 index a36bbc41..00000000 Binary files a/sop-website/sop-portal/img/logo_old_white.png and /dev/null differ diff --git a/sop-website/sop-portal/img/logo_white.png b/sop-website/sop-portal/img/logo_white.png deleted file mode 100644 index a09738f8..00000000 Binary files a/sop-website/sop-portal/img/logo_white.png and /dev/null differ diff --git a/sop-website/sop-portal/img/mailinglist.png b/sop-website/sop-portal/img/mailinglist.png deleted file mode 100644 index a219fe88..00000000 Binary files a/sop-website/sop-portal/img/mailinglist.png and /dev/null differ diff --git a/sop-website/sop-portal/img/mailinglist_hover.png b/sop-website/sop-portal/img/mailinglist_hover.png deleted file mode 100644 index 785705c9..00000000 Binary files a/sop-website/sop-portal/img/mailinglist_hover.png and /dev/null differ diff --git a/sop-website/sop-portal/img/pullrequest.png b/sop-website/sop-portal/img/pullrequest.png deleted file mode 100644 index fc05edfb..00000000 Binary files a/sop-website/sop-portal/img/pullrequest.png and /dev/null differ diff --git a/sop-website/sop-portal/img/quick_start.png b/sop-website/sop-portal/img/quick_start.png deleted file mode 100644 index 44b7069d..00000000 Binary files a/sop-website/sop-portal/img/quick_start.png and /dev/null differ diff --git a/sop-website/sop-portal/img/segmentfault.png b/sop-website/sop-portal/img/segmentfault.png deleted file mode 100644 index 09d2f3da..00000000 Binary files a/sop-website/sop-portal/img/segmentfault.png and /dev/null differ diff --git a/sop-website/sop-portal/img/segmentfault_hover.png b/sop-website/sop-portal/img/segmentfault_hover.png deleted file mode 100644 index 56707d02..00000000 Binary files a/sop-website/sop-portal/img/segmentfault_hover.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/arrow_down.png b/sop-website/sop-portal/img/system/arrow_down.png deleted file mode 100644 index 2924eb55..00000000 Binary files a/sop-website/sop-portal/img/system/arrow_down.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/arrow_right.png b/sop-website/sop-portal/img/system/arrow_right.png deleted file mode 100644 index 4555b12b..00000000 Binary files a/sop-website/sop-portal/img/system/arrow_right.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/blog.png b/sop-website/sop-portal/img/system/blog.png deleted file mode 100644 index 579f759d..00000000 Binary files a/sop-website/sop-portal/img/system/blog.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/community.png b/sop-website/sop-portal/img/system/community.png deleted file mode 100644 index 5836a4af..00000000 Binary files a/sop-website/sop-portal/img/system/community.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/docs.png b/sop-website/sop-portal/img/system/docs.png deleted file mode 100644 index f41db69e..00000000 Binary files a/sop-website/sop-portal/img/system/docs.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/docs_hover.png b/sop-website/sop-portal/img/system/docs_hover.png deleted file mode 100644 index 3568312e..00000000 Binary files a/sop-website/sop-portal/img/system/docs_hover.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/docs_normal.png b/sop-website/sop-portal/img/system/docs_normal.png deleted file mode 100644 index 4f31e506..00000000 Binary files a/sop-website/sop-portal/img/system/docs_normal.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/menu_gray.png b/sop-website/sop-portal/img/system/menu_gray.png deleted file mode 100644 index 9838a877..00000000 Binary files a/sop-website/sop-portal/img/system/menu_gray.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/menu_white.png b/sop-website/sop-portal/img/system/menu_white.png deleted file mode 100644 index 5943a141..00000000 Binary files a/sop-website/sop-portal/img/system/menu_white.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/next.png b/sop-website/sop-portal/img/system/next.png deleted file mode 100644 index d65f97d3..00000000 Binary files a/sop-website/sop-portal/img/system/next.png and /dev/null differ diff --git a/sop-website/sop-portal/img/system/prev.png b/sop-website/sop-portal/img/system/prev.png deleted file mode 100644 index a68639b5..00000000 Binary files a/sop-website/sop-portal/img/system/prev.png and /dev/null differ diff --git a/sop-website/sop-portal/img/users_alibaba.png b/sop-website/sop-portal/img/users_alibaba.png deleted file mode 100644 index 25c277d4..00000000 Binary files a/sop-website/sop-portal/img/users_alibaba.png and /dev/null differ diff --git a/sop-website/sop-portal/img/weibo.png b/sop-website/sop-portal/img/weibo.png deleted file mode 100644 index 89ffd326..00000000 Binary files a/sop-website/sop-portal/img/weibo.png and /dev/null differ diff --git a/sop-website/sop-portal/img/weibo_hover.png b/sop-website/sop-portal/img/weibo_hover.png deleted file mode 100644 index 9d800ff3..00000000 Binary files a/sop-website/sop-portal/img/weibo_hover.png and /dev/null differ diff --git a/sop-website/sop-portal/index.html b/sop-website/sop-portal/index.html deleted file mode 100644 index 2518eab7..00000000 --- a/sop-website/sop-portal/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - diff --git a/sop-website/sop-portal/md_json/blog.json b/sop-website/sop-portal/md_json/blog.json deleted file mode 100644 index 464d75c0..00000000 --- a/sop-website/sop-portal/md_json/blog.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "en-us": [ - { - "filename": "blog1.md", - "link": "/en-us/blog/blog1.html", - "meta": { - "key1": "hello", - "key2": "world" - } - } - ], - "zh-cn": [ - { - "filename": "blog1.md", - "link": "/zh-cn/blog/blog1.html", - "meta": { - "key1": "hello", - "key2": "world" - } - } - ] -} \ No newline at end of file diff --git a/sop-website/sop-portal/md_json/docs.json b/sop-website/sop-portal/md_json/docs.json deleted file mode 100644 index e00de912..00000000 --- a/sop-website/sop-portal/md_json/docs.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "en-us": [ - { - "filename": "demo1.md", - "link": "/en-us/docs/demo1.html", - "meta": {} - }, - { - "filename": "demo2.md", - "link": "/en-us/docs/demo2.html", - "meta": {} - }, - { - "filename": "demo3.md", - "link": "/en-us/docs/dir/demo3.html", - "meta": {} - } - ], - "zh-cn": [ - { - "filename": "demo1.md", - "link": "/zh-cn/docs/demo1.html", - "meta": {} - }, - { - "filename": "demo2.md", - "link": "/zh-cn/docs/demo2.html", - "meta": {} - }, - { - "filename": "demo3.md", - "link": "/zh-cn/docs/dir/demo3.html", - "meta": {} - } - ] -} \ No newline at end of file diff --git a/sop-website/sop-portal/package.json b/sop-website/sop-portal/package.json deleted file mode 100644 index 10f78e0d..00000000 --- a/sop-website/sop-portal/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "site", - "version": "0.0.1", - "private": true, - "scripts": { - "start": "docsite start", - "build": "docsite build" - }, - "devDependencies": { - "docsite": "^1.1.0", - "babel-core": "6.23.1", - "babel-eslint": "^6.0.5", - "babel-loader": "6.4.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-plugin-transform-object-assign": "^6.22.0", - "babel-plugin-transform-object-rest-spread": "^6.23.0", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-preset-es2015": "6.22.0", - "babel-preset-react": "6.23.0", - "babel-preset-stage-0": "6.22.0", - "babel-register": "^6.26.0", - "css-loader": "0.6.12", - "eslint": "^3.6.0", - "eslint-config-ali": "^1.0.0", - "eslint-plugin-import": "*", - "eslint-plugin-react": "*", - "extract-text-webpack-plugin": "^2.1.2", - "gulp": "3.9.1", - "gulp-util": "2.2.20", - "json-loader": "*", - "node-libs-browser": "2.0.0", - "node-sass": "^4.7.2", - "opn": "^5.3.0", - "raw-loader": "^0.5.1", - "react": "16.5.2", - "react-dom": "16.5.2", - "sass-loader": "6.0.2", - "style-loader": "0.6.5", - "webpack": "^2.6.1", - "webpack-dev-server": "^2.4.5" - }, - "dependencies": { - "classnames": "^2.2.5", - "core-decorators": "^0.20.0", - "js-cookie": "^2.2.0", - "react": "^16.5.2", - "react-dom": "^16.5.2", - "react-scroll": "^1.7.9", - "react-tilt": "^0.1.4", - "whatwg-fetch": "^2.0.4" - } -} diff --git a/sop-website/sop-portal/redirect.ejs b/sop-website/sop-portal/redirect.ejs deleted file mode 100644 index 25f7b6be..00000000 --- a/sop-website/sop-portal/redirect.ejs +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - diff --git a/sop-website/sop-portal/site_config/blog.js b/sop-website/sop-portal/site_config/blog.js deleted file mode 100644 index cecd311b..00000000 --- a/sop-website/sop-portal/site_config/blog.js +++ /dev/null @@ -1,112 +0,0 @@ -export default { - 'en-us': { - barText: 'Blog', - postsTitle: 'All posts', - list: [ - { - title: 'The first blog', - author: 'author1', - dateStr: 'May 12nd,2018', - desc: 'Blog description, some text to sum up the main content of the blog', - link: '/en-us/blog/blog1.html', - }, - { - title: 'The second blog', - author: 'author2', - dateStr: 'May 2nd,2018', - desc: 'Blog description, some text to sum up the main content of the blog', - link: '/en-us/blog/blog2.html', - }, - { - title: 'The third blog', - author: 'author3', - dateStr: 'April 25th,2018', - desc: 'Blog description, some text to sum up the main content of the blog', - link: '/en-us/blog/blog3.html', - }, - { - title: 'The forth blog', - author: 'author4', - dateStr: 'April 22nd,2018', - desc: 'Blog description, some text to sum up the main content of the blog', - link: '/en-us/blog/blog4.html', - }, - { - title: 'The fifth blog', - author: 'author5', - dateStr: 'April 22nd,2018', - desc: 'Blog description, some text to sum up the main content of the blog', - link: '/en-us/blog/blog5.html', - }, - { - title: 'The sixth blog', - author: 'author6', - dateStr: 'April 22nd,2018', - desc: 'Blog description, some text to sum up the main content of the blog', - link: '/en-us/blog/blog6.html', - }, - { - title: 'The seventh blog', - author: 'author7', - dateStr: 'April 22nd,2018', - desc: 'Blog description, some text to sum up the main content of the blog', - link: '/en-us/blog/blog7.html', - }, - ], - }, - 'zh-cn': { - barText: '博客', - postsTitle: '所有文章', - list: [ - { - title: '第一篇博客', - author: 'author1', - dateStr: 'May 12nd,2018', - desc: '博客描述,总结博客的主体内容', - link: '/zh-cn/blog/blog1.html', - }, - { - title: '第二篇博客', - author: 'author2', - dateStr: 'May 2nd,2018', - desc: '博客描述,总结博客的主体内容', - link: '/zh-cn/blog/blog2.html', - }, - { - title: '第三篇博客', - author: 'author3', - dateStr: 'April 25th,2018', - desc: '博客描述,总结博客的主体内容', - link: '/zh-cn/blog/blog3.html', - }, - { - title: '第四篇博客', - author: 'author4', - dateStr: 'April 22nd,2018', - desc: '博客描述,总结博客的主体内容', - link: '/zh-cn/blog/blog4.html', - }, - { - title: '第五篇博客', - author: 'author5', - dateStr: 'April 22nd,2018', - desc: '博客描述,总结博客的主体内容', - link: '/zh-cn/blog/blog5.html', - }, - { - title: '第六篇博客', - author: 'author6', - dateStr: 'April 22nd,2018', - desc: '博客描述,总结博客的主体内容', - link: '/zh-cn/blog/blog6.html', - }, - { - title: '第七篇博客', - author: 'author7', - dateStr: 'April 22nd,2018', - desc: '博客描述,总结博客的主体内容', - link: '/zh-cn/blog/blog7.html', - }, - ], - }, -}; diff --git a/sop-website/sop-portal/site_config/community.jsx b/sop-website/sop-portal/site_config/community.jsx deleted file mode 100644 index 1affe896..00000000 --- a/sop-website/sop-portal/site_config/community.jsx +++ /dev/null @@ -1,334 +0,0 @@ -import React from 'react'; - -export default { - 'en-us': { - barText: 'Community', - events: { - title: 'Events & News', - list: [ - { - img: '/img/brhtqqzh.jpeg', - title: 'this is the title', - content: 'this is the content', - dateStr: 'May 12nd,2018', - link: '/en-us/blog/blog1.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: 'this is the title', - content: 'this is the content', - dateStr: 'May 12nd,2018', - link: '/en-us/blog/blog2.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: 'this is the title', - content: 'this is the content', - dateStr: 'May 12nd,2018', - link: '/en-us/blog/blog3.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: 'this is the title', - content: 'this is the content', - dateStr: 'May 12nd,2018', - link: '/en-us/blog/blog4.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: 'this is the title', - content: 'this is the content', - dateStr: 'May 12nd,2018', - link: '/en-us/blog/blog5.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: 'this is the title', - content: 'this is the content', - dateStr: 'May 12nd,2018', - link: '/en-us/blog/blog6.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: 'this is the title', - content: 'this is the content', - dateStr: 'May 12nd,2018', - link: '/en-us/blog/blog7.html', - }, - ] - }, - contacts: { - title: 'Talk To Us', - desc: 'Feel free to contact us via the following channel.', - list: [ - { - img: '/img/mailinglist.png', - imgHover: '/img/mailinglist_hover.png', - title: 'Mailing List', - link: '' - }, - { - img: '/img/alibaba.png', - imgHover: '/img/alibaba_hover.png', - title: 'gitter', - link: '', - }, - { - img: '/img/segmentfault.png', - imgHover: '/img/segmentfault_hover.png', - title: 'Segment Fault', - link: '' - }, - { - img: '/img/weibo.png', - imgHover: '/img/weibo_hover.png', - title: 'weibo', - link: '', - }, - ], - }, - contributorGuide: { - title: 'Contributor Guide', - desc: 'some description', - list: [ - { - img: '/img/mailinglist.png', - title: 'Mailing List', - content: this is the content, - }, - { - img: '/img/issue.png', - title: 'Issue', - content: this is the content, - }, - { - img: '/img/documents.png', - title: 'Documents', - content: this is the content, - }, - { - img: '/img/pullrequest.png', - title: 'Pull Request', - content: this is the content, - }, - ], - }, - ecos: { - title: 'Eco System', - list: [ - { - title: 'Eco1', - content: Eco System1, - tags: [ - { - text: 'tag1', - link: '', - bgColor: '#7A63FC', - }, - { - text: 'tag2', - link: '', - bgColor: '#00D0D9', - }, - { - text: 'tag3', - link: '', - bgColor: '#00D0D9', - }, - ], - }, - { - title: 'Eco2', - content: Eco System2, - tags: [ - { - text: 'tag1', - link: '', - bgColor: '#7A63FC', - }, - { - text: 'tag2', - link: '', - bgColor: '#00D0D9', - }, - { - text: 'tag3', - link: '/en-us/docs/demo1.html', - bgColor: '#00D0D9', - }, - { - text: 'tag4', - link: '', - bgColor: '#00D0D9', - }, - ], - }, - ], - }, - }, - 'zh-cn': { - barText: '社区', - events: { - title: '事件 & 新闻', - list: [ - { - img: '/img/brhtqqzh.jpeg', - title: '这是标题', - content: '这是内容', - dateStr: 'May 12nd,2018', - link: '/zh-cn/blog/blog1.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: '这是标题', - content: '这是内容', - dateStr: 'May 12nd,2018', - link: '/zh-cn/blog/blog2.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: '这是标题', - content: '这是内容', - dateStr: 'May 12nd,2018', - link: '/zh-cn/blog/blog3.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: '这是标题', - content: '这是内容', - dateStr: 'May 12nd,2018', - link: '/zh-cn/blog/blog4.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: '这是标题', - content: '这是内容', - dateStr: 'May 12nd,2018', - link: '/zh-cn/blog/blog5.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: '这是标题', - content: '这是内容', - dateStr: 'May 12nd,2018', - link: '/zh-cn/blog/blog6.html', - }, - { - img: '/img/brhtqqzh.jpeg', - title: '这是标题', - content: '这是内容', - dateStr: 'May 12nd,2018', - link: '/zh-cn/blog/blog7.html', - }, - ] - }, - contacts: { - title: '联系我们', - desc: '有问题需要反馈?请通过一下方式联系我们。', - list: [ - { - img: '/img/mailinglist.png', - imgHover: '/img/mailinglist_hover.png', - title: '邮件列表', - link: '' - }, - { - img: '/img/alibaba.png', - imgHover: '/img/alibaba_hover.png', - title: 'gitter', - link: '', - }, - { - img: '/img/segmentfault.png', - imgHover: '/img/segmentfault_hover.png', - title: 'Segment Fault', - link: '' - }, - { - img: '/img/weibo.png', - imgHover: '/img/weibo_hover.png', - title: '微博', - link: '', - }, - ], - }, - contributorGuide: { - title: '贡献指南', - desc: '一些描述', - list: [ - { - img: '/img/mailinglist.png', - title: '邮件列表', - content: 这是描述, - }, - { - img: '/img/issue.png', - title: '报告缺陷', - content: 这是描述, - }, - { - img: '/img/documents.png', - title: '文档', - content: 这是描述, - }, - { - img: '/img/pullrequest.png', - title: 'Pull Request', - content: 这是描述, - }, - ], - }, - ecos: { - title: '生态系统', - list: [ - { - title: '生态系统1', - content: 生态系统1, - tags: [ - { - text: '标签1', - link: '', - bgColor: '#7A63FC', - }, - { - text: '标签2', - link: '', - bgColor: '#00D0D9', - }, - { - text: '标签3', - link: '', - bgColor: '#00D0D9', - }, - ], - }, - { - title: '生态系统2', - content: 生态系统2, - tags: [ - { - text: '标签1', - link: '', - bgColor: '#7A63FC', - }, - { - text: '标签2', - link: '', - bgColor: '#00D0D9', - }, - { - text: '标签3', - link: '/zh-cn/docs/demo1.html', - bgColor: '#00D0D9', - }, - { - text: '标签4', - link: '', - bgColor: '#00D0D9', - }, - ], - }, - ], - }, - }, -}; diff --git a/sop-website/sop-portal/site_config/docs.js b/sop-website/sop-portal/site_config/docs.js deleted file mode 100644 index 95ecf679..00000000 --- a/sop-website/sop-portal/site_config/docs.js +++ /dev/null @@ -1,58 +0,0 @@ -export default { - 'en-us': { - sidemenu: [ - { - title: 'header title', - children: [ - { - title: 'demo1', - link: '/en-us/docs/demo1.html', - }, - { - title: 'demo2', - link: '/en-us/docs/demo2.html', - }, - { - title: 'dir', - opened: true, - children: [ - { - title: 'demo3', - link: '/en-us/docs/dir/demo3.html', - }, - ], - }, - ], - }, - ], - barText: 'Documentation', - }, - 'zh-cn': { - sidemenu: [ - { - title: '大标题', - children: [ - { - title: '示例1', - link: '/zh-cn/docs/demo1.html', - }, - { - title: '示例2', - link: '/zh-cn/docs/demo2.html', - }, - { - title: '目录', - opened: true, - children: [ - { - title: '示例3', - link: '/zh-cn/docs/dir/demo3.html', - }, - ], - }, - ], - }, - ], - barText: '文档', - }, -}; diff --git a/sop-website/sop-portal/site_config/home.jsx b/sop-website/sop-portal/site_config/home.jsx deleted file mode 100644 index 8115785a..00000000 --- a/sop-website/sop-portal/site_config/home.jsx +++ /dev/null @@ -1,170 +0,0 @@ -import React from 'react'; - -export default { - 'zh-cn': { - brand: { - brandName: '开放平台', - briefIntroduction: 'XX开放平台,一句话介绍。。。', - buttons: [ - { - text: '开始使用', - link: 'http://localhost:8083/index.html#/login', - type: 'primary', - } - ], - }, - introduction: { - title: 'XX介绍', - desc: 'XX介绍描述文字', - img: '/img/home_1.png', - }, - // 介绍部分 - introductions: [ - { - title: '介绍部分1', - desc: '介绍文字。。。', - img: '/img/quick_start.png', - }, - { - title: '介绍部分2', - desc: '介绍文字。。。', - img: '/img/quick_start.png', - }], - features: { - title: '特性一览', - list: [ - { - img: '/img/feature_maintenance.png', - title: '特性1', - content: '描述文字。。', - }, - { - img: '/img/feature_loadbalances.png', - title: '特性2', - content: '描述文字。。', - } - ], - }, - start: { - title: '快速开始', - desc: '简单描述', - img: '/img/quick_start.png', - button: { - text: '阅读更多', - link: '/zh-cn/docs/demo1.html', - }, - }, - users: { - title: '用户', - desc: 简单描述, - list: [ - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - ], - }, - }, - 'en-us': { - brand: { - brandName: 'brandName', - briefIntroduction: 'some description of product', - buttons: [ - { - text: 'Quick Start', - link: '/en-us/docs/demo1.html', - type: 'primary', - }, - { - text: 'View on Github', - link: '', - type: 'normal', - }, - ], - }, - introduction: { - title: 'introduction title', - desc: 'some introduction of your product', - img: '/img/architecture.png', - }, - introductions: [], - features: { - title: 'Feature List', - list: [ - { - img: '/img/feature_transpart.png', - title: 'feature1', - content: 'feature description', - }, - { - img: '/img/feature_loadbalances.png', - title: 'feature2', - content: 'feature description', - }, - { - img: '/img/feature_service.png', - title: 'feature3', - content: 'feature description', - }, - { - img: '/img/feature_hogh.png', - title: 'feature4', - content: 'feature description', - }, - { - img: '/img/feature_runtime.png', - title: 'feature5', - content: 'feature description', - }, - { - img: '/img/feature_maintenance.png', - title: 'feature6', - content: 'feature description', - } - ] - }, - start: { - title: 'Quick start', - desc: 'some description text', - img: '/img/quick_start.png', - button: { - text: 'READ MORE', - link: '/en-us/docs/demo1.html', - }, - }, - users: { - title: 'users', - desc: some description, - list: [ - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - '/img/users_alibaba.png', - ], - }, - }, -}; diff --git a/sop-website/sop-portal/site_config/site.js b/sop-website/sop-portal/site_config/site.js deleted file mode 100644 index 0edcaad8..00000000 --- a/sop-website/sop-portal/site_config/site.js +++ /dev/null @@ -1,116 +0,0 @@ -// 全局的一些配置 -export default { - rootPath: '', // 发布到服务器的根目录,需以/开头但不能有尾/,如果只有/,请填写空字符串 - port: 8080, // 本地开发服务器的启动端口 - domain: 'localhost', // 站点部署域名,无需协议和path等 - defaultSearch: '', // 默认搜索引擎,baidu或者google - defaultLanguage: 'zh-cn', - 'en-us': { - pageMenu: [ - { - key: 'home', // 用作顶部菜单的选中 - text: 'HOME', - link: '/en-us/index.html', - }, - { - key: 'docs', - text: 'DOCS', - link: '/en-us/docs/demo1.html', - }, - { - key: 'blog', - text: 'BLOG', - link: '/en-us/blog/index.html', - }, - { - key: 'community', - text: 'COMMUNITY', - link: '/en-us/community/index.html', - }, - ], - disclaimer: { - title: 'Disclaimer', - content: 'the disclaimer content', - }, - documentation: { - title: 'Documentation', - list: [ - { - text: 'Overview', - link: '/en-us/docs/demo1.html', - }, - { - text: 'Quick start', - link: '/en-us/docs/demo2.html', - }, - { - text: 'Developer guide', - link: '/en-us/docs/dir/demo3.html', - }, - ], - }, - resources: { - title: 'Resources', - list: [ - { - text: 'Blog', - link: '/en-us/blog/index.html', - }, - { - text: 'Community', - link: '/en-us/community/index.html', - }, - ], - }, - copyright: 'Copyright © 2018 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - }, - 'zh-cn': { - pageMenu: [ - { - key: 'blog', - text: '登录', - link: 'http://localhost:8083/index.html#/login', - }, - { - key: 'community', - text: '免费注册', - link: 'http://localhost:8083/index.html#/isvReg', - }, - ], - disclaimer: { - title: 'XX', - content: 'XX', - }, - documentation: { - title: '文档', - list: [ - { - text: '概览', - link: '/zh-cn/docs/demo1.html', - }, - { - text: '快速开始', - link: '/zh-cn/docs/demo2.html', - }, - { - text: '开发者指南', - link: '/zh-cn/docs/dir/demo3.html', - }, - ], - }, - resources: { - title: '资源', - list: [ - { - text: '博客', - link: '/zh-cn/blog/index.html', - }, - { - text: '社区', - link: '/zh-cn/community/index.html', - }, - ], - }, - copyright: 'Copyright © 2018 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - }, -}; diff --git a/sop-website/sop-portal/src/components/bar/index.jsx b/sop-website/sop-portal/src/components/bar/index.jsx deleted file mode 100644 index 191e145e..00000000 --- a/sop-website/sop-portal/src/components/bar/index.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { getLink } from '../../../utils'; -import './index.scss'; - -const propTypes = { - text: PropTypes.string.isRequired, // 显示的文案 - img: PropTypes.string.isRequired, // 显示的图片链接 -}; - -const Bar = (props) => { - const { text, img } = props; - const cls = classnames({ - bar: true, - }); - return ( -
-
- - {text} - -
-
- ); -}; - -Bar.propTypes = propTypes; - -export default Bar; diff --git a/sop-website/sop-portal/src/components/bar/index.scss b/sop-website/sop-portal/src/components/bar/index.scss deleted file mode 100644 index ba890ed2..00000000 --- a/sop-website/sop-portal/src/components/bar/index.scss +++ /dev/null @@ -1,59 +0,0 @@ -@import '../../variables.scss'; - -.bar { - margin-top: $headerHeight; - background-image: linear-gradient(-90deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - .bar-body { - max-width: $contentWidth; - margin: 0 auto; - height: $barHeight; - line-height: $barHeight; - font-family: Avenir-Heavy; - font-size: 36px; - color: #FFF; - position: relative; - &::before { - content: ''; - height: 100%; - position: absolute; - left: 42px; - top: 0; - opacity: 0.3; - border-left: 1px solid #FFFFFF; - } - &::after { - content: ''; - height: 16px; - position: absolute; - left: 40px; - top: 50%; - margin: auto 0; - border-left: 4px solid #FFFFFF; - } - .front-img { - width: 80px; - height: 80px; - vertical-align: middle; - margin: 0 28px 0 70px; - } - .back-img { - width: 160px; - height: 160px; - position: absolute; - right: 168px; - bottom: 0; - opacity: 0.15; - } - @media screen and (max-width: $mobileWidth){ - &::before { - left: 22px; - } - &::after { - left: 20px; - } - .front-img { - margin-left: 50px; - } - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/button/index.jsx b/sop-website/sop-portal/src/components/button/index.jsx deleted file mode 100644 index 49da2c04..00000000 --- a/sop-website/sop-portal/src/components/button/index.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { getLink } from '../../../utils'; -import './index.scss'; - -const propTypes = { - type: PropTypes.oneOf(['primary', 'normal']), - link: PropTypes.string, - target: PropTypes.string, -}; -const defaultProps = { - type: 'primary', - link: '', - target: '_self', -}; -const Button = (props) => { - return ( - - {props.children} - - ); -}; - -Button.propTypes = propTypes; -Button.defaultProps = defaultProps; - -export default Button; diff --git a/sop-website/sop-portal/src/components/button/index.scss b/sop-website/sop-portal/src/components/button/index.scss deleted file mode 100644 index 5c0d62cc..00000000 --- a/sop-website/sop-portal/src/components/button/index.scss +++ /dev/null @@ -1,20 +0,0 @@ -.button { - box-sizing: border-box; - display: inline-block; - height: 48px; - line-height: 48px; - min-width: 140px; - font-family: Avenir-Heavy; - font-size: 16px; - color: #FFF; - text-align: center; - border-radius: 4px; - text-decoration: none; - &-primary { - background: #4190FF; - } - &-normal { - background: transparent; - border: 1px solid #fff; - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/footer/index.jsx b/sop-website/sop-portal/src/components/footer/index.jsx deleted file mode 100644 index ff560a51..00000000 --- a/sop-website/sop-portal/src/components/footer/index.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -// import siteConfig from '../../../site_config/site'; -// import { getLink } from '../../../utils'; -import './index.scss'; -import { getLink } from '../../../utils'; - -const propTypes = { - logo: PropTypes.string.isRequired, // logo地址 - language: PropTypes.oneOf(['zh-cn', 'en-us']), -}; - -const beianStyle = { - div: { - margin: '0 auto', - padding: '20px 0', - a: { - color: '#999', - paddingRight: '10px', - textDecoration: 'none', - height: '20px', - lineHeight: '20px', - img: { - float: 'left' - }, - p: { - float: 'left', - height: '20px', - lineHeight: '20px', - margin: '0px 0px 0px 5px', - color: '#939393' - } - } - } -} - -class Footer extends React.Component { - - render() { - // const { logo, language } = this.props; - // const dataSource = siteConfig[language]; - return ( - - ); - } -} - -Footer.propTypes = propTypes; - -export default Footer; diff --git a/sop-website/sop-portal/src/components/footer/index.scss b/sop-website/sop-portal/src/components/footer/index.scss deleted file mode 100644 index 2177fa36..00000000 --- a/sop-website/sop-portal/src/components/footer/index.scss +++ /dev/null @@ -1,104 +0,0 @@ -@import '../../variables.scss'; - -.footer-container { - background: #F8F8F8; - .footer-body { - max-width: $contentWidth; - margin: 0 auto; - box-sizing: border-box; - padding: 40px 40px 0; - @media screen and (max-width: $mobileWidth) { - padding-left: 20px; - padding-right: 20px; - } - //img { - // // display: block; - // width: 270px; - // margin-bottom: 28px; - // margin-right: 20px; - // vertical-align: middle; - //} - .cols-container { - margin-bottom: 60px; - .col { - display: inline-block; - box-sizing: border-box; - vertical-align: top; - } - .col-12 { - width: 50%; - padding-right: 125px; - } - .col-6 { - width: 25%; - } - h3 { - font-family: Avenir-Heavy; - font-size: 18px; - color: #333; - line-height: 18px; - margin-bottom: 20px; - } - p { - font-family: Avenir-Medium; - font-size: 12px; - color: #999; - line-height: 18px; - } - dl { - font-family: Avenir-Heavy; - line-height: 18px; - } - dt { - font-weight: bold; - font-size: 18px; - color: #333; - margin-bottom: 20px; - } - dd { - padding: 0; - margin: 0; - a { - text-decoration: none; - display: block; - font-size: 14px; - color: #999; - margin: 10px 0; - } - a:hover { - color: $brandColor; - } - } - } - .copyright { - border-top: 1px solid #ccc; - min-height: 60px; - line-height: 20px; - text-align: center; - font-family: Avenir-Medium; - font-size: 12px; - color: #999; - display: flex; - align-items: center; - span { - display: inline-block; - margin: 0 auto; - } - } - } -} - -@media screen and (max-width: $mobileWidth) { - .footer-container { - .footer-body { - .cols-container { - .col { - width: 100%; - text-align: center; - padding: 0; - } - } - } - } -} -.beian {color: #999;} diff --git a/sop-website/sop-portal/src/components/header/index.jsx b/sop-website/sop-portal/src/components/header/index.jsx deleted file mode 100644 index a034a6e7..00000000 --- a/sop-website/sop-portal/src/components/header/index.jsx +++ /dev/null @@ -1,190 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { autobind } from 'core-decorators'; -import siteConfig from '../../../site_config/site'; -import { getLink } from '../../../utils'; -import './index.scss'; - -const languageSwitch = [ - { - text: '中', - value: 'en-us', - }, - { - text: 'En', - value: 'zh-cn', - }, -]; -const searchSwitch = { - baidu: { - logo: 'https://img.alicdn.com/tfs/TB1n6DQayLaK1RjSZFxXXamPFXa-300-300.png', - url: 'https://www.baidu.com/s?wd=', - }, - google: { - logo: 'https://img.alicdn.com/tfs/TB1REfuaCzqK1RjSZFjXXblCFXa-300-300.jpg', - url: 'https://www.google.com/search?q=', - }, -}; -const noop = () => {}; -const propTypes = { - currentKey: PropTypes.string, - logo: PropTypes.string.isRequired, - type: PropTypes.oneOf(['primary', 'normal']), - language: PropTypes.oneOf(['en-us', 'zh-cn']), - onLanguageChange: PropTypes.func, -}; -const defaultProps = { - type: 'primary', - language: 'en-us', - onLanguageChange: noop, -}; - -@autobind -class Header extends React.Component { - constructor(props) { - super(props); - this.state = { - menuBodyVisible: false, - language: props.language, - search: siteConfig.defaultSearch, - searchValue: '', - inputVisible: false, - }; - } - - componentWillReceiveProps(nextProps) { - this.setState({ - language: nextProps.language, - }); - } - - toggleMenu() { - this.setState({ - menuBodyVisible: !this.state.menuBodyVisible, - }); - } - - switchLang() { - let language; - if (this.state.language === 'zh-cn') { - language = 'en-us'; - } else { - language = 'zh-cn'; - } - this.setState({ - language, - }); - this.props.onLanguageChange(language); - } - - switchSearch() { - let search; - if (this.state.search === 'baidu') { - search = 'google'; - } else { - search = 'baidu'; - } - this.setState({ - search, - }); - } - - toggleSearch() { - this.setState({ - searchVisible: !this.state.searchVisible, - }); - } - - onInputChange(e) { - this.setState({ - searchValue: e.target.value, - }); - } - - goSearch() { - const { search, searchValue } = this.state; - window.open(`${searchSwitch[search].url}${window.encodeURIComponent(`${searchValue} site:${siteConfig.domain}`)}`); - } - - onKeyDown(e) { - if (e.keyCode === 13) { - this.goSearch(); - } - } - - render() { - const { type, logo, onLanguageChange, currentKey } = this.props; - const { menuBodyVisible, language, search, searchVisible } = this.state; - return ( -
-
- {siteConfig.name} - { - siteConfig.defaultSearch ? - ( -
- - { - searchVisible ? - ( -
- - -
- ) : null - } -
- ) : null - } - { - null - } -
- -
    - {siteConfig[language].pageMenu.map(item => ( -
  • - {item.text} -
  • ))} -
-
-
-
- ); - } -} - -Header.propTypes = propTypes; -Header.defaultProps = defaultProps; -export default Header; diff --git a/sop-website/sop-portal/src/components/header/index.scss b/sop-website/sop-portal/src/components/header/index.scss deleted file mode 100644 index 3c6cb9d5..00000000 --- a/sop-website/sop-portal/src/components/header/index.scss +++ /dev/null @@ -1,249 +0,0 @@ -@import '../../variables.scss'; - -.header-container { - position: fixed; - left: 0; - top: 0; - width: 100%; - z-index: 1000; - background-color: #fff; - &-primary { - background-color: transparent; - } - &-normal { - background-color: #fff; - box-shadow: 0 2px 10px 0 rgba(0,0,0,0.08); - } - .header-body { - max-width: $contentWidth; - margin: 0 auto; - height: $headerHeight; - line-height: $headerHeight; - .logo { - margin-left: 40px; - width: 180px; - vertical-align: sub; - } - .header-menu { - float: right; - .header-menu-toggle { - display: none; - width: 19px; - margin-right: 40px; - margin-top: $headerHeight / 2 - 15px; - cursor: pointer; - } - } - ul { - padding: 0; - margin: 0; - } - li { - display: inline-block; - margin-right: 40px; - } - .menu-item { - font-family: Avenir-Heavy; - font-size: 14px; - vertical-align: bottom; - } - .menu-item-primary { - a { - color: #fff; - opacity: 0.6; - font-family: Avenir-Medium; - } - &:hover { - a { - // font-family: Avenir-Heavy; - opacity: 1; - } - } - &-active { - a { - // font-family: Avenir-Heavy; - opacity: 1; - } - } - } - .menu-item-normal { - a { - color: #333; - opacity: 0.6; - font-family: Avenir-Medium; - } - &:hover { - a { - // font-family: Avenir-Heavy; - opacity: 1; - } - } - &-active { - a { - // font-family: Avenir-Heavy; - opacity: 1; - } - } - } - .language-switch { - float: right; - box-sizing: border-box; - width: 24px; - height: 24px; - line-height: 20px; - margin-top: $headerHeight / 2 - 12px; - margin-right: 40px; - text-align: center; - border-radius: 2px; - cursor: pointer; - font-family: PingFangSC-Medium; - font-size: 14px; - opacity: 0.6; - &:hover { - opacity: 1; - } - } - .language-switch-primary { - border: 1px solid #FFF; - color: #FFF; - } - .language-switch-normal { - border: 1px solid #333; - color: #333; - } - .search { - float: right; - width: 24px; - height: 24px; - margin-top: 21px; - margin-right: 40px; - line-height: normal; - position: relative; - .icon-search { - display: inline-block; - cursor: pointer; - width: 12px; - height: 12px; - border-radius: 50%; - border:2px solid; - position: relative; - &::before { - content: ''; - transform: rotate(45deg); - width:8px; - height: 2px; - position: absolute; - top:13px; - left:11px; - } - } - &-primary { - .icon-search { - border-color: #fff; - opacity: 0.6; - &::before { - background-color: #fff; - } - &:hover { - opacity: 1; - } - } - } - &-normal { - .icon-search { - border-color: #333; - opacity: 0.6; - &::before { - background-color: #333; - } - &:hover { - opacity: 1; - } - } - } - .search-input { - position: absolute; - left: -172px; - top: 28px; - background: #fff; - border-radius: 4px; - overflow: hidden; - box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.05), 0 -8px 16px 0 rgba(0, 0, 0, 0.05); - img { - width: 28px; - height: 28px; - cursor: pointer; - vertical-align: middle; - } - input { - padding: 0 4px; - border: none; - outline: none; - width: 160px; - height: 24px; - vertical-align: middle; - } - } - } - } -} - -@media screen and (max-width: $mobileWidth) { - .header-container { - .header-body { - .logo { - margin-left: 20px; - width: 270px; - } - .language-switch { - margin-right: 20px; - } - .header-menu { - ul { - display: none; - } - .header-menu-toggle { - display: inline-block; - margin-right: 20px; - } - } - .header-menu-open { - ul { - background-color: $headerMenuBgColor; - display: inline-block; - position: absolute; - right: 0; - top: $headerHeight; - z-index: 100; - } - li { - width: 200px; - display: list-item; - padding-left: 30px; - list-style: none; - line-height: 40px; - margin-right: 0; - a { - color: #333; - display: inline-block; - width: 100%; - } - &:hover { - background: $endColor; - a { - color: #fff; - opacity: 1; - } - } - } - .menu-item-primary-active, .menu-item-normal-active { - background: $endColor; - a { - color: #fff; - opacity: 1; - } - } - } - } - } -} diff --git a/sop-website/sop-portal/src/components/language/index.jsx b/sop-website/sop-portal/src/components/language/index.jsx deleted file mode 100644 index 48053646..00000000 --- a/sop-website/sop-portal/src/components/language/index.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { autobind } from 'core-decorators'; -import cookie from 'js-cookie'; -import siteConfig from '../../../site_config/site'; - -@autobind -class Language extends React.Component { - - onLanguageChange(language) { - const pathname = window.location.pathname; - let oldLang; - if (language === 'zh-cn') { - oldLang = 'en-us'; - } else { - oldLang = 'zh-cn'; - } - const newPathname = pathname.replace(`${window.rootPath}/${oldLang}`, `${window.rootPath}/${language}`); - cookie.set('docsite_language', language, { expires: 365}); - window.location = newPathname; - } - - getLanguage() { - const urlLang = window.location.pathname.replace(window.rootPath || '', '').split('/')[1]; - let language = this.props.lang || urlLang || cookie.get('docsite_language') || siteConfig.defaultLanguage; - // 防止链接被更改导致错误的cookie存储 - if (language !== 'en-us' && language !== 'zh-cn') { - language = siteConfig.defaultLanguage; - } - // 同步cookie语言版本 - if (language !== cookie.get('docsite_language')) { - cookie.set('docsite_language', language, { expires: 365 }); - } - return language; - } -} - -export default Language; diff --git a/sop-website/sop-portal/src/components/pageSlider/index.jsx b/sop-website/sop-portal/src/components/pageSlider/index.jsx deleted file mode 100644 index c09a506c..00000000 --- a/sop-website/sop-portal/src/components/pageSlider/index.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { autobind } from 'core-decorators'; -import classnames from 'classnames'; -import { throttle, getLink } from '../../../utils'; - -import './index.scss'; - -const propTypes = { - pageSize: PropTypes.number, // 每页最多显示的条数 - -}; -const defaultProps = { - pageSize: 5, -}; - -@autobind -class pageSlider extends React.Component { - constructor(props) { - super(props); - this.container = null; - this.state = { - page: 0, - pageWidth: 0, - }; - } - - componentDidMount() { - const pageWidth = this.container.getBoundingClientRect().width; - /* eslint-disable react/no-did-mount-set-state */ - this.setState({ - pageWidth, - }); - this.throttleAdjust = throttle(() => { - this.setState({ - pageWidth: this.container.getBoundingClientRect().width, - }); - }, 200); - window.addEventListener('resize', this.throttleAdjust); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.throttleAdjust); - } - - changePage(i) { - this.setState({ - page: i, - }); - } - - renderSliderList() { - const { children, pageSize } = this.props; - const { page, pageWidth } = this.state; - const splitGroup = []; - const len = React.Children.count(children); - // 分成的屏数 - const splitNum = Math.ceil(len / pageSize); - /* eslint-disable no-plusplus*/ - for (let i = 0; i < splitNum; i++) { - splitGroup.push(Array.from(children).slice(i * pageSize, (i + 1) * pageSize)); - } - return ( -
- {splitGroup.map((group, i) => { - return ( -
- { - group.map((child, j) => ( -
- {child} -
- ) - ) - } -
- ); - })} -
- ); - } - - renderControl() { - const { children, pageSize } = this.props; - const { page } = this.state; - const len = React.Children.count(children); - // 分成的屏数 - const splitNum = Math.ceil(len / pageSize); - return ( -
- - -
- ); - } - - render() { - return ( -
{ this.container = node; }}> - {this.renderSliderList()} - {this.renderControl()} -
- ); - } -} - -pageSlider.propTypes = propTypes; -pageSlider.defaultProps = defaultProps; - -export default pageSlider; diff --git a/sop-website/sop-portal/src/components/pageSlider/index.scss b/sop-website/sop-portal/src/components/pageSlider/index.scss deleted file mode 100644 index 07597412..00000000 --- a/sop-website/sop-portal/src/components/pageSlider/index.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import '../../variables.scss'; - -.page-slider { - overflow: hidden; - .slider-list { - overflow: visible; - .slider-page { - overflow: hidden; - display: inline-block; - vertical-align: top; - } - } - .slider-control { - overflow: hidden; - img { - display: inline-block; - width: 52px; - height: 52px; - cursor: pointer; - } - .slider-control-prev { - float: left; - &-hidden { - display: none; - } - } - .slider-control-next { - float: right; - &-hidden { - display: none; - } - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/sidemenu/index.jsx b/sop-website/sop-portal/src/components/sidemenu/index.jsx deleted file mode 100644 index fc58d232..00000000 --- a/sop-website/sop-portal/src/components/sidemenu/index.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import classnames from 'classnames'; -import { autobind } from 'core-decorators'; -import Item from './item.jsx'; -import './index.scss'; - -@autobind -class SideMenu extends React.Component { - constructor(props) { - super(props); - this.state = { - menuBodyVisible: false, - }; - } - - toggleMenuBody() { - this.setState({ - menuBodyVisible: !this.state.menuBodyVisible, - }); - } - - render() { - const { dataSource } = this.props; - const { menuBodyVisible } = this.state; - const cls = classnames({ - sidemenu: true, - 'sidemenu-open': menuBodyVisible, - }); - const itemCls = classnames({ - 'menu-item': true, - 'menu-item-level-1': true, - }); - return ( -
-
- -
-
    - { - dataSource.map((data, i) => { - return ( -
  • - - {data.title} - -
      - {data.children.map((item, j) => )} -
    -
  • - ); - }) - } -
-
- ); - } -} - -export default SideMenu; diff --git a/sop-website/sop-portal/src/components/sidemenu/index.scss b/sop-website/sop-portal/src/components/sidemenu/index.scss deleted file mode 100644 index 44051ffd..00000000 --- a/sop-website/sop-portal/src/components/sidemenu/index.scss +++ /dev/null @@ -1,121 +0,0 @@ -@import '../../variables.scss'; -.sidemenu { - background: $sideMenuBgColor; - width: $sideMenuWidth; - position: relative; - display: inline-block; - padding: 20px 0; - .sidemenu-toggle { - text-align: center; - cursor: pointer; - position: absolute; - top: 0; - width: 40px; - right: -40px; - height: 30px; - line-height: 30px; - background: $sideMenuBgColor; - border-radius: 0 4px 4px 0; - display: none; - img { - width: 16px; - text-align: center; - vertical-align: middle; - } - } - ul { - list-style: none; - padding: 0; - margin: 0; - } - li { - line-height: 0; - } - span, a { - box-sizing: border-box; - display: inline-block; - position: relative; - width: 100%; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .menu-item-selected { - a { - background: white; - } - a::before { - content: ''; - position: absolute; - left: 0; - top: 0; - width: 4px; - height: 100%; - background-image:linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - } - } - .menu-item-level-1 { - & > span { - font-family: Avenir-Heavy; - font-size: 18px; - color: #333; - padding-left: 20px; - height: 40px; - line-height: 40px; - } - } - - .menu-item-level-2, .menu-item-level-3 { - cursor: pointer; - & > span, & > a { - font-family: Avenir-Medium; - font-size: 14px; - color: #666; - &:hover { - background: white; - &::before { - content: ''; - position: absolute; - left: 0; - top: 0; - width: 4px; - height: 100%; - background-image:linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - } - } - } - } - - .menu-item-level-2 { - & > span, & > a { - padding-left: 40px; - height: 36px; - line-height: 36px; - img.menu-toggle { - float: right; - width: 13px; - height: 8px; - margin: 14px 20px 14px 0; - } - } - } - .menu-item-level-3 { - & > a { - padding-left: 60px; - height: 36px; - line-height: 36px; - } - } -} - -@media screen and (max-width: $mobileWidth) { - .sidemenu { - width: 0; - .sidemenu-toggle { - display: inline-block; - } - &.sidemenu-open { - width: $sideMenuWidth; - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/sidemenu/item.jsx b/sop-website/sop-portal/src/components/sidemenu/item.jsx deleted file mode 100644 index 75204df6..00000000 --- a/sop-website/sop-portal/src/components/sidemenu/item.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from 'react'; -import { autobind } from 'core-decorators'; -import classnames from 'classnames'; -import { getLink } from '../../../utils'; - -@autobind -class Item extends React.Component { - constructor(props) { - super(props); - const { item } = props; - const hasChildren = item.children && item.children.length; - let opened = props.item.opened; - if (hasChildren) { - if (opened === undefined) { - // 未配置展开,则是否展开由是否选中决定 - opened = item.children.find(child => getLink(child.link) === window.location.pathname); - } - } else { - opened = false; - } - this.state = { - opened, - }; - } - - onItemClick(e) { - this.props.toggleMenuBody(); - e.stopPropagation(); - } - - toggle() { - this.setState({ - opened: !this.state.opened, - }); - } - - renderSubMenu(data) { - return ( -
    - { - data.map((item, index) => ( -
  • - {item.title} -
  • - )) - } -
- ); - } - - render() { - const { item } = this.props; - const hasChildren = item.children && item.children.length; - const { opened } = this.state; - const cls = classnames({ - 'menu-item': true, - 'menu-item-level-2': true, - 'menu-item-selected': getLink(item.link) === window.location.pathname, - }); - const style = { - height: opened ? 36 * (item.children.length + 1) : 36, - overflow: 'hidden', - }; - if (hasChildren) { - return ( -
  • - { - - {item.title} - - - } - {this.renderSubMenu(item.children)} -
  • - ); - } - return ( -
  • - {item.title} -
  • - ); - } -} - -export default Item; diff --git a/sop-website/sop-portal/src/components/slider/index.jsx b/sop-website/sop-portal/src/components/slider/index.jsx deleted file mode 100644 index fb742f18..00000000 --- a/sop-website/sop-portal/src/components/slider/index.jsx +++ /dev/null @@ -1,163 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { autobind } from 'core-decorators'; -import classnames from 'classnames'; -import { throttle } from '../../../utils'; - -import './index.scss'; - -@autobind -class Slider extends React.Component { - constructor(props) { - super(props); - this.container = null; - this.state = { - screenIndex: 0, - visibleNum: 1, - }; - } - - componentDidMount() { - this.throttleAdjust = throttle(() => { - this.setState({ - visibleNum: this.getVisibleNum(), - }); - }, 200); - window.addEventListener('resize', this.throttleAdjust); - // 做重新布局 - /* eslint-disable react/no-did-mount-set-state */ - this.setState({ - visibleNum: this.getVisibleNum(), - }); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.throttleAdjust); - } - - getVisibleNum() { - // 比较粗略的计算,假定一屏的子节点外边距之和不会超过一个子节点的宽度 - /* eslint-disable react/no-find-dom-node */ - let result = 1; - const containerWidth = this.container.getBoundingClientRect().width; - const childWidth = this.sliderItemChild0.getBoundingClientRect ? this.sliderItemChild0.getBoundingClientRect().width : ReactDOM.findDOMNode(this.sliderItemChild0).getBoundingClientRect().width; - if (containerWidth && childWidth) { - result = Math.floor(containerWidth / childWidth); - } - // 有可能result为0,下面的除法就会出现分母为0,从而导致得出Inifity,无限循环导致浏览器崩溃 - return result || 1; - } - - getListWidth() { - let width = 0; - const { children } = this.props; - const { visibleNum } = this.state; - const len = React.Children.count(children); - // 最终分成的屏数 - const splitNum = Math.ceil(len / visibleNum); - if (this.container) { - const containerWidth = this.container.getBoundingClientRect().width; - width = containerWidth * splitNum; - } - return width; - } - - changeScreen(i) { - const { screenIndex } = this.state; - // const total = React.Children.count(this.props.children); - if (i !== screenIndex) { - this.setState({ - screenIndex: i - }); - } - } - - renderSliderList() { - const { children } = this.props; - const { screenIndex, visibleNum } = this.state; - const splitGroup = []; - const len = React.Children.count(children); - // 分成的屏数 - const splitNum = Math.ceil(len / visibleNum); - /* eslint-disable no-plusplus*/ - for (let i = 0; i < splitNum; i++) { - splitGroup.push(Array.from(children).slice(i * visibleNum, (i + 1) * visibleNum)); - } - return ( -
    - {splitGroup.map((group, i) => { - return ( -
    { this[`sliderScreen${i}`] = node; }} - > - { - group.map((child, j) => ( -
    - {React.cloneElement(child, { - ref: (node) => { - this[`sliderItemChild${(i * visibleNum) + j}`] = node; - } - })} -
    - ) - ) - } -
    - ); - })} -
    - ); - } - - renderControl() { - const { children } = this.props; - const { screenIndex, visibleNum } = this.state; - const len = React.Children.count(children); - // 分成的屏数 - const splitNum = Math.ceil(len / visibleNum); - const temp = []; - for (let i = 0; i < splitNum; i++) { - temp.push(( - - )); - } - return ( -
    - {temp} -
    - ); - } - - render() { - return ( -
    { this.container = node; }}> - {this.renderSliderList()} - {this.renderControl()} -
    - ); - } -} - -export default Slider; diff --git a/sop-website/sop-portal/src/components/slider/index.scss b/sop-website/sop-portal/src/components/slider/index.scss deleted file mode 100644 index f4bd9353..00000000 --- a/sop-website/sop-portal/src/components/slider/index.scss +++ /dev/null @@ -1,30 +0,0 @@ -@import '../../variables.scss'; - -.slider { - overflow: hidden; - .slider-list { - overflow: visible; - .slider-screen { - display: inline-flex; - justify-content: space-around; - overflow: hidden; - } - } - .slider-control { - text-align: center; - margin-top: 20px; - .slider-control-item { - cursor: pointer; - display: inline-block; - width: 20px; - height: 4px; - margin-right: 4px; - background: #ccc; - &-active { - width: 40px; - height: 6px; - background-image: linear-gradient(-90deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - } - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/markdown.scss b/sop-website/sop-portal/src/markdown.scss deleted file mode 100644 index fd30a16e..00000000 --- a/sop-website/sop-portal/src/markdown.scss +++ /dev/null @@ -1,1555 +0,0 @@ -@font-face { - font-family: octicons-link; - src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff'); -} - -.markdown-body { - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100%; - line-height: 1.5; - color: #24292e; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; - font-size: 16px; - line-height: 1.5; - word-wrap: break-word; -} - -.markdown-body .pl-c { - color: #6a737d; -} - -.markdown-body .pl-c1, -.markdown-body .pl-s .pl-v { - color: #005cc5; -} - -.markdown-body .pl-e, -.markdown-body .pl-en { - color: #6f42c1; -} - -.markdown-body .pl-smi, -.markdown-body .pl-s .pl-s1 { - color: #24292e; -} - -.markdown-body .pl-ent { - color: #22863a; -} - -.markdown-body .pl-k { - color: #d73a49; -} - -.markdown-body .pl-s, -.markdown-body .pl-pds, -.markdown-body .pl-s .pl-pse .pl-s1, -.markdown-body .pl-sr, -.markdown-body .pl-sr .pl-cce, -.markdown-body .pl-sr .pl-sre, -.markdown-body .pl-sr .pl-sra { - color: #032f62; -} - -.markdown-body .pl-v, -.markdown-body .pl-smw { - color: #e36209; -} - -.markdown-body .pl-bu { - color: #b31d28; -} - -.markdown-body .pl-ii { - color: #fafbfc; - background-color: #b31d28; -} - -.markdown-body .pl-c2 { - color: #fafbfc; - background-color: #d73a49; -} - -.markdown-body .pl-c2::before { - content: "^M"; -} - -.markdown-body .pl-sr .pl-cce { - font-weight: bold; - color: #22863a; -} - -.markdown-body .pl-ml { - color: #735c0f; -} - -.markdown-body .pl-mh, -.markdown-body .pl-mh .pl-en, -.markdown-body .pl-ms { - font-weight: bold; - color: #005cc5; -} - -.markdown-body .pl-mi { - font-style: italic; - color: #24292e; -} - -.markdown-body .pl-mb { - font-weight: bold; - color: #24292e; -} - -.markdown-body .pl-md { - color: #b31d28; - background-color: #ffeef0; -} - -.markdown-body .pl-mi1 { - color: #22863a; - background-color: #f0fff4; -} - -.markdown-body .pl-mc { - color: #e36209; - background-color: #ffebda; -} - -.markdown-body .pl-mi2 { - color: #f6f8fa; - background-color: #005cc5; -} - -.markdown-body .pl-mdr { - font-weight: bold; - color: #6f42c1; -} - -.markdown-body .pl-ba { - color: #586069; -} - -.markdown-body .pl-sg { - color: #959da5; -} - -.markdown-body .pl-corl { - text-decoration: underline; - color: #032f62; -} - -.markdown-body .octicon { - display: inline-block; - vertical-align: text-top; - fill: currentColor; -} - -.markdown-body a { - background-color: transparent; -} - -.markdown-body a:active, -.markdown-body a:hover { - outline-width: 0; -} - -.markdown-body strong { - font-weight: inherit; -} - -.markdown-body strong { - font-weight: bolder; -} - -.markdown-body h1 { - font-size: 2em; - margin: 0.67em 0; -} - -.markdown-body img { - border-style: none; -} - -.markdown-body code, -.markdown-body kbd, -.markdown-body pre { - font-family: monospace, monospace; - font-size: 1em; -} - -.markdown-body hr { - box-sizing: content-box; - height: 0; - overflow: visible; -} - -.markdown-body input { - font: inherit; - margin: 0; -} - -.markdown-body input { - overflow: visible; -} - -.markdown-body [type="checkbox"] { - box-sizing: border-box; - padding: 0; -} - -.markdown-body * { - box-sizing: border-box; -} - -.markdown-body input { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -.markdown-body a { - color: #0366d6; - text-decoration: none; -} - -.markdown-body a:hover { - color: #0366d6; - text-decoration: underline; -} - -.markdown-body strong { - font-weight: 600; -} - -.markdown-body hr { - height: 0; - margin: 15px 0; - overflow: hidden; - background: transparent; - border: 0; - border-bottom: 1px solid #dfe2e5; -} - -.markdown-body hr::before { - display: table; - content: ""; -} - -.markdown-body hr::after { - display: table; - clear: both; - content: ""; -} - -.markdown-body table { - border-spacing: 0; - border-collapse: collapse; -} - -.markdown-body td, -.markdown-body th { - padding: 0; -} - -.markdown-body h1, -.markdown-body h2, -.markdown-body h3, -.markdown-body h4, -.markdown-body h5, -.markdown-body h6 { - margin-top: 0; - margin-bottom: 0; -} - -.markdown-body h1 { - font-size: 32px; - font-weight: 600; -} - -.markdown-body h2 { - font-size: 24px; - font-weight: 600; -} - -.markdown-body h3 { - font-size: 20px; - font-weight: 600; -} - -.markdown-body h4 { - font-size: 16px; - font-weight: 600; -} - -.markdown-body h5 { - font-size: 14px; - font-weight: 600; -} - -.markdown-body h6 { - font-size: 12px; - font-weight: 600; -} - -.markdown-body p { - margin-top: 0; - margin-bottom: 10px; -} - -.markdown-body blockquote { - margin: 0; -} - -.markdown-body ul, -.markdown-body ol { - padding-left: 0; - margin-top: 0; - margin-bottom: 0; -} - -.markdown-body ol ol, -.markdown-body ul ol { - list-style-type: lower-roman; -} - -.markdown-body ul ul ol, -.markdown-body ul ol ol, -.markdown-body ol ul ol, -.markdown-body ol ol ol { - list-style-type: lower-alpha; -} - -.markdown-body dd { - margin-left: 0; -} - -.markdown-body code { - font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; - font-size: 12px; -} - -.markdown-body pre { - margin-top: 0; - margin-bottom: 0; - font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; - font-size: 12px; -} - -.markdown-body .octicon { - vertical-align: text-bottom; -} - -.markdown-body .pl-0 { - padding-left: 0 !important; -} - -.markdown-body .pl-1 { - padding-left: 4px !important; -} - -.markdown-body .pl-2 { - padding-left: 8px !important; -} - -.markdown-body .pl-3 { - padding-left: 16px !important; -} - -.markdown-body .pl-4 { - padding-left: 24px !important; -} - -.markdown-body .pl-5 { - padding-left: 32px !important; -} - -.markdown-body .pl-6 { - padding-left: 40px !important; -} - -.markdown-body::before { - display: table; - content: ""; -} - -.markdown-body::after { - display: table; - clear: both; - content: ""; -} - -.markdown-body>*:first-child { - margin-top: 0 !important; -} - -.markdown-body>*:last-child { - margin-bottom: 0 !important; -} - -.markdown-body a:not([href]) { - color: inherit; - text-decoration: none; -} - -.markdown-body .anchor { - float: left; - padding-right: 4px; - margin-left: -20px; - line-height: 1; -} - -.markdown-body .anchor:focus { - outline: none; -} - -.markdown-body p, -.markdown-body blockquote, -.markdown-body ul, -.markdown-body ol, -.markdown-body dl, -.markdown-body table, -.markdown-body pre { - margin-top: 0; - margin-bottom: 16px; -} - -.markdown-body hr { - height: 0.25em; - padding: 0; - margin: 24px 0; - background-color: #e1e4e8; - border: 0; -} - -.markdown-body blockquote { - padding: 0 1em; - color: #6a737d; - border-left: 0.25em solid #dfe2e5; -} - -.markdown-body blockquote>:first-child { - margin-top: 0; -} - -.markdown-body blockquote>:last-child { - margin-bottom: 0; -} - -.markdown-body kbd { - display: inline-block; - padding: 3px 5px; - font-size: 11px; - line-height: 10px; - color: #444d56; - vertical-align: middle; - background-color: #fafbfc; - border: solid 1px #c6cbd1; - border-bottom-color: #959da5; - border-radius: 3px; - box-shadow: inset 0 -1px 0 #959da5; -} - -.markdown-body h1, -.markdown-body h2, -.markdown-body h3, -.markdown-body h4, -.markdown-body h5, -.markdown-body h6 { - margin-top: 24px; - margin-bottom: 16px; - font-weight: 600; - line-height: 1.25; -} - -.markdown-body h1 .octicon-link, -.markdown-body h2 .octicon-link, -.markdown-body h3 .octicon-link, -.markdown-body h4 .octicon-link, -.markdown-body h5 .octicon-link, -.markdown-body h6 .octicon-link { - color: #1b1f23; - vertical-align: middle; - visibility: hidden; -} - -.markdown-body h1:hover .anchor, -.markdown-body h2:hover .anchor, -.markdown-body h3:hover .anchor, -.markdown-body h4:hover .anchor, -.markdown-body h5:hover .anchor, -.markdown-body h6:hover .anchor { - text-decoration: none; -} - -.markdown-body h1:hover .anchor .octicon-link, -.markdown-body h2:hover .anchor .octicon-link, -.markdown-body h3:hover .anchor .octicon-link, -.markdown-body h4:hover .anchor .octicon-link, -.markdown-body h5:hover .anchor .octicon-link, -.markdown-body h6:hover .anchor .octicon-link { - visibility: visible; -} - -.markdown-body h1 { - padding-bottom: 0.3em; - font-size: 2em; - border-bottom: 1px solid #eaecef; -} - -.markdown-body h2 { - padding-bottom: 0.3em; - font-size: 1.5em; - border-bottom: 1px solid #eaecef; -} - -.markdown-body h3 { - font-size: 1.25em; -} - -.markdown-body h4 { - font-size: 1em; -} - -.markdown-body h5 { - font-size: 0.875em; -} - -.markdown-body h6 { - font-size: 0.85em; - color: #6a737d; -} - -.markdown-body ul, -.markdown-body ol { - padding-left: 2em; -} - -.markdown-body ul ul, -.markdown-body ul ol, -.markdown-body ol ol, -.markdown-body ol ul { - margin-top: 0; - margin-bottom: 0; -} - -.markdown-body li { - word-wrap: break-all; -} - -.markdown-body li>p { - margin-top: 16px; -} - -.markdown-body li+li { - margin-top: 0.25em; -} - -.markdown-body dl { - padding: 0; -} - -.markdown-body dl dt { - padding: 0; - margin-top: 16px; - font-size: 1em; - font-style: italic; - font-weight: 600; -} - -.markdown-body dl dd { - padding: 0 16px; - margin-bottom: 16px; -} - -.markdown-body table { - display: block; - width: 100%; - overflow: auto; -} - -.markdown-body table th { - font-weight: 600; -} - -.markdown-body table th, -.markdown-body table td { - padding: 6px 13px; - border: 1px solid #dfe2e5; -} - -.markdown-body table tr { - background-color: #fff; - border-top: 1px solid #c6cbd1; -} - -.markdown-body table tr:nth-child(2n) { - background-color: #f6f8fa; -} - -.markdown-body img { - max-width: 100%; - box-sizing: content-box; - background-color: #fff; -} - -.markdown-body img[align=right] { - padding-left: 20px; -} - -.markdown-body img[align=left] { - padding-right: 20px; -} - -.markdown-body code { - padding: 0.2em 0.4em; - margin: 0; - font-size: 85%; - background-color: rgba(27,31,35,0.05); - border-radius: 3px; -} - -.markdown-body pre { - word-wrap: normal; -} - -.markdown-body pre>code { - padding: 0; - margin: 0; - font-size: 100%; - word-break: normal; - white-space: pre; - background: transparent; - border: 0; -} - -.markdown-body .highlight { - margin-bottom: 16px; -} - -.markdown-body .highlight pre { - margin-bottom: 0; - word-break: normal; -} - -.markdown-body .highlight pre, -.markdown-body pre { - padding: 16px; - overflow: auto; - font-size: 85%; - line-height: 1.45; - background-color: #f6f8fa; - border-radius: 3px; -} - -.markdown-body pre code { - display: inline; - max-width: auto; - padding: 0; - margin: 0; - overflow: visible; - line-height: inherit; - word-wrap: normal; - background-color: transparent; - border: 0; -} - -.markdown-body .full-commit .btn-outline:not(:disabled):hover { - color: #005cc5; - border-color: #005cc5; -} - -.markdown-body kbd { - display: inline-block; - padding: 3px 5px; - font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; - line-height: 10px; - color: #444d56; - vertical-align: middle; - background-color: #fafbfc; - border: solid 1px #d1d5da; - border-bottom-color: #c6cbd1; - border-radius: 3px; - box-shadow: inset 0 -1px 0 #c6cbd1; -} - -.markdown-body :checked+.radio-label { - position: relative; - z-index: 1; - border-color: #0366d6; -} - -.markdown-body .task-list-item { - list-style-type: none; -} - -.markdown-body .task-list-item+.task-list-item { - margin-top: 3px; -} - -.markdown-body .task-list-item input { - margin: 0 0.2em 0.25em -1.6em; - vertical-align: middle; -} - -.markdown-body hr { - border-bottom-color: #eee; -} - - /* 代码高亮 */ - - .markdown-body pre code { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #f6f8fa; - color: #24292e; -} - -/* - -github.com style (c) Vasily Polovnyov - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #333; - background: #f8f8f8; -} - -.hljs-comment, -.hljs-quote { - color: #998; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-subst { - color: #333; - font-weight: bold; -} - -.hljs-number, -.hljs-literal, -.hljs-variable, -.hljs-template-variable, -.hljs-tag .hljs-attr { - color: #008080; -} - -.hljs-string, -.hljs-doctag { - color: #d14; -} - -.hljs-title, -.hljs-section, -.hljs-selector-id { - color: #900; - font-weight: bold; -} - -.hljs-subst { - font-weight: normal; -} - -.hljs-type, -.hljs-class .hljs-title { - color: #458; - font-weight: bold; -} - -.hljs-tag, -.hljs-name, -.hljs-attribute { - color: #000080; - font-weight: normal; -} - -.hljs-regexp, -.hljs-link { - color: #009926; -} - -.hljs-symbol, -.hljs-bullet { - color: #990073; -} - -.hljs-built_in, -.hljs-builtin-name { - color: #0086b3; -} - -.hljs-meta { - color: #999; - font-weight: bold; -} - -.hljs-deletion { - background: #fdd; -} - -.hljs-addition { - background: #dfd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - - -// katex 样式 -$katexPath: 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/'; -@font-face { - font-family:KaTeX_AMS; - src:url(#{$katexPath}fonts/KaTeX_AMS-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_AMS-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_AMS-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_AMS-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_AMS-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Caligraphic; - src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.eot); - src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.ttf) format('ttf'); - font-weight:700; - font-style:normal -} -@font-face { - font-family:KaTeX_Caligraphic; - src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Fraktur; - src:url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.eot); - src:url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.ttf) format('ttf'); - font-weight:700; - font-style:normal -} -@font-face { - font-family:KaTeX_Fraktur; - src:url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Main; - src:url(#{$katexPath}fonts/KaTeX_Main-Bold.eot); - src:url(#{$katexPath}fonts/KaTeX_Main-Bold.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Main-Bold.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Main-Bold.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Main-Bold.ttf) format('ttf'); - font-weight:700; - font-style:normal -} -@font-face { - font-family:KaTeX_Main; - src:url(#{$katexPath}fonts/KaTeX_Main-Italic.eot); - src:url(#{$katexPath}fonts/KaTeX_Main-Italic.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Main-Italic.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Main-Italic.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Main-Italic.ttf) format('ttf'); - font-weight:400; - font-style:italic -} -@font-face { - font-family:KaTeX_Main; - src:url(#{$katexPath}fonts/KaTeX_Main-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Main-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Main-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Main-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Main-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Math; - src:url(#{$katexPath}fonts/KaTeX_Math-Italic.eot); - src:url(#{$katexPath}fonts/KaTeX_Math-Italic.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Math-Italic.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Math-Italic.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Math-Italic.ttf) format('ttf'); - font-weight:400; - font-style:italic -} -@font-face { - font-family:KaTeX_SansSerif; - src:url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Script; - src:url(#{$katexPath}fonts/KaTeX_Script-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Script-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Script-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Script-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Script-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Size1; - src:url(#{$katexPath}fonts/KaTeX_Size1-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Size1-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size1-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size1-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size1-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Size2; - src:url(#{$katexPath}fonts/KaTeX_Size2-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Size2-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size2-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size2-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size2-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Size3; - src:url(#{$katexPath}fonts/KaTeX_Size3-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Size3-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size3-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size3-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size3-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Size4; - src:url(#{$katexPath}fonts/KaTeX_Size4-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Size4-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size4-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size4-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size4-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -@font-face { - font-family:KaTeX_Typewriter; - src:url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.eot); - src:url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.ttf) format('ttf'); - font-weight:400; - font-style:normal -} -.katex-display { - display:block; - margin:1em 0; - text-align:center -} -.katex-display>.katex { - display:inline-block -} -.katex { - font:400 1.21em KaTeX_Main; - line-height:1.2; - white-space:nowrap; - text-indent:0 -} -.katex .katex-html { - display:inline-block -} -.katex .katex-mathml { - position:absolute; - clip:rect(1px, 1px, 1px, 1px); - padding:0; - border:0; - height:1px; - width:1px; - overflow:hidden -} -.katex .base, .katex .strut { - display:inline-block -} -.katex .mathit { - font-family:KaTeX_Math; - font-style:italic -} -.katex .mathbf { - font-family:KaTeX_Main; - font-weight:700 -} -.katex .amsrm, .katex .mathbb { - font-family:KaTeX_AMS -} -.katex .mathcal { - font-family:KaTeX_Caligraphic -} -.katex .mathfrak { - font-family:KaTeX_Fraktur -} -.katex .mathtt { - font-family:KaTeX_Typewriter -} -.katex .mathscr { - font-family:KaTeX_Script -} -.katex .mathsf { - font-family:KaTeX_SansSerif -} -.katex .mainit { - font-family:KaTeX_Main; - font-style:italic -} -.katex .textstyle>.mord+.mop { - margin-left:.16667em -} -.katex .textstyle>.mord+.mbin { - margin-left:.22222em -} -.katex .textstyle>.mord+.mrel { - margin-left:.27778em -} -.katex .textstyle>.mop+.mop, .katex .textstyle>.mop+.mord, .katex .textstyle>.mord+.minner { - margin-left:.16667em -} -.katex .textstyle>.mop+.mrel { - margin-left:.27778em -} -.katex .textstyle>.mop+.minner { - margin-left:.16667em -} -.katex .textstyle>.mbin+.minner, .katex .textstyle>.mbin+.mop, .katex .textstyle>.mbin+.mopen, .katex .textstyle>.mbin+.mord { - margin-left:.22222em -} -.katex .textstyle>.mrel+.minner, .katex .textstyle>.mrel+.mop, .katex .textstyle>.mrel+.mopen, .katex .textstyle>.mrel+.mord { - margin-left:.27778em -} -.katex .textstyle>.mclose+.mop { - margin-left:.16667em -} -.katex .textstyle>.mclose+.mbin { - margin-left:.22222em -} -.katex .textstyle>.mclose+.mrel { - margin-left:.27778em -} -.katex .textstyle>.mclose+.minner, .katex .textstyle>.minner+.mop, .katex .textstyle>.minner+.mord, .katex .textstyle>.mpunct+.mclose, .katex .textstyle>.mpunct+.minner, .katex .textstyle>.mpunct+.mop, .katex .textstyle>.mpunct+.mopen, .katex .textstyle>.mpunct+.mord, .katex .textstyle>.mpunct+.mpunct, .katex .textstyle>.mpunct+.mrel { - margin-left:.16667em -} -.katex .textstyle>.minner+.mbin { - margin-left:.22222em -} -.katex .textstyle>.minner+.mrel { - margin-left:.27778em -} -.katex .mclose+.mop, .katex .minner+.mop, .katex .mop+.mop, .katex .mop+.mord, .katex .mord+.mop, .katex .textstyle>.minner+.minner, .katex .textstyle>.minner+.mopen, .katex .textstyle>.minner+.mpunct { - margin-left:.16667em -} -.katex .reset-textstyle.textstyle { - font-size:1em -} -.katex .reset-textstyle.scriptstyle { - font-size:.7em -} -.katex .reset-textstyle.scriptscriptstyle { - font-size:.5em -} -.katex .reset-scriptstyle.textstyle { - font-size:1.42857em -} -.katex .reset-scriptstyle.scriptstyle { - font-size:1em -} -.katex .reset-scriptstyle.scriptscriptstyle { - font-size:.71429em -} -.katex .reset-scriptscriptstyle.textstyle { - font-size:2em -} -.katex .reset-scriptscriptstyle.scriptstyle { - font-size:1.4em -} -.katex .reset-scriptscriptstyle.scriptscriptstyle { - font-size:1em -} -.katex .style-wrap { - position:relative -} -.katex .vlist { - display:inline-block -} -.katex .vlist>span { - display:block; - height:0; - position:relative -} -.katex .vlist>span>span { - display:inline-block -} -.katex .vlist .baseline-fix { - display:inline-table; - table-layout:fixed -} -.katex .msupsub { - text-align:left -} -.katex .mfrac>span>span { - text-align:center -} -.katex .mfrac .frac-line { - width:100% -} -.katex .mfrac .frac-line:before { - border-bottom-style:solid; - border-bottom-width:1px; - content:""; - display:block -} -.katex .mfrac .frac-line:after { - border-bottom-style:solid; - border-bottom-width:.04em; - content:""; - display:block; - margin-top:-1px -} -.katex .mspace { - display:inline-block -} -.katex .mspace.negativethinspace { - margin-left:-.16667em -} -.katex .mspace.thinspace { - width:.16667em -} -.katex .mspace.mediumspace { - width:.22222em -} -.katex .mspace.thickspace { - width:.27778em -} -.katex .mspace.enspace { - width:.5em -} -.katex .mspace.quad { - width:1em -} -.katex .mspace.qquad { - width:2em -} -.katex .llap, .katex .rlap { - width:0; - position:relative -} -.katex .llap>.inner, .katex .rlap>.inner { - position:absolute -} -.katex .llap>.fix, .katex .rlap>.fix { - display:inline-block -} -.katex .llap>.inner { - right:0 -} -.katex .rlap>.inner { - left:0 -} -.katex .katex-logo .a { - font-size:.75em; - margin-left:-.32em; - position:relative; - top:-.2em -} -.katex .katex-logo .t { - margin-left:-.23em -} -.katex .katex-logo .e { - margin-left:-.1667em; - position:relative; - top:.2155em -} -.katex .katex-logo .x { - margin-left:-.125em -} -.katex .rule { - display:inline-block; - border-style:solid; - position:relative -} -.katex .overline .overline-line { - width:100% -} -.katex .overline .overline-line:before { - border-bottom-style:solid; - border-bottom-width:1px; - content:""; - display:block -} -.katex .overline .overline-line:after { - border-bottom-style:solid; - border-bottom-width:.04em; - content:""; - display:block; - margin-top:-1px -} -.katex .sqrt>.sqrt-sign { - position:relative -} -.katex .sqrt .sqrt-line { - width:100% -} -.katex .sqrt .sqrt-line:before { - border-bottom-style:solid; - border-bottom-width:1px; - content:""; - display:block -} -.katex .sqrt .sqrt-line:after { - border-bottom-style:solid; - border-bottom-width:.04em; - content:""; - display:block; - margin-top:-1px -} -.katex .sqrt>.root { - margin-left:.27777778em; - margin-right:-.55555556em -} -.katex .fontsize-ensurer, .katex .sizing { - display:inline-block -} -.katex .fontsize-ensurer.reset-size1.size1, .katex .sizing.reset-size1.size1 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size1.size2, .katex .sizing.reset-size1.size2 { - font-size:1.4em -} -.katex .fontsize-ensurer.reset-size1.size3, .katex .sizing.reset-size1.size3 { - font-size:1.6em -} -.katex .fontsize-ensurer.reset-size1.size4, .katex .sizing.reset-size1.size4 { - font-size:1.8em -} -.katex .fontsize-ensurer.reset-size1.size5, .katex .sizing.reset-size1.size5 { - font-size:2em -} -.katex .fontsize-ensurer.reset-size1.size6, .katex .sizing.reset-size1.size6 { - font-size:2.4em -} -.katex .fontsize-ensurer.reset-size1.size7, .katex .sizing.reset-size1.size7 { - font-size:2.88em -} -.katex .fontsize-ensurer.reset-size1.size8, .katex .sizing.reset-size1.size8 { - font-size:3.46em -} -.katex .fontsize-ensurer.reset-size1.size9, .katex .sizing.reset-size1.size9 { - font-size:4.14em -} -.katex .fontsize-ensurer.reset-size1.size10, .katex .sizing.reset-size1.size10 { - font-size:4.98em -} -.katex .fontsize-ensurer.reset-size2.size1, .katex .sizing.reset-size2.size1 { - font-size:.71428571em -} -.katex .fontsize-ensurer.reset-size2.size2, .katex .sizing.reset-size2.size2 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size2.size3, .katex .sizing.reset-size2.size3 { - font-size:1.14285714em -} -.katex .fontsize-ensurer.reset-size2.size4, .katex .sizing.reset-size2.size4 { - font-size:1.28571429em -} -.katex .fontsize-ensurer.reset-size2.size5, .katex .sizing.reset-size2.size5 { - font-size:1.42857143em -} -.katex .fontsize-ensurer.reset-size2.size6, .katex .sizing.reset-size2.size6 { - font-size:1.71428571em -} -.katex .fontsize-ensurer.reset-size2.size7, .katex .sizing.reset-size2.size7 { - font-size:2.05714286em -} -.katex .fontsize-ensurer.reset-size2.size8, .katex .sizing.reset-size2.size8 { - font-size:2.47142857em -} -.katex .fontsize-ensurer.reset-size2.size9, .katex .sizing.reset-size2.size9 { - font-size:2.95714286em -} -.katex .fontsize-ensurer.reset-size2.size10, .katex .sizing.reset-size2.size10 { - font-size:3.55714286em -} -.katex .fontsize-ensurer.reset-size3.size1, .katex .sizing.reset-size3.size1 { - font-size:.625em -} -.katex .fontsize-ensurer.reset-size3.size2, .katex .sizing.reset-size3.size2 { - font-size:.875em -} -.katex .fontsize-ensurer.reset-size3.size3, .katex .sizing.reset-size3.size3 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size3.size4, .katex .sizing.reset-size3.size4 { - font-size:1.125em -} -.katex .fontsize-ensurer.reset-size3.size5, .katex .sizing.reset-size3.size5 { - font-size:1.25em -} -.katex .fontsize-ensurer.reset-size3.size6, .katex .sizing.reset-size3.size6 { - font-size:1.5em -} -.katex .fontsize-ensurer.reset-size3.size7, .katex .sizing.reset-size3.size7 { - font-size:1.8em -} -.katex .fontsize-ensurer.reset-size3.size8, .katex .sizing.reset-size3.size8 { - font-size:2.1625em -} -.katex .fontsize-ensurer.reset-size3.size9, .katex .sizing.reset-size3.size9 { - font-size:2.5875em -} -.katex .fontsize-ensurer.reset-size3.size10, .katex .sizing.reset-size3.size10 { - font-size:3.1125em -} -.katex .fontsize-ensurer.reset-size4.size1, .katex .sizing.reset-size4.size1 { - font-size:.55555556em -} -.katex .fontsize-ensurer.reset-size4.size2, .katex .sizing.reset-size4.size2 { - font-size:.77777778em -} -.katex .fontsize-ensurer.reset-size4.size3, .katex .sizing.reset-size4.size3 { - font-size:.88888889em -} -.katex .fontsize-ensurer.reset-size4.size4, .katex .sizing.reset-size4.size4 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size4.size5, .katex .sizing.reset-size4.size5 { - font-size:1.11111111em -} -.katex .fontsize-ensurer.reset-size4.size6, .katex .sizing.reset-size4.size6 { - font-size:1.33333333em -} -.katex .fontsize-ensurer.reset-size4.size7, .katex .sizing.reset-size4.size7 { - font-size:1.6em -} -.katex .fontsize-ensurer.reset-size4.size8, .katex .sizing.reset-size4.size8 { - font-size:1.92222222em -} -.katex .fontsize-ensurer.reset-size4.size9, .katex .sizing.reset-size4.size9 { - font-size:2.3em -} -.katex .fontsize-ensurer.reset-size4.size10, .katex .sizing.reset-size4.size10 { - font-size:2.76666667em -} -.katex .fontsize-ensurer.reset-size5.size1, .katex .sizing.reset-size5.size1 { - font-size:.5em -} -.katex .fontsize-ensurer.reset-size5.size2, .katex .sizing.reset-size5.size2 { - font-size:.7em -} -.katex .fontsize-ensurer.reset-size5.size3, .katex .sizing.reset-size5.size3 { - font-size:.8em -} -.katex .fontsize-ensurer.reset-size5.size4, .katex .sizing.reset-size5.size4 { - font-size:.9em -} -.katex .fontsize-ensurer.reset-size5.size5, .katex .sizing.reset-size5.size5 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size5.size6, .katex .sizing.reset-size5.size6 { - font-size:1.2em -} -.katex .fontsize-ensurer.reset-size5.size7, .katex .sizing.reset-size5.size7 { - font-size:1.44em -} -.katex .fontsize-ensurer.reset-size5.size8, .katex .sizing.reset-size5.size8 { - font-size:1.73em -} -.katex .fontsize-ensurer.reset-size5.size9, .katex .sizing.reset-size5.size9 { - font-size:2.07em -} -.katex .fontsize-ensurer.reset-size5.size10, .katex .sizing.reset-size5.size10 { - font-size:2.49em -} -.katex .fontsize-ensurer.reset-size6.size1, .katex .sizing.reset-size6.size1 { - font-size:.41666667em -} -.katex .fontsize-ensurer.reset-size6.size2, .katex .sizing.reset-size6.size2 { - font-size:.58333333em -} -.katex .fontsize-ensurer.reset-size6.size3, .katex .sizing.reset-size6.size3 { - font-size:.66666667em -} -.katex .fontsize-ensurer.reset-size6.size4, .katex .sizing.reset-size6.size4 { - font-size:.75em -} -.katex .fontsize-ensurer.reset-size6.size5, .katex .sizing.reset-size6.size5 { - font-size:.83333333em -} -.katex .fontsize-ensurer.reset-size6.size6, .katex .sizing.reset-size6.size6 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size6.size7, .katex .sizing.reset-size6.size7 { - font-size:1.2em -} -.katex .fontsize-ensurer.reset-size6.size8, .katex .sizing.reset-size6.size8 { - font-size:1.44166667em -} -.katex .fontsize-ensurer.reset-size6.size9, .katex .sizing.reset-size6.size9 { - font-size:1.725em -} -.katex .fontsize-ensurer.reset-size6.size10, .katex .sizing.reset-size6.size10 { - font-size:2.075em -} -.katex .fontsize-ensurer.reset-size7.size1, .katex .sizing.reset-size7.size1 { - font-size:.34722222em -} -.katex .fontsize-ensurer.reset-size7.size2, .katex .sizing.reset-size7.size2 { - font-size:.48611111em -} -.katex .fontsize-ensurer.reset-size7.size3, .katex .sizing.reset-size7.size3 { - font-size:.55555556em -} -.katex .fontsize-ensurer.reset-size7.size4, .katex .sizing.reset-size7.size4 { - font-size:.625em -} -.katex .fontsize-ensurer.reset-size7.size5, .katex .sizing.reset-size7.size5 { - font-size:.69444444em -} -.katex .fontsize-ensurer.reset-size7.size6, .katex .sizing.reset-size7.size6 { - font-size:.83333333em -} -.katex .fontsize-ensurer.reset-size7.size7, .katex .sizing.reset-size7.size7 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size7.size8, .katex .sizing.reset-size7.size8 { - font-size:1.20138889em -} -.katex .fontsize-ensurer.reset-size7.size9, .katex .sizing.reset-size7.size9 { - font-size:1.4375em -} -.katex .fontsize-ensurer.reset-size7.size10, .katex .sizing.reset-size7.size10 { - font-size:1.72916667em -} -.katex .fontsize-ensurer.reset-size8.size1, .katex .sizing.reset-size8.size1 { - font-size:.28901734em -} -.katex .fontsize-ensurer.reset-size8.size2, .katex .sizing.reset-size8.size2 { - font-size:.40462428em -} -.katex .fontsize-ensurer.reset-size8.size3, .katex .sizing.reset-size8.size3 { - font-size:.46242775em -} -.katex .fontsize-ensurer.reset-size8.size4, .katex .sizing.reset-size8.size4 { - font-size:.52023121em -} -.katex .fontsize-ensurer.reset-size8.size5, .katex .sizing.reset-size8.size5 { - font-size:.57803468em -} -.katex .fontsize-ensurer.reset-size8.size6, .katex .sizing.reset-size8.size6 { - font-size:.69364162em -} -.katex .fontsize-ensurer.reset-size8.size7, .katex .sizing.reset-size8.size7 { - font-size:.83236994em -} -.katex .fontsize-ensurer.reset-size8.size8, .katex .sizing.reset-size8.size8 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size8.size9, .katex .sizing.reset-size8.size9 { - font-size:1.19653179em -} -.katex .fontsize-ensurer.reset-size8.size10, .katex .sizing.reset-size8.size10 { - font-size:1.43930636em -} -.katex .fontsize-ensurer.reset-size9.size1, .katex .sizing.reset-size9.size1 { - font-size:.24154589em -} -.katex .fontsize-ensurer.reset-size9.size2, .katex .sizing.reset-size9.size2 { - font-size:.33816425em -} -.katex .fontsize-ensurer.reset-size9.size3, .katex .sizing.reset-size9.size3 { - font-size:.38647343em -} -.katex .fontsize-ensurer.reset-size9.size4, .katex .sizing.reset-size9.size4 { - font-size:.43478261em -} -.katex .fontsize-ensurer.reset-size9.size5, .katex .sizing.reset-size9.size5 { - font-size:.48309179em -} -.katex .fontsize-ensurer.reset-size9.size6, .katex .sizing.reset-size9.size6 { - font-size:.57971014em -} -.katex .fontsize-ensurer.reset-size9.size7, .katex .sizing.reset-size9.size7 { - font-size:.69565217em -} -.katex .fontsize-ensurer.reset-size9.size8, .katex .sizing.reset-size9.size8 { - font-size:.83574879em -} -.katex .fontsize-ensurer.reset-size9.size9, .katex .sizing.reset-size9.size9 { - font-size:1em -} -.katex .fontsize-ensurer.reset-size9.size10, .katex .sizing.reset-size9.size10 { - font-size:1.20289855em -} -.katex .fontsize-ensurer.reset-size10.size1, .katex .sizing.reset-size10.size1 { - font-size:.20080321em -} -.katex .fontsize-ensurer.reset-size10.size2, .katex .sizing.reset-size10.size2 { - font-size:.2811245em -} -.katex .fontsize-ensurer.reset-size10.size3, .katex .sizing.reset-size10.size3 { - font-size:.32128514em -} -.katex .fontsize-ensurer.reset-size10.size4, .katex .sizing.reset-size10.size4 { - font-size:.36144578em -} -.katex .fontsize-ensurer.reset-size10.size5, .katex .sizing.reset-size10.size5 { - font-size:.40160643em -} -.katex .fontsize-ensurer.reset-size10.size6, .katex .sizing.reset-size10.size6 { - font-size:.48192771em -} -.katex .fontsize-ensurer.reset-size10.size7, .katex .sizing.reset-size10.size7 { - font-size:.57831325em -} -.katex .fontsize-ensurer.reset-size10.size8, .katex .sizing.reset-size10.size8 { - font-size:.69477912em -} -.katex .fontsize-ensurer.reset-size10.size9, .katex .sizing.reset-size10.size9 { - font-size:.8313253em -} -.katex .fontsize-ensurer.reset-size10.size10, .katex .sizing.reset-size10.size10 { - font-size:1em -} -.katex .delimsizing.size1 { - font-family:KaTeX_Size1 -} -.katex .delimsizing.size2 { - font-family:KaTeX_Size2 -} -.katex .delimsizing.size3 { - font-family:KaTeX_Size3 -} -.katex .delimsizing.size4 { - font-family:KaTeX_Size4 -} -.katex .delimsizing.mult .delim-size1>span { - font-family:KaTeX_Size1 -} -.katex .delimsizing.mult .delim-size4>span { - font-family:KaTeX_Size4 -} -.katex .nulldelimiter { - display:inline-block; - width:.12em -} -.katex .op-symbol { - position:relative -} -.katex .op-symbol.small-op { - font-family:KaTeX_Size1 -} -.katex .op-symbol.large-op { - font-family:KaTeX_Size2 -} -.katex .accent>.vlist>span, .katex .op-limits>.vlist>span { - text-align:center -} -.katex .accent .accent-body>span { - width:0 -} -.katex .accent .accent-body.accent-vec>span { - position:relative; - left:.326em -} -.katex .mtable .vertical-separator { - display:inline-block; - margin:0 -.025em; - border-right:.05em solid #000 -} -.katex .mtable .arraycolsep { - display:inline-block -} -.katex .mtable .col-align-c>.vlist { - text-align:center -} -.katex .mtable .col-align-l>.vlist { - text-align:left -} -.katex .mtable .col-align-r>.vlist { - text-align:right -} diff --git a/sop-website/sop-portal/src/pages/blog/blogItem.jsx b/sop-website/sop-portal/src/pages/blog/blogItem.jsx deleted file mode 100644 index bd9eb5ee..00000000 --- a/sop-website/sop-portal/src/pages/blog/blogItem.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { autobind } from 'core-decorators'; -import { getLink } from '../../../utils'; - -import './blogItem.scss'; - -@autobind -class BlogItem extends React.Component { - constructor(props) { - super(props); - this.state = { - isHovered: false, - }; - } - - onMouseOver() { - this.setState({ - isHovered: true, - }); - } - - onMouseOut() { - this.setState({ - isHovered: false, - }); - } - - render() { - const { dataSource } = this.props; - const { link, target, title, author, companyIcon, companyIconHover, dateStr, desc } = dataSource; - const { isHovered } = this.state; - return ( - -
    - - {title} -
    -
    - {author} - { - companyIcon ? : null - } - {dateStr} -
    -

    {desc}

    -
    - ); - } -} - -export default BlogItem; diff --git a/sop-website/sop-portal/src/pages/blog/blogItem.scss b/sop-website/sop-portal/src/pages/blog/blogItem.scss deleted file mode 100644 index c7b3b028..00000000 --- a/sop-website/sop-portal/src/pages/blog/blogItem.scss +++ /dev/null @@ -1,57 +0,0 @@ -.blog-item { - box-sizing: border-box; - display: block; - width: 100%; - padding: 20px; - margin-bottom: 40px; - background: #F8F8F8; - .title { - img { - width: 16px; - height: 20px; - margin-right: 8px; - } - span { - font-family: Avenir-Heavy; - font-size: 20px; - color: #666666; - } - } - .brief-info { - padding: 12px 0 20px; - .author { - font-family: Avenir-Heavy; - font-size: 14px; - color: #999; - margin-right: 8px; - } - img { - width: 14px; - height: 14px; - } - .date { - float: right; - font-family: Avenir-Medium; - font-size: 12px; - color: #999; - } - } - p { - font-family: Avenir-Medium; - font-size: 14px; - color: #666; - margin: 0; - } - &:hover { - .title { - span { - color: #333; - } - } - .brief-info { - .author { - color: #666; - } - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/blog/index.jsx b/sop-website/sop-portal/src/pages/blog/index.jsx deleted file mode 100644 index 1069de74..00000000 --- a/sop-website/sop-portal/src/pages/blog/index.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Language from '../../components/language'; -import Header from '../../components/header'; -import Bar from '../../components/bar'; -import PageSlider from '../../components/pageSlider'; -import BlogItem from './blogItem'; -import Footer from '../../components/footer'; -import blogConfig from '../../../site_config/blog'; -import { getLink } from '../../../utils'; -import './index.scss'; - -class Blog extends Language { - - render() { - const language = this.getLanguage(); - const dataSource = blogConfig[language]; - const blogs = dataSource.list; - return ( -
    -
    - -
    -
    - - { - blogs.map((blog, i) => ( - - )) - } - -
    -
    -

    {dataSource.postsTitle}

    - -
    -
    -
    -
    - ); - } -} - -document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); - -export default Blog; diff --git a/sop-website/sop-portal/src/pages/blog/index.scss b/sop-website/sop-portal/src/pages/blog/index.scss deleted file mode 100644 index dd46ee16..00000000 --- a/sop-website/sop-portal/src/pages/blog/index.scss +++ /dev/null @@ -1,55 +0,0 @@ -@import '../../variables.scss'; -@import '../../reset.scss'; - -.blog-list-page { - .blog-container { - max-width: $contentWidth; - margin: 0 auto; - box-sizing: border-box; - padding: 50px 8% 80px; - .col { - display: inline-block; - box-sizing: border-box; - &-18 { - width: 75%; - border-right: 1px solid #CBCCCD; - padding-right: 6%; - } - &-6 { - width: 25%; - padding-left: 20px; - vertical-align: top; - h4 { - font-family: Avenir-Heavy; - font-size: 18px; - color: #333; - margin: 0 0 20px; - } - ul { - list-style: none; - margin: 0; - padding: 0; - li { - width: 100%; - margin-bottom: 14px; - span { - font-family: Avenir-Medium; - font-size: 12px; - color: #666; - } - } - } - } - } - @media screen and (max-width: $mobileWidth){ - .left-part { - width: 100%; - border-right: none; - padding-right: 0; - } - .right-part { - display: none; - } - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/blogDetail/index.jsx b/sop-website/sop-portal/src/pages/blogDetail/index.jsx deleted file mode 100644 index 93181677..00000000 --- a/sop-website/sop-portal/src/pages/blogDetail/index.jsx +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { scroller } from 'react-scroll'; -import path from 'path'; -import 'whatwg-fetch'; // fetch polyfill -import Language from '../../components/language'; -import Header from '../../components/header'; -import Footer from '../../components/footer'; -import './index.scss'; - -// 锚点正则 -const anchorReg = /^#[^/]/; -// 相对地址正则,包括./、../、直接文件夹名称开头、直接文件开头 -const relativeReg = /^((\.{1,2}\/)|([\w-]+[/.]))/; -class BlogDetail extends Language { - - constructor(props) { - super(props); - this.state = { - __html: '', - }; - } - - componentDidMount() { - // 通过请求获取生成好的json数据,静态页和json文件在同一个目录下 - fetch(window.location.pathname.replace(/\.html$/i, '.json')) - .then(res => res.json()) - .then((md) => { - this.setState({ - __html: md && md.__html ? md.__html : '', - }); - }); - this.markdownContainer.addEventListener('click', (e) => { - const isAnchor = e.target.nodeName.toLowerCase() === 'a' && e.target.getAttribute('href') && anchorReg.test(e.target.getAttribute('href')); - if (isAnchor) { - e.preventDefault(); - const id = e.target.getAttribute('href').slice(1); - scroller.scrollTo(id, { - duration: 1000, - smooth: 'easeInOutQuint', - }); - } - }); - } - - componentDidUpdate() { - this.handleRelativeLink(); - this.handleRelativeImg(); - } - - handleRelativeLink() { - const language = this.getLanguage(); - // 获取当前文档所在文件系统中的路径 - // rootPath/en-us/blog/dir/hello.html => /blog/en-us/dir - const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); - const filePath = splitPart.join('/'); - const alinks = Array.from(this.markdownContainer.querySelectorAll('a')); - alinks.forEach((alink) => { - const href = alink.getAttribute('href'); - if (relativeReg.test(href)) { - // 文档之间有中英文之分,md的相对地址要转换为对应HTML的地址 - alink.href = `${path.join(`${window.rootPath}/${language}`, filePath, href.replace(/\.(md|markdown)$/, '.html'))}`; - } - }); - } - - handleRelativeImg() { - const language = this.getLanguage(); - // 获取当前文档所在文件系统中的路径 - // rootPath/en-us/blog/dir/hello.html => /blog/en-us/dir - const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); - splitPart.splice(2, 0, language); - const filePath = splitPart.join('/'); - const imgs = Array.from(this.markdownContainer.querySelectorAll('img')); - imgs.forEach((img) => { - const src = img.getAttribute('src'); - if (relativeReg.test(src)) { - // 图片无中英文之分 - img.src = `${path.join(window.rootPath, filePath, src)}`; - } - }); - } - - render() { - const language = this.getLanguage(); - const __html = this.props.__html || this.state.__html; - return ( -
    -
    -
    { this.markdownContainer = node; }} - dangerouslySetInnerHTML={{ __html }} - /> -
    -
    - ); - } -} - -document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); - -export default BlogDetail; diff --git a/sop-website/sop-portal/src/pages/blogDetail/index.scss b/sop-website/sop-portal/src/pages/blogDetail/index.scss deleted file mode 100644 index b2c9cd18..00000000 --- a/sop-website/sop-portal/src/pages/blogDetail/index.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import '../../variables.scss'; -@import '../../reset.scss'; -@import '../../markdown.scss'; - -.blog-detail-page { - .blog-content { - padding: 80px 20%; - margin: $headerHeight auto 0; - max-width: 735px; - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/community/contactItem.jsx b/sop-website/sop-portal/src/pages/community/contactItem.jsx deleted file mode 100644 index 38b6a0ec..00000000 --- a/sop-website/sop-portal/src/pages/community/contactItem.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { autobind } from 'core-decorators'; -import { getLink } from '../../../utils'; - -@autobind -class ContactItem extends React.Component { - constructor(props) { - super(props); - this.state = { - img: props.contact.img, - }; - } - - onMouseOver() { - this.setState({ - img: this.props.contact.imgHover, - }); - } - - onMouseOut() { - this.setState({ - img: this.props.contact.img, - }); - } - - render() { - const { contact } = this.props; - const { img } = this.state; - return ( - - -
    {contact.title}
    -
    - ); - } -} - -export default ContactItem; diff --git a/sop-website/sop-portal/src/pages/community/contributorItem.jsx b/sop-website/sop-portal/src/pages/community/contributorItem.jsx deleted file mode 100644 index 9645eda8..00000000 --- a/sop-website/sop-portal/src/pages/community/contributorItem.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { getLink } from '../../../utils'; - -const ContributorItem = (props) => { - const { contributor } = props; - const { img, title, content } = contributor; - return ( -
    - -
    {title}
    -

    {content}

    -
    - ); -}; - -export default ContributorItem; diff --git a/sop-website/sop-portal/src/pages/community/ecoItem.jsx b/sop-website/sop-portal/src/pages/community/ecoItem.jsx deleted file mode 100644 index 365d2091..00000000 --- a/sop-website/sop-portal/src/pages/community/ecoItem.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { getLink } from '../../../utils'; - -const EcoItem = (props) => { - const { eco } = props; - return ( -
    -

    {eco.title}

    -

    {eco.content}

    -
    - { - eco.tags.map((tag, i) => ( - - { - tag.text - } - - )) - } -
    -
    - ); -}; - -export default EcoItem; diff --git a/sop-website/sop-portal/src/pages/community/eventCard.jsx b/sop-website/sop-portal/src/pages/community/eventCard.jsx deleted file mode 100644 index 5760d54c..00000000 --- a/sop-website/sop-portal/src/pages/community/eventCard.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { getLink } from '../../../utils'; - -class EventCard extends React.Component { - render() { - const { event } = this.props; - return ( -
    - - - -
    -

    {event.title}

    -

    {event.content}

    - - {event.dateStr} - - -
    -
    - ); - } -} - -export default EventCard; diff --git a/sop-website/sop-portal/src/pages/community/index.jsx b/sop-website/sop-portal/src/pages/community/index.jsx deleted file mode 100644 index 007c4a57..00000000 --- a/sop-website/sop-portal/src/pages/community/index.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Language from '../../components/language'; -import Header from '../../components/header'; -import Bar from '../../components/bar'; -import Slider from '../../components/slider'; -import EventCard from './eventCard'; -import ContactItem from './contactItem'; -import ContributorItem from './contributorItem'; -import EcoItem from './ecoItem'; -import Footer from '../../components/footer'; -import communityConfig from '../../../site_config/community.jsx'; - -import './index.scss'; - -class Community extends Language { - - render() { - const language = this.getLanguage(); - const dataSource = communityConfig[language]; - return ( -
    -
    - -
    -

    {dataSource.events.title}

    - - {dataSource.events.list.map((event, i) => ( - - ))} - -
    -
    -

    {dataSource.ecos.title}

    -
    - { - dataSource.ecos.list.map((eco, i) => ( - - )) - } -
    -
    -
    -

    {dataSource.contacts.title}

    -

    {dataSource.contacts.desc}

    -
    - { - dataSource.contacts.list.map((contact, i) => ( - - )) - } -
    -
    -
    -

    {dataSource.contributorGuide.title}

    -

    {dataSource.contributorGuide.desc}

    -
    - { - dataSource.contributorGuide.list.map((contributor, i) => ( - - )) - } -
    -
    -
    -
    - ); - } -} - -document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); - -export default Community; diff --git a/sop-website/sop-portal/src/pages/community/index.scss b/sop-website/sop-portal/src/pages/community/index.scss deleted file mode 100644 index 9823a671..00000000 --- a/sop-website/sop-portal/src/pages/community/index.scss +++ /dev/null @@ -1,189 +0,0 @@ -@import '../../variables.scss'; -@import '../../reset.scss'; - -.community-page { - .events-section { - max-width: $contentWidth; - margin: 0 auto; - box-sizing: border-box; - padding: 80px 40px 60px; - @media screen and (max-width: $mobileWidth) { - padding: 0; - } - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #333; - text-align: center; - margin: 0 0 40px; - } - .event-card { - width: 373px; - font-size: 0; - img { - width: 373px; - height: 209px; - } - @media screen and (max-width: $mobileWidth / 2) { - width: 320px; - img { - width: 320px; - height: 179px; - } - } - .event-introduction { - padding: 20px; - background: #F8F8F8; - h4 { - font-family: Avenir-Heavy; - font-size: 20px; - color: #333; - margin: 0 0 10px; - } - p { - font-family: Avenir-Medium; - font-size: 14px; - color: #666; - margin: 0; - } - a { - display: inline-block; - width: 100%; - font-family: Avenir-Medium; - font-size: 12px; - color: #999; - margin-top: 10px; - .arrow { - width: 8px; - height: 13px; - float: right; - } - } - } - } - } - .contact-section { - max-width: $contentWidth; - margin: 0 auto; - box-sizing: border-box; - padding: 60px 40px 40px; - @media screen and (max-width: $mobileWidth) { - padding-left: 20px; - padding-right: 20px; - } - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #333; - text-align: center; - margin: 0 0 12px; - } - p { - font-family: Avenir-Medium; - font-size: 14px; - color: #666; - text-align: center; - margin: 0 0 40px; - } - .contact-list { - display: flex; - justify-content: space-around; - flex-wrap: wrap; - .contact-item { - display: inline-block; - text-align: center; - padding: 0 20px; - // width: 82px; - font-family: Avenir-Heavy; - font-size: 18px; - color: #999; - &:hover { - color: $brandColor; - } - img { - width: 82px; - height: 86px; - } - } - } - } - .contributor-section { - max-width: $contentWidth; - margin: 0 auto; - box-sizing: border-box; - padding: 60px 40px 40px; - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #333; - text-align: center; - margin: 0 0 12px; - } - p { - font-family: Avenir-Medium; - font-size: 14px; - color: #666; - text-align: center; - margin: 0 0 40px; - } - .contributor-list { - display: flex; - justify-content: space-around; - flex-wrap: wrap; - .contributor-item { - display: inline-block; - width: 240px; - text-align: center; - font-family: Avenir-Heavy; - font-size: 18px; - color: #999; - img { - width: 82px; - height: 86px; - } - } - } - } - .eco-section { - padding: 60px 20% 0; - max-width: 735px; - margin: 0 auto; - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #333; - text-align: center; - margin-bottom: 12px; - } - .eco-item { - margin-bottom: 30px; - h4 { - font-family: Avenir-Heavy; - font-size: 18px; - color: #333; - margin: 0 0 10px; - } - p { - font-family: Avenir-Medium; - font-size: 14px; - color: #666; - text-align: justify; - margin: 12px 0; - } - .tags { - a { - font-family: Avenir-Medium; - font-size: 12px; - color: #fff; - display: inline-block; - height: 32px; - line-height: 32px; - padding: 0 16px; - border-radius: 2px; - text-align: center; - margin: 0 10px 10px 0; - } - } - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/documentation/index.jsx b/sop-website/sop-portal/src/pages/documentation/index.jsx deleted file mode 100644 index fb53f715..00000000 --- a/sop-website/sop-portal/src/pages/documentation/index.jsx +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { scroller } from 'react-scroll'; -import 'whatwg-fetch'; // fetch polyfill -import path from 'path'; -import Language from '../../components/language'; -import Header from '../../components/header'; -import Bar from '../../components/bar'; -import Sidemenu from '../../components/sidemenu'; -import Footer from '../../components/footer'; -import docsConfig from '../../../site_config/docs'; -import './index.scss'; - -// 锚点正则 -const anchorReg = /^#[^/]/; -// 相对地址正则,包括./、../、直接文件夹名称开头、直接文件开头 -const relativeReg = /^((\.{1,2}\/)|([\w-]+[/.]))/; - -class Documentation extends Language { - - constructor(props) { - super(props); - this.state = { - __html: '', - }; - } - - componentDidMount() { - // 通过请求获取生成好的json数据,静态页和json文件在同一个目录下 - fetch(window.location.pathname.replace(/\.html$/i, '.json')) - .then(res => res.json()) - .then((md) => { - this.setState({ - __html: md && md.__html ? md.__html : '', - }); - }); - this.markdownContainer.addEventListener('click', (e) => { - const isAnchor = e.target.nodeName.toLowerCase() === 'a' && e.target.getAttribute('href') && anchorReg.test(e.target.getAttribute('href')); - if (isAnchor) { - e.preventDefault(); - const id = e.target.getAttribute('href').slice(1); - scroller.scrollTo(id, { - duration: 1000, - smooth: 'easeInOutQuint', - }); - } - }); - } - - componentDidUpdate() { - this.handleRelativeLink(); - this.handleRelativeImg(); - } - - handleRelativeLink() { - const language = this.getLanguage(); - // 获取当前文档所在文件系统中的路径 - // rootPath/en-us/docs/dir/hello.html => /docs/en-us/dir - const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); - const filePath = splitPart.join('/'); - const alinks = Array.from(this.markdownContainer.querySelectorAll('a')); - alinks.forEach((alink) => { - const href = alink.getAttribute('href'); - if (relativeReg.test(href)) { - // 文档之间有中英文之分,md的相对地址要转换为对应HTML的地址 - alink.href = `${path.join(`${window.rootPath}/${language}`, filePath, href.replace(/\.(md|markdown)$/, '.html'))}`; - } - }); - } - - handleRelativeImg() { - const language = this.getLanguage(); - // 获取当前文档所在文件系统中的路径 - // rootPath/en-us/docs/dir/hello.html => /docs/en-us/dir - const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); - splitPart.splice(2, 0, language); - const filePath = splitPart.join('/'); - const imgs = Array.from(this.markdownContainer.querySelectorAll('img')); - imgs.forEach((img) => { - const src = img.getAttribute('src'); - if (relativeReg.test(src)) { - // 图片无中英文之分 - img.src = `${path.join(window.rootPath, filePath, src)}`; - } - }); - } - - render() { - const language = this.getLanguage(); - const dataSource = docsConfig[language]; - const __html = this.props.__html || this.state.__html; - return ( -
    -
    - -
    - -
    { this.markdownContainer = node; }} - dangerouslySetInnerHTML={{ __html }} - /> -
    -
    -
    - ); - } -} - -document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); - -export default Documentation; diff --git a/sop-website/sop-portal/src/pages/documentation/index.scss b/sop-website/sop-portal/src/pages/documentation/index.scss deleted file mode 100644 index a29ea511..00000000 --- a/sop-website/sop-portal/src/pages/documentation/index.scss +++ /dev/null @@ -1,36 +0,0 @@ -@import '../../variables.scss'; -@import '../../reset.scss'; -@import '../../markdown.scss'; - -.documentation-page { - .content-section { - max-width: $contentWidth; - margin: 0 auto; - box-sizing: border-box; - padding: 40px 40px 60px; - position: relative; - min-height: 1100px; - .doc-content { - display: inline-block; - vertical-align: top; - box-sizing: border-box; - padding: 20px 6% 0; - width: calc(100% - #{$sideMenuWidth}); - } - } - @media screen and (max-width: $mobileWidth) { - .content-section { - padding-left: 20px; - padding-right: 20px; - .doc-content { - width: 100%; - } - } - .sidemenu { - position: absolute; - z-index: 100; - left: 0; - top: 40px; - } - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/home/featureItem.jsx b/sop-website/sop-portal/src/pages/home/featureItem.jsx deleted file mode 100644 index 8164ec3c..00000000 --- a/sop-website/sop-portal/src/pages/home/featureItem.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { getLink } from '../../../utils'; - -const Item = (props) => { - const { feature } = props; - return ( -
  • - -
    -

    {feature.title}

    -

    {feature.content}

    -
    -
  • - ); -}; - -export default Item; diff --git a/sop-website/sop-portal/src/pages/home/index.jsx b/sop-website/sop-portal/src/pages/home/index.jsx deleted file mode 100644 index f3fa29f9..00000000 --- a/sop-website/sop-portal/src/pages/home/index.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { getScrollTop, getLink } from '../../../utils'; -import Header from '../../components/header'; -import Button from '../../components/button'; -import Footer from '../../components/footer'; -import Language from '../../components/language'; -import Item from './featureItem'; -import homeConfig from '../../../site_config/home'; -import './index.scss'; - -class Home extends Language { - - constructor(props) { - super(props); - this.state = { - headerType: 'primary', - }; - } - - componentDidMount() { - window.addEventListener('scroll', () => { - const scrollTop = getScrollTop(); - if (scrollTop > 66) { - this.setState({ - headerType: 'normal', - }); - } else { - this.setState({ - headerType: 'primary', - }); - } - }); - } - - render() { - const language = this.getLanguage(); - const dataSource = homeConfig[language]; - const { headerType } = this.state; - const headerLogo = headerType === 'primary' ? '/img/logo_white.png' : '/img/logo.png'; - return ( -
    -
    -
    -
    -
    -

    {dataSource.brand.brandName}

    -
    -

    {dataSource.brand.briefIntroduction}

    -
    - { - dataSource.brand.buttons.map(b => ) - } -
    -
    -
    -
    -
    -
    -
    -
    -
    - { - dataSource.introductions.map((item, i) => ( -
    -
    -

    {item.title}

    -

    {item.desc}

    -
    - -
    - ))} -
    -
    -

    {dataSource.features.title}

    -
      - { - dataSource.features.list.map((feature, i) => ( - - )) - } -
    -
    -
    -
    - ); - } -} - -document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); - -export default Home; diff --git a/sop-website/sop-portal/src/pages/home/index.scss b/sop-website/sop-portal/src/pages/home/index.scss deleted file mode 100644 index 6ecbcda6..00000000 --- a/sop-website/sop-portal/src/pages/home/index.scss +++ /dev/null @@ -1,472 +0,0 @@ -@import '../../variables.scss'; -@import '../../reset.scss'; - -$animation1Height: 500px; -$animation2Height: 375px; -$animation3Height: 248px; -$animation4Height: 570px; -$animation5Height: 460px; - -@keyframes fallingStar1 { - 0% { - transform: translate3d(0, -100%, 0); - opacity: 1; - } - - 100% { - transform: translate3d(0, $animation1Height, 0); - opacity: 0; - } -} - -@keyframes fallingStar2 { - 0% { - transform: translate3d(0, -100%, 0); - opacity: 1; - } - - 100% { - transform: translate3d(0, $animation2Height, 0); - opacity: 0; - } -} - -@keyframes fallingStar3 { - 0% { - transform: translate3d(0, -100%, 0); - opacity: 1; - } - - 100% { - transform: translate3d(0, $animation3Height, 0); - opacity: 0; - } -} - -@keyframes fallingStar4 { - 0% { - transform: translate3d(0, -100%, 0); - opacity: 1; - } - - 100% { - transform: translate3d(0, $animation4Height, 0); - opacity: 0; - } -} - -@keyframes fallingStar5 { - 0% { - transform: translate3d(0, -100%, 0); - opacity: 1; - } - - 100% { - transform: translate3d(0, $animation5Height ,0); - opacity: 0; - } -} - -.home-page { - .top-section { - position: relative; - height: 720px; - background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - .animation { - position: absolute; - top: 0; - border-right: 1px solid rgba(255, 255, 255, 0.3); - transform: scaleX(0.5); - &::before { - content: ''; - height: 16px; - opacity: 1; - border-right: 4px solid #fff; - position: absolute; - left: -2px; - transform: translate3d(0,-100%,0); - } - &1 { - left: 15%; - height: $animation1Height; - &::before { - animation: fallingStar1 1.3s infinite ease-in-out; - } - } - &2 { - left: 35%; - height: $animation2Height; - &::before { - animation: fallingStar2 1.5s infinite ease-in-out; - } - } - &3 { - left: 52%; - height: $animation3Height; - &::before { - animation: fallingStar3 2s infinite ease-in-out; - } - } - &4 { - left: 65%; - height: $animation4Height; - &::before { - animation: fallingStar4 1.5s infinite ease-in-out; - } - } - &5 { - left: 85%; - height: $animation5Height; - &::before { - animation: fallingStar5 1.3s infinite ease-in-out; - } - } - } - .vertical-middle { - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); - box-sizing: border-box; - width: 100%; - text-align: center; - padding: 0 20px; - } - .product-name { - position: relative; - display: inline-block; - h2 { - font-family: Avenir-Heavy; - font-size: 46px; - color: #FFF; - text-align: center; - word-break: break-word; - margin: 0; - } - } - .product-desc { - opacity: 0.6; - font-family: Avenir-Medium; - font-size: 24px; - color: #FFF; - text-align: center; - margin: 12px auto 0; - max-width: 730px; - } - .button-area { - text-align: center; - margin-top: 40px; - .button { - margin-right: 20px; - } - .button:last-child { - margin-right: 0; - } - } - } - .introduction-section { - background: #F9FAFA; - .introduction-body { - max-width: $contentWidth; - box-sizing: border-box; - margin: 0 auto; - min-height: 640px; - padding: 0 40px; - position: relative; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - &::before { - content: ''; - position: absolute; - top: 0; - left: 40px; - height: 165px; - opacity: 0.3; - border-right: 1px solid #666; - } - &::after { - content: ''; - position: absolute; - left: 39px; - top: 165px; - width: 3px; - height: 17px; - background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - } - .introduction { - display: inline-block; - width: calc(100% - 726px); - min-width: 300px; - max-width: 590px; - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #333; - margin-bottom: 20px; - word-break: break-word; - margin-left: 10px; - margin-top: 40px; - } - p { - opacity: 0.56; - font-family: Avenir-Medium; - font-size: 18px; - color: #999; - margin-left: 20px; - } - } - img { - //width: 562px; - //margin: 0 82px; - margin: 20px 20px; - max-width: 100%; - box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.05); - } - @media screen and (max-width: 1106px){ - .introduction { - display: inline-block; - width: 100%; - max-width: 100%; - } - img { - margin: 0; - } - } - } - } - .feature-section { - box-sizing: border-box; - max-width: $contentWidth; - margin: 0 auto; - position: relative; - padding: 80px 40px 40px; - &::before { - content: ''; - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%); - margin: 0 auto; - height: 83px; - opacity: 0.3; - border-right: 1px solid #666; - } - &::after { - content: ''; - position: absolute; - top: 66px; - left: 50%; - transform: translateX(-50%); - width: 3px; - height: 17px; - background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - } - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #333; - text-align: center; - margin: 0 0 40px; - } - ul { - list-style: none; - padding: 0; - margin: 0; - li { - vertical-align: top; - display: inline-block; - margin-bottom: 40px; - width: 50%; - img { - vertical-align: top; - width: 60px; - height: 60px; - margin-right: 20px; - } - div { - display: inline-block; - width: 80%; - h4 { - font-family: Avenir-Heavy; - font-size: 20px; - color: #333; - margin-top: 10px; - } - p { - font-family: Avenir-Medium; - font-size: 14px; - line-height: 20px; - color: #999; - } - } - } - @media screen and (max-width: 768px){ - li { - width: 100%; - } - } - } - } - .start-section { - background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - .start-body { - max-width: $contentWidth; - margin: 0 auto; - box-sizing: border-box; - height: 260px; - padding: 35px 40px; - position: relative; - &::before { - content: ''; - position: absolute; - top: 0; - left: 20px; - height: 100%; - opacity: 0.3; - border-right: 1px solid #fff; - } - &::after { - content: ''; - position: absolute; - left: 19px; - top: 48px; - width: 3px; - height: 17px; - background: #fff; - } - .left-part { - display: inline-block; - width: 50%; - vertical-align: top; - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #FFF; - margin: 0; - } - p { - opacity: 0.8; - font-family: Avenir-Medium; - font-size: 18px; - color: #FFF; - line-height: 24px; - margin: 6px 0 12px; - } - a { - font-family: Avenir-Heavy; - font-size: 14px; - color: $brandColor; - text-align: center; - display: inline-block; - width: 140px; - height: 48px; - line-height: 48px; - background: #FFF; - border-radius: 4px; - } - } - .right-part { - display: inline-block; - width: 50%; - font-size: 0; - margin-top: 15px; - img { - margin-left: 5%; - width: 500px; - } - } - @media screen and (max-width: 1050px){ - & { - height: 474px; - } - .left-part { - width: 100%; - } - .right-part { - width: 100%; - margin-top: 38px; - img { - max-width: 100%; - margin-left: 0; - } - } - } - } - } - .users-section { - box-sizing: border-box; - max-width: $contentWidth; - margin: 0 auto; - padding: 80px 40px 40px; - position: relative; - &::before { - content: ''; - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%); - margin: 0 auto; - height: 83px; - opacity: 0.3; - border-right: 1px solid #666; - } - &::after { - content: ''; - position: absolute; - top: 66px; - left: 50%; - transform: translateX(-50%); - width: 3px; - height: 17px; - background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); - } - h3 { - font-family: Avenir-Heavy; - font-size: 36px; - color: #333; - text-align: center; - margin-bottom: 40px; - } - p { - font-family: Avenir-Medium; - font-size: 14px; - color: #666; - text-align: center; - margin: 0 0 40px; - a { - color: #1e6bb8; - text-decoration: none; - } - } - .users { - // display: flex; - // flex-wrap: wrap; - // justify-content: space-between; - display: inline-block; - text-align: center; - img { - margin-right: 10px; - width: 140px; - height: 80px; - margin-bottom: 40px; - } - } - } - @media screen and (max-width: $mobileWidth) { - .introduction-section { - padding: 0 20px; - &::before { - left: 20px; - } - &::after { - left: 19px; - } - } - .feature-section, .users-section { - padding-left: 20px; - padding-right: 20px; - } - } -} diff --git a/sop-website/sop-portal/src/reset.scss b/sop-website/sop-portal/src/reset.scss deleted file mode 100644 index f5c3568b..00000000 --- a/sop-website/sop-portal/src/reset.scss +++ /dev/null @@ -1,12 +0,0 @@ -@import './markdown.scss'; - -* { - padding: 0; - margin: 0; -} -a { - text-decoration: none; -} -h1, h2, h3, h4, h5, h6 { - font-weight: 400; -} diff --git a/sop-website/sop-portal/src/variables.scss b/sop-website/sop-portal/src/variables.scss deleted file mode 100644 index 09e19d22..00000000 --- a/sop-website/sop-portal/src/variables.scss +++ /dev/null @@ -1,24 +0,0 @@ -// 品牌色 -$brandColor: #2DACEC; -// 渐变起始色 -$startColor: #03DDE4; -// 渐变中间色 -$intermediateColor: #30AFED; -// 渐变结束色 -$endColor: #8755FF; -// 侧边菜单背景色 -$sideMenuBgColor: #f8f8f8; -// 顶部悬浮菜单背景色,用于移动端 -$headerMenuBgColor: #f8f8f8; - - -// 头部高度 -$headerHeight: 66px; -// 侧边栏宽度 -$sideMenuWidth: 295px; -// bar高度 -$barHeight: 200px; -// 切换到移动端显示屏宽界限 -$mobileWidth: 640px; -// 页面主体最大宽度 -$contentWidth: 1280px; \ No newline at end of file diff --git a/sop-website/sop-portal/template.ejs b/sop-website/sop-portal/template.ejs deleted file mode 100644 index 9c26eb48..00000000 --- a/sop-website/sop-portal/template.ejs +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - <%= title %> - - - - -
    <%- __html %>
    - - - - - - diff --git a/sop-website/sop-portal/utils/index.js b/sop-website/sop-portal/utils/index.js deleted file mode 100644 index 8a964069..00000000 --- a/sop-website/sop-portal/utils/index.js +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -export const throttle = (fn, delay) => { - let timer = null; - return function(...args) { - const context = this; - clearTimeout(timer); - timer = setTimeout(() => { - fn.apply(context, args); - }, delay); - }; -}; - -export const getScrollTop = () => { - let scrollTop = 0; - if (document.documentElement && document.documentElement.scrollTop) { - scrollTop = document.documentElement.scrollTop; - } else if (document.body) { - scrollTop = document.body.scrollTop; - } - return scrollTop; -}; - -export const getLink = (link) => { - if (`${link}`.length > 1 && /^\/[^/]/.test(`${link}`)) { - return `${window.rootPath}${link}`; - } - return link; -}; - -export const parseJSONStr = (str) => { - try { - return JSON.parse(str); - } catch (err) { - return str; - } -} diff --git a/sop-website/sop-portal/webpack.config.js b/sop-website/sop-portal/webpack.config.js deleted file mode 100644 index 3ac6c989..00000000 --- a/sop-website/sop-portal/webpack.config.js +++ /dev/null @@ -1,55 +0,0 @@ -const path = require('path'); -const fs = require('fs'); -const webpack = require('webpack'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); - -const entry = {}; -const targetPath = path.join(__dirname, './src/pages'); -fs.readdirSync(targetPath).forEach(page => { - if ( - fs.statSync(path.join(targetPath, page)).isDirectory() && - fs.existsSync(path.join(targetPath, page, 'index.jsx')) - ) { - entry[page] = path.join(targetPath, page, 'index.jsx'); - } -}); -module.exports = { - entry, - output: { - path: path.join(__dirname, 'build'), - filename: '[name].js', - }, - externals: { - react: 'React', - 'react-dom': 'ReactDOM', - }, - module: { - loaders: [ - { - test: /\.js|jsx$/, - exclude: [/node_modules/, /build\/lib/, /\.min\.js$/], - use: 'babel-loader', - }, - { - test: /\.(s)?css$/, - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: ['css-loader', 'sass-loader'], - }), - }, - { - test: /\.json?$/, - exclude: /node_modules/, - use: 'json-loader', - }, - ], - }, - resolve: { - extensions: ['.js', '.jsx', '.json'], - }, - plugins: [ - new webpack.NoEmitOnErrorsPlugin(), - new webpack.optimize.OccurrenceOrderPlugin(), - new ExtractTextPlugin('[name].css'), - ], -}; diff --git a/sop-website/sop-portal/zh-cn/blog/blog1.html b/sop-website/sop-portal/zh-cn/blog/blog1.html deleted file mode 100644 index 8fb0b850..00000000 --- a/sop-website/sop-portal/zh-cn/blog/blog1.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - blog1 - - - - -

    博客1

    -

    支持元数据的解析,---(至少三个-)开头之间的数据按照key:value的形式,最终会被解析到md_json/blog.json中,其中filename__html为保留字段,请勿使用。

    -

    博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充

    -
    - - - - - - diff --git a/sop-website/sop-portal/zh-cn/blog/blog1.json b/sop-website/sop-portal/zh-cn/blog/blog1.json deleted file mode 100644 index 2f15a275..00000000 --- a/sop-website/sop-portal/zh-cn/blog/blog1.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "filename": "blog1.md", - "__html": "

    博客1

    \n

    支持元数据的解析,---(至少三个-)开头之间的数据按照key:value的形式,最终会被解析到md_json/blog.json中,其中filename__html为保留字段,请勿使用。

    \n

    博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充

    \n", - "link": "/zh-cn/blog/blog1.html", - "meta": { - "key1": "hello", - "key2": "world" - } -} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/blog/index.html b/sop-website/sop-portal/zh-cn/blog/index.html deleted file mode 100644 index 39ea8695..00000000 --- a/sop-website/sop-portal/zh-cn/blog/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - 网页标签title - - - - - - - - - - - diff --git a/sop-website/sop-portal/zh-cn/community/index.html b/sop-website/sop-portal/zh-cn/community/index.html deleted file mode 100644 index 9b4694c2..00000000 --- a/sop-website/sop-portal/zh-cn/community/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - 网页标签title - - - - -
    社区

    事件 & 新闻

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    生态系统

    生态系统1

    生态系统1

    生态系统2

    生态系统2

    联系我们

    有问题需要反馈?请通过一下方式联系我们。

    贡献指南

    一些描述

    邮件列表

    这是描述

    报告缺陷

    这是描述

    文档

    这是描述

    Pull Request

    这是描述

    - - - - - - diff --git a/sop-website/sop-portal/zh-cn/docs/demo1.html b/sop-website/sop-portal/zh-cn/docs/demo1.html deleted file mode 100644 index 16c64385..00000000 --- a/sop-website/sop-portal/zh-cn/docs/demo1.html +++ /dev/null @@ -1,426 +0,0 @@ - - - - - - - - - - demo1 - - - - -
    文档

    脚注的使用

    -

    这是一段示例文字,展示脚注的使用。 -这是脚注1 [1]。点击脚注滚动至对应脚注处。

    -

    下面是一大段文字,填充开始。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。 -这是填充文字。

    -
    -
    -
      -
    1. 这是脚注的说明 ↩︎

      -
    2. -
    -
    -
    - - - - - - diff --git a/sop-website/sop-portal/zh-cn/docs/demo1.json b/sop-website/sop-portal/zh-cn/docs/demo1.json deleted file mode 100644 index bb8f579f..00000000 --- a/sop-website/sop-portal/zh-cn/docs/demo1.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "filename": "demo1.md", - "__html": "

    脚注的使用

    \n

    这是一段示例文字,展示脚注的使用。\n这是脚注1 [1]。点击脚注滚动至对应脚注处。

    \n

    下面是一大段文字,填充开始。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。

    \n
    \n
    \n
      \n
    1. 这是脚注的说明 ↩︎

      \n
    2. \n
    \n
    \n", - "link": "/zh-cn/docs/demo1.html", - "meta": {} -} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/docs/demo2.html b/sop-website/sop-portal/zh-cn/docs/demo2.html deleted file mode 100644 index 1988be87..00000000 --- a/sop-website/sop-portal/zh-cn/docs/demo2.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - demo2 - - - - -
    文档

    文档之间跳转、图片引用的示例

    -

    文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 -点击跳转: demo1.md

    -

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 -

    -

    数学公式的使用(katex)

    -

    E=mc2E = mc^2

    -

    x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n -

    -

    X3\sqrt[3]{X} -

    -

    5x\sqrt{5 - x} -

    -
    - - - - - - diff --git a/sop-website/sop-portal/zh-cn/docs/demo2.json b/sop-website/sop-portal/zh-cn/docs/demo2.json deleted file mode 100644 index beb267d2..00000000 --- a/sop-website/sop-portal/zh-cn/docs/demo2.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "filename": "demo2.md", - "__html": "

    文档之间跳转、图片引用的示例

    \n

    文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。\n点击跳转: demo1.md

    \n

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如\n\"\"

    \n

    数学公式的使用(katex)

    \n

    E=mc2E = mc^2E=mc2

    \n

    x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n\nx=a1n+a2n+a3n

    \n

    X3\\sqrt[3]{X}\n3X

    \n

    5x\\sqrt{5 - x}\n5x

    \n", - "link": "/zh-cn/docs/demo2.html", - "meta": {} -} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/docs/dir/demo3.html b/sop-website/sop-portal/zh-cn/docs/dir/demo3.html deleted file mode 100644 index 27cc4945..00000000 --- a/sop-website/sop-portal/zh-cn/docs/dir/demo3.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - demo3 - - - - -
    文档

    跨目录文档之间跳转、图片引用的示例

    -

    这是一段文字,将要跳转到demo1。 -文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 -点击跳转: demo1.md

    -

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 -

    -
    - - - - - - diff --git a/sop-website/sop-portal/zh-cn/docs/dir/demo3.json b/sop-website/sop-portal/zh-cn/docs/dir/demo3.json deleted file mode 100644 index 748f6bd8..00000000 --- a/sop-website/sop-portal/zh-cn/docs/dir/demo3.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "filename": "demo3.md", - "__html": "

    跨目录文档之间跳转、图片引用的示例

    \n

    这是一段文字,将要跳转到demo1。\n文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。\n点击跳转: demo1.md

    \n

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如\n\"\"

    \n", - "link": "/zh-cn/docs/dir/demo3.html", - "meta": {} -} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/index.html b/sop-website/sop-portal/zh-cn/index.html deleted file mode 100644 index 83c429cf..00000000 --- a/sop-website/sop-portal/zh-cn/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - XX开放平台 - - - - -

    开放平台

    XX开放平台,一句话介绍。。。

    介绍部分1

    介绍文字。。。

    介绍部分2

    介绍文字。。。

    特性一览

    • 特性1

      描述文字。。

    • 特性2

      描述文字。。

    - - - - - - diff --git a/sop-website/sop-website-frontend/.browserslistrc b/sop-website/sop-website-frontend/.browserslistrc new file mode 100644 index 00000000..40bd99ce --- /dev/null +++ b/sop-website/sop-website-frontend/.browserslistrc @@ -0,0 +1,4 @@ +> 1% +last 2 versions +not dead +not ie 11 \ No newline at end of file diff --git a/sop-website/sop-portal-vue/.gitignore b/sop-website/sop-website-frontend/.dockerignore similarity index 51% rename from sop-website/sop-portal-vue/.gitignore rename to sop-website/sop-website-frontend/.dockerignore index ae3d36d3..0376edde 100644 --- a/sop-website/sop-portal-vue/.gitignore +++ b/sop-website/sop-website-frontend/.dockerignore @@ -1,17 +1,21 @@ -yarn.lock +node_modules .DS_Store -node_modules/ -dist/ +dist +dist-ssr +*.local +.eslintcache +report.html + +yarn.lock npm-debug.log* -yarn-debug.log* -yarn-error.log* -package-lock.json +.pnpm-error.log* +.pnpm-debug.log tests/**/coverage/ # Editor directories and files .idea -.vscode *.suo *.ntvs* *.njsproj *.sln +tsconfig.tsbuildinfo diff --git a/sop-website/sop-website-vue/.editorconfig b/sop-website/sop-website-frontend/.editorconfig similarity index 100% rename from sop-website/sop-website-vue/.editorconfig rename to sop-website/sop-website-frontend/.editorconfig diff --git a/sop-website/sop-website-frontend/.env b/sop-website/sop-website-frontend/.env new file mode 100644 index 00000000..a2345d94 --- /dev/null +++ b/sop-website/sop-website-frontend/.env @@ -0,0 +1,5 @@ +# 平台本地运行端口号 +VITE_PORT = 9124 + +# 是否隐藏首页 隐藏 true 不隐藏 false (勿删除,VITE_HIDE_HOME只需在.env文件配置) +VITE_HIDE_HOME = false diff --git a/sop-website/sop-website-frontend/.env.development b/sop-website/sop-website-frontend/.env.development new file mode 100644 index 00000000..511e186c --- /dev/null +++ b/sop-website/sop-website-frontend/.env.development @@ -0,0 +1,8 @@ +# 平台本地运行端口号 +VITE_PORT = 9124 + +# 开发环境读取配置文件路径 +VITE_PUBLIC_PATH = / + +# 开发环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +VITE_ROUTER_HISTORY = "hash" diff --git a/sop-website/sop-website-frontend/.env.production b/sop-website/sop-website-frontend/.env.production new file mode 100644 index 00000000..84e60861 --- /dev/null +++ b/sop-website/sop-website-frontend/.env.production @@ -0,0 +1,13 @@ +# 线上环境平台打包路径 +VITE_PUBLIC_PATH = / + +# 线上环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +VITE_ROUTER_HISTORY = "hash" + +# 是否在打包时使用cdn替换本地库 替换 true 不替换 false +VITE_CDN = false + +# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) +# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +VITE_COMPRESSION = "none" \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.env.staging b/sop-website/sop-website-frontend/.env.staging new file mode 100644 index 00000000..65b57e3c --- /dev/null +++ b/sop-website/sop-website-frontend/.env.staging @@ -0,0 +1,16 @@ +# 预发布也需要生产环境的行为 +# https://cn.vitejs.dev/guide/env-and-mode.html#modes +# NODE_ENV = development + +VITE_PUBLIC_PATH = / + +# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +VITE_ROUTER_HISTORY = "hash" + +# 是否在打包时使用cdn替换本地库 替换 true 不替换 false +VITE_CDN = true + +# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) +# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +VITE_COMPRESSION = "none" diff --git a/sop-website/sop-website-frontend/.gitignore b/sop-website/sop-website-frontend/.gitignore new file mode 100644 index 00000000..423ed2b8 --- /dev/null +++ b/sop-website/sop-website-frontend/.gitignore @@ -0,0 +1,22 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +.eslintcache +report.html +vite.config.*.timestamp* + +yarn.lock +npm-debug.log* +.pnpm-error.log* +.pnpm-debug.log +tests/**/coverage/ + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +tsconfig.tsbuildinfo \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.husky/commit-msg b/sop-website/sop-website-frontend/.husky/commit-msg new file mode 100755 index 00000000..5ee2d163 --- /dev/null +++ b/sop-website/sop-website-frontend/.husky/commit-msg @@ -0,0 +1,8 @@ +#!/bin/sh + +# shellcheck source=./_/husky.sh +. "$(dirname "$0")/_/husky.sh" + +PATH="/usr/local/bin:$PATH" + +npx --no-install commitlint --edit "$1" \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.husky/common.sh b/sop-website/sop-website-frontend/.husky/common.sh new file mode 100644 index 00000000..5f0540b7 --- /dev/null +++ b/sop-website/sop-website-frontend/.husky/common.sh @@ -0,0 +1,9 @@ +#!/bin/sh +command_exists () { + command -v "$1" >/dev/null 2>&1 +} + +# Workaround for Windows 10, Git Bash and Pnpm +if command_exists winpty && test -t 1; then + exec < /dev/tty +fi diff --git a/sop-website/sop-website-frontend/.husky/pre-commit b/sop-website/sop-website-frontend/.husky/pre-commit new file mode 100755 index 00000000..6e229ea3 --- /dev/null +++ b/sop-website/sop-website-frontend/.husky/pre-commit @@ -0,0 +1,10 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" +. "$(dirname "$0")/common.sh" + +[ -n "$CI" ] && exit 0 + +PATH="/usr/local/bin:$PATH" + +# Perform lint check on files in the staging area through .lintstagedrc configuration +pnpm exec lint-staged \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.lintstagedrc b/sop-website/sop-website-frontend/.lintstagedrc new file mode 100644 index 00000000..ebf359aa --- /dev/null +++ b/sop-website/sop-website-frontend/.lintstagedrc @@ -0,0 +1,20 @@ +{ + "*.{js,jsx,ts,tsx}": [ + "prettier --cache --ignore-unknown --write", + "eslint --cache --fix" + ], + "{!(package)*.json,*.code-snippets,.!({browserslist,npm,nvm})*rc}": [ + "prettier --cache --write--parser json" + ], + "package.json": ["prettier --cache --write"], + "*.vue": [ + "prettier --write", + "eslint --cache --fix", + "stylelint --fix --allow-empty-input" + ], + "*.{css,scss,html}": [ + "prettier --cache --ignore-unknown --write", + "stylelint --fix --allow-empty-input" + ], + "*.md": ["prettier --cache --ignore-unknown --write"] +} diff --git a/sop-website/sop-website-frontend/.markdownlint.json b/sop-website/sop-website-frontend/.markdownlint.json new file mode 100644 index 00000000..d628d441 --- /dev/null +++ b/sop-website/sop-website-frontend/.markdownlint.json @@ -0,0 +1,11 @@ +{ + "default": true, + "MD003": false, + "MD033": false, + "MD013": false, + "MD001": false, + "MD025": false, + "MD024": false, + "MD007": { "indent": 4 }, + "no-hard-tabs": false +} diff --git a/sop-website/sop-website-frontend/.npmrc b/sop-website/sop-website-frontend/.npmrc new file mode 100644 index 00000000..dddf8bc0 --- /dev/null +++ b/sop-website/sop-website-frontend/.npmrc @@ -0,0 +1,4 @@ +shell-emulator=true +shamefully-hoist=true +enable-pre-post-scripts=false +strict-peer-dependencies=false \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.nvmrc b/sop-website/sop-website-frontend/.nvmrc new file mode 100644 index 00000000..47465842 --- /dev/null +++ b/sop-website/sop-website-frontend/.nvmrc @@ -0,0 +1 @@ +v20.15.0 \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.prettierrc.js b/sop-website/sop-website-frontend/.prettierrc.js new file mode 100644 index 00000000..775d970a --- /dev/null +++ b/sop-website/sop-website-frontend/.prettierrc.js @@ -0,0 +1,9 @@ +// @ts-check + +/** @type {import("prettier").Config} */ +export default { + bracketSpacing: true, + singleQuote: false, + arrowParens: "avoid", + trailingComma: "none" +}; diff --git a/sop-website/sop-website-frontend/.stylelintignore b/sop-website/sop-website-frontend/.stylelintignore new file mode 100644 index 00000000..0c34e619 --- /dev/null +++ b/sop-website/sop-website-frontend/.stylelintignore @@ -0,0 +1,4 @@ +/dist/* +/public/* +public/* +src/style/reset.scss \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.vscode/extensions.json b/sop-website/sop-website-frontend/.vscode/extensions.json new file mode 100644 index 00000000..dae154d3 --- /dev/null +++ b/sop-website/sop-website-frontend/.vscode/extensions.json @@ -0,0 +1,20 @@ +{ + "recommendations": [ + "christian-kohler.path-intellisense", + "warmthsea.vscode-custom-code-color", + "vscode-icons-team.vscode-icons", + "davidanson.vscode-markdownlint", + "ms-azuretools.vscode-docker", + "stylelint.vscode-stylelint", + "bradlc.vscode-tailwindcss", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "lokalise.i18n-ally", + "redhat.vscode-yaml", + "csstools.postcss", + "mikestead.dotenv", + "eamodio.gitlens", + "antfu.iconify", + "Vue.volar" + ] +} \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.vscode/settings.json b/sop-website/sop-website-frontend/.vscode/settings.json new file mode 100644 index 00000000..8599bf84 --- /dev/null +++ b/sop-website/sop-website-frontend/.vscode/settings.json @@ -0,0 +1,56 @@ +{ + "editor.formatOnType": true, + "editor.formatOnSave": true, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "editor.tabSize": 2, + "editor.formatOnPaste": true, + "editor.guides.bracketPairs": "active", + "files.autoSave": "afterDelay", + "git.confirmSync": false, + "workbench.startupEditor": "newUntitledFile", + "editor.suggestSelection": "first", + "editor.acceptSuggestionOnCommitCharacter": false, + "css.lint.propertyIgnoredDueToDisplay": "ignore", + "editor.quickSuggestions": { + "other": true, + "comments": true, + "strings": true + }, + "files.associations": { + "editor.snippetSuggestions": "top" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "i18n-ally.localesPaths": "locales", + "i18n-ally.keystyle": "nested", + "i18n-ally.sortKeys": true, + "i18n-ally.namespace": true, + "i18n-ally.enabledParsers": [ + "yaml", + "js" + ], + "i18n-ally.sourceLanguage": "en", + "i18n-ally.displayLanguage": "zh-CN", + "i18n-ally.enabledFrameworks": [ + "vue" + ], + "iconify.excludes": [ + "el" + ], + "vscodeCustomCodeColor.highlightValue": [ + "v-loading", + "v-auth", + "v-copy", + "v-longpress", + "v-optimize", + "v-perms", + "v-ripple" + ], + "vscodeCustomCodeColor.highlightValueColor": "#b392f0", +} \ No newline at end of file diff --git a/sop-website/sop-website-frontend/.vscode/vue3.0.code-snippets b/sop-website/sop-website-frontend/.vscode/vue3.0.code-snippets new file mode 100644 index 00000000..bb43589a --- /dev/null +++ b/sop-website/sop-website-frontend/.vscode/vue3.0.code-snippets @@ -0,0 +1,22 @@ +{ + "Vue3.0快速生成模板": { + "scope": "vue", + "prefix": "Vue3.0", + "body": [ + "\n", + "\n", + "", + "$2" + ], + "description": "Vue3.0" + } +} diff --git a/sop-website/sop-website-frontend/.vscode/vue3.2.code-snippets b/sop-website/sop-website-frontend/.vscode/vue3.2.code-snippets new file mode 100644 index 00000000..2cebb463 --- /dev/null +++ b/sop-website/sop-website-frontend/.vscode/vue3.2.code-snippets @@ -0,0 +1,17 @@ +{ + "Vue3.2+快速生成模板": { + "scope": "vue", + "prefix": "Vue3.2+", + "body": [ + "\n", + "\n", + "", + "$2" + ], + "description": "Vue3.2+" + } +} diff --git a/sop-website/sop-website-frontend/.vscode/vue3.3.code-snippets b/sop-website/sop-website-frontend/.vscode/vue3.3.code-snippets new file mode 100644 index 00000000..dc7a1062 --- /dev/null +++ b/sop-website/sop-website-frontend/.vscode/vue3.3.code-snippets @@ -0,0 +1,20 @@ +{ + "Vue3.3+defineOptions快速生成模板": { + "scope": "vue", + "prefix": "Vue3.3+", + "body": [ + "\n", + "\n", + "", + "$2" + ], + "description": "Vue3.3+defineOptions快速生成模板" + } +} diff --git a/sop-website/sop-website-frontend/Dockerfile b/sop-website/sop-website-frontend/Dockerfile new file mode 100644 index 00000000..cd6d51a9 --- /dev/null +++ b/sop-website/sop-website-frontend/Dockerfile @@ -0,0 +1,20 @@ +FROM node:20-alpine as build-stage + +WORKDIR /app +RUN corepack enable +RUN corepack prepare pnpm@latest --activate + +RUN npm config set registry https://registry.npmmirror.com + +COPY .npmrc package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile + +COPY . . +RUN pnpm build + +FROM nginx:stable-alpine as production-stage + +COPY --from=build-stage /app/dist /usr/share/nginx/html +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/sop-website/sop-website-vue/LICENSE b/sop-website/sop-website-frontend/LICENSE similarity index 96% rename from sop-website/sop-website-vue/LICENSE rename to sop-website/sop-website-frontend/LICENSE index 61515750..6d4889d0 100644 --- a/sop-website/sop-website-vue/LICENSE +++ b/sop-website/sop-website-frontend/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-present PanJiaChen +Copyright (c) 2020-present, pure-admin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/sop-website/sop-website-frontend/README.md b/sop-website/sop-website-frontend/README.md new file mode 100644 index 00000000..b9c6d3d4 --- /dev/null +++ b/sop-website/sop-website-frontend/README.md @@ -0,0 +1,39 @@ +# 接口文档预览 + +> 采用 ECMAScript 模块(ESM)规范来编写和组织代码,使用了最新的 Vue3、Vite、Element-Plus、TypeScript、Pinia、Tailwindcss 等主流技术 + +## 参考文档 + +- 模板:https://pure-admin.github.io/pure-admin-doc/ +- 表单表格组件:https://plus-pro-components.com/ + +## 开发部署 + +安装nodejs + +> node版本 >= 18.18.0 + +- macOS用户在命令前面添加`sudo` + +```shell +# 安装pnpm +npm install -g pnpm + +# 安装依赖 +pnpm install + +# 启动 +pnpm dev +``` + +打包: + +`pnpm build` + +安装一个包: + +`pnpm add 包名` + +卸载一个包: + +`pnpm remove 包名` \ No newline at end of file diff --git a/sop-website/sop-website-frontend/commitlint.config.js b/sop-website/sop-website-frontend/commitlint.config.js new file mode 100644 index 00000000..eea755d0 --- /dev/null +++ b/sop-website/sop-website-frontend/commitlint.config.js @@ -0,0 +1,35 @@ +// @ts-check + +/** @type {import("@commitlint/types").UserConfig} */ +export default { + ignores: [commit => commit.includes("init")], + extends: ["@commitlint/config-conventional"], + rules: { + "body-leading-blank": [2, "always"], + "footer-leading-blank": [1, "always"], + "header-max-length": [2, "always", 108], + "subject-empty": [2, "never"], + "type-empty": [2, "never"], + "type-enum": [ + 2, + "always", + [ + "feat", + "fix", + "perf", + "style", + "docs", + "test", + "refactor", + "build", + "ci", + "chore", + "revert", + "wip", + "workflow", + "types", + "release" + ] + ] + } +}; diff --git a/sop-website/sop-website-frontend/eslint.config.js b/sop-website/sop-website-frontend/eslint.config.js new file mode 100644 index 00000000..b4d48fbc --- /dev/null +++ b/sop-website/sop-website-frontend/eslint.config.js @@ -0,0 +1,181 @@ +import js from "@eslint/js"; +import pluginVue from "eslint-plugin-vue"; +import * as parserVue from "vue-eslint-parser"; +import configPrettier from "eslint-config-prettier"; +import pluginPrettier from "eslint-plugin-prettier"; +import { defineFlatConfig } from "eslint-define-config"; +import * as parserTypeScript from "@typescript-eslint/parser"; +import pluginTypeScript from "@typescript-eslint/eslint-plugin"; + +export default defineFlatConfig([ + { + ...js.configs.recommended, + ignores: [ + "**/.*", + "dist/*", + "*.d.ts", + "public/*", + "src/assets/**", + "src/**/iconfont/**" + ], + languageOptions: { + globals: { + // index.d.ts + RefType: "readonly", + EmitType: "readonly", + TargetContext: "readonly", + ComponentRef: "readonly", + ElRef: "readonly", + ForDataType: "readonly", + AnyFunction: "readonly", + PropType: "readonly", + Writable: "readonly", + Nullable: "readonly", + NonNullable: "readonly", + Recordable: "readonly", + ReadonlyRecordable: "readonly", + Indexable: "readonly", + DeepPartial: "readonly", + Without: "readonly", + Exclusive: "readonly", + TimeoutHandle: "readonly", + IntervalHandle: "readonly", + Effect: "readonly", + ChangeEvent: "readonly", + WheelEvent: "readonly", + ImportMetaEnv: "readonly", + Fn: "readonly", + PromiseFn: "readonly", + ComponentElRef: "readonly", + parseInt: "readonly", + parseFloat: "readonly" + } + }, + plugins: { + prettier: pluginPrettier + }, + rules: { + ...configPrettier.rules, + ...pluginPrettier.configs.recommended.rules, + "no-debugger": "off", + "no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_" + } + ], + "prettier/prettier": [ + "error", + { + endOfLine: "auto" + } + ] + } + }, + { + files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"], + languageOptions: { + parser: parserTypeScript, + parserOptions: { + sourceType: "module" + } + }, + plugins: { + "@typescript-eslint": pluginTypeScript + }, + rules: { + ...pluginTypeScript.configs.strict.rules, + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-redeclare": "error", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/prefer-as-const": "warn", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-import-type-side-effects": "error", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/consistent-type-imports": [ + "error", + { disallowTypeAnnotations: false, fixStyle: "inline-type-imports" } + ], + "@typescript-eslint/prefer-literal-enum-member": [ + "error", + { allowBitwiseExpressions: true } + ], + "@typescript-eslint/no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_" + } + ] + } + }, + { + files: ["**/*.d.ts"], + rules: { + "eslint-comments/no-unlimited-disable": "off", + "import/no-duplicates": "off", + "unused-imports/no-unused-vars": "off" + } + }, + { + files: ["**/*.?([cm])js"], + rules: { + "@typescript-eslint/no-require-imports": "off", + "@typescript-eslint/no-var-requires": "off" + } + }, + { + files: ["**/*.vue"], + languageOptions: { + globals: { + $: "readonly", + $$: "readonly", + $computed: "readonly", + $customRef: "readonly", + $ref: "readonly", + $shallowRef: "readonly", + $toRef: "readonly" + }, + parser: parserVue, + parserOptions: { + ecmaFeatures: { + jsx: true + }, + extraFileExtensions: [".vue"], + parser: "@typescript-eslint/parser", + sourceType: "module" + } + }, + plugins: { + vue: pluginVue + }, + processor: pluginVue.processors[".vue"], + rules: { + ...pluginVue.configs.base.rules, + ...pluginVue.configs["vue3-essential"].rules, + ...pluginVue.configs["vue3-recommended"].rules, + "no-undef": "off", + "no-unused-vars": "off", + "vue/no-v-html": "off", + "vue/require-default-prop": "off", + "vue/require-explicit-emits": "off", + "vue/multi-word-component-names": "off", + "vue/no-setup-props-reactivity-loss": "off", + "vue/html-self-closing": [ + "error", + { + html: { + void: "always", + normal: "always", + component: "always" + }, + svg: "always", + math: "always" + } + ] + } + } +]); diff --git a/sop-website/sop-website-frontend/index.html b/sop-website/sop-website-frontend/index.html new file mode 100644 index 00000000..f7f61354 --- /dev/null +++ b/sop-website/sop-website-frontend/index.html @@ -0,0 +1,87 @@ + + + + + + + + pure-admin-thin + + + + + +
    + +
    +
    + + + diff --git a/sop-website/sop-website-frontend/locales/en.yaml b/sop-website/sop-website-frontend/locales/en.yaml new file mode 100644 index 00000000..9d23b5fc --- /dev/null +++ b/sop-website/sop-website-frontend/locales/en.yaml @@ -0,0 +1,91 @@ +buttons: + pureLoginOut: LoginOut + pureLogin: Login + pureOpenSystemSet: Open System Configs + pureReload: Reload + pureCloseCurrentTab: Close CurrentTab + pureCloseLeftTabs: Close LeftTabs + pureCloseRightTabs: Close RightTabs + pureCloseOtherTabs: Close OtherTabs + pureCloseAllTabs: Close AllTabs + pureContentFullScreen: Content FullScreen + pureContentExitFullScreen: Content ExitFullScreen + pureClickCollapse: Collapse + pureClickExpand: Expand + pureConfirm: Confirm + pureSwitch: Switch + pureClose: Close + pureBackTop: BackTop + pureOpenText: Open + pureCloseText: Close +search: + pureTotal: Total + pureHistory: History + pureCollect: Collect + pureDragSort: (Drag Sort) + pureEmpty: Empty + purePlaceholder: Search Menu +panel: + pureSystemSet: System Configs + pureCloseSystemSet: Close System Configs + pureClearCacheAndToLogin: Clear cache and return to login page + pureClearCache: Clear Cache + pureOverallStyle: Overall Style + pureOverallStyleLight: Light + pureOverallStyleLightTip: Set sail freshly and light up the comfortable work interface + pureOverallStyleDark: Dark + pureOverallStyleDarkTip: Moonlight Overture, indulge in the tranquility and elegance of the night + pureOverallStyleSystem: Auto + pureOverallStyleSystemTip: Synchronize time, the interface naturally responds to morning and dusk + pureThemeColor: Theme Color + pureLayoutModel: Layout Model + pureVerticalTip: The menu on the left is familiar and friendly + pureHorizontalTip: Top menu, concise overview + pureMixTip: Mixed menu, flexible + pureStretch: Stretch Page + pureStretchFixed: Fixed + pureStretchFixedTip: Compact pages make it easy to find the information you need + pureStretchCustom: Custom + pureStretchCustomTip: Minimum 1280, maximum 1600 + pureTagsStyle: Tags Style + pureTagsStyleSmart: Smart + pureTagsStyleSmartTip: Smart tags add fun and brilliance + pureTagsStyleCard: Card + pureTagsStyleCardTip: Card tags for efficient browsing + pureTagsStyleChrome: Chrome + pureTagsStyleChromeTip: Chrome style is classic and elegant + pureInterfaceDisplay: Interface Display + pureGreyModel: Grey Model + pureWeakModel: Weak Model + pureHiddenTags: Hidden Tags + pureHiddenFooter: Hidden Footer + pureMultiTagsCache: MultiTags Cache +menus: + pureHome: Home + pureLogin: Login + pureAbnormal: Abnormal Page + pureFourZeroFour: "404" + pureFourZeroOne: "403" + pureFive: "500" + purePermission: Permission Manage + purePermissionPage: Page Permission + purePermissionButton: Button Permission + purePermissionButtonRouter: Route return button permission + purePermissionButtonLogin: Login interface return button permission +status: + pureLoad: Loading... + pureMessage: Message + pureNotify: Notify + pureTodo: Todo + pureNoMessage: No Message + pureNoNotify: No Notify + pureNoTodo: No Todo +login: + pureUsername: Username + purePassword: Password + pureLogin: Login + pureLoginSuccess: Login Success + pureLoginFail: Login Fail + pureUsernameReg: Please enter username + purePassWordReg: Please enter password + purePassWordRuleReg: The password format should be any combination of 8-18 digits \ No newline at end of file diff --git a/sop-website/sop-website-frontend/locales/zh-CN.yaml b/sop-website/sop-website-frontend/locales/zh-CN.yaml new file mode 100644 index 00000000..16d9ec6f --- /dev/null +++ b/sop-website/sop-website-frontend/locales/zh-CN.yaml @@ -0,0 +1,91 @@ +buttons: + pureLoginOut: 退出系统 + pureLogin: 登录 + pureOpenSystemSet: 打开系统配置 + pureReload: 重新加载 + pureCloseCurrentTab: 关闭当前标签页 + pureCloseLeftTabs: 关闭左侧标签页 + pureCloseRightTabs: 关闭右侧标签页 + pureCloseOtherTabs: 关闭其他标签页 + pureCloseAllTabs: 关闭全部标签页 + pureContentFullScreen: 内容区全屏 + pureContentExitFullScreen: 内容区退出全屏 + pureClickCollapse: 点击折叠 + pureClickExpand: 点击展开 + pureConfirm: 确认 + pureSwitch: 切换 + pureClose: 关闭 + pureBackTop: 回到顶部 + pureOpenText: 开 + pureCloseText: 关 +search: + pureTotal: 共 + pureHistory: 搜索历史 + pureCollect: 收藏 + pureDragSort: (可拖拽排序) + pureEmpty: 暂无搜索结果 + purePlaceholder: 搜索菜单(支持拼音搜索) +panel: + pureSystemSet: 系统配置 + pureCloseSystemSet: 关闭配置 + pureClearCacheAndToLogin: 清空缓存并返回登录页 + pureClearCache: 清空缓存 + pureOverallStyle: 整体风格 + pureOverallStyleLight: 浅色 + pureOverallStyleLightTip: 清新启航,点亮舒适的工作界面 + pureOverallStyleDark: 深色 + pureOverallStyleDarkTip: 月光序曲,沉醉于夜的静谧雅致 + pureOverallStyleSystem: 自动 + pureOverallStyleSystemTip: 同步时光,界面随晨昏自然呼应 + pureThemeColor: 主题色 + pureLayoutModel: 导航模式 + pureVerticalTip: 左侧菜单,亲切熟悉 + pureHorizontalTip: 顶部菜单,简洁概览 + pureMixTip: 混合菜单,灵活多变 + pureStretch: 页宽 + pureStretchFixed: 固定 + pureStretchFixedTip: 紧凑页面,轻松找到所需信息 + pureStretchCustom: 自定义 + pureStretchCustomTip: 最小1280、最大1600 + pureTagsStyle: 页签风格 + pureTagsStyleSmart: 灵动 + pureTagsStyleSmartTip: 灵动标签,添趣生辉 + pureTagsStyleCard: 卡片 + pureTagsStyleCardTip: 卡片标签,高效浏览 + pureTagsStyleChrome: 谷歌 + pureTagsStyleChromeTip: 谷歌风格,经典美观 + pureInterfaceDisplay: 界面显示 + pureGreyModel: 灰色模式 + pureWeakModel: 色弱模式 + pureHiddenTags: 隐藏标签页 + pureHiddenFooter: 隐藏页脚 + pureMultiTagsCache: 页签持久化 +menus: + pureHome: 首页 + pureLogin: 登录 + pureAbnormal: 异常页面 + pureFourZeroFour: "404" + pureFourZeroOne: "403" + pureFive: "500" + purePermission: 权限管理 + purePermissionPage: 页面权限 + purePermissionButton: 按钮权限 + purePermissionButtonRouter: 路由返回按钮权限 + purePermissionButtonLogin: 登录接口返回按钮权限 +status: + pureLoad: 加载中... + pureMessage: 消息 + pureNotify: 通知 + pureTodo: 待办 + pureNoMessage: 暂无消息 + pureNoNotify: 暂无通知 + pureNoTodo: 暂无待办 +login: + pureUsername: 账号 + purePassword: 密码 + pureLogin: 登录 + pureLoginSuccess: 登录成功 + pureLoginFail: 登录失败 + pureUsernameReg: 请输入账号 + purePassWordReg: 请输入密码 + purePassWordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合 \ No newline at end of file diff --git a/sop-website/sop-website-frontend/mock/asyncRoutes.ts b/sop-website/sop-website-frontend/mock/asyncRoutes.ts new file mode 100644 index 00000000..dba02f72 --- /dev/null +++ b/sop-website/sop-website-frontend/mock/asyncRoutes.ts @@ -0,0 +1,38 @@ +// 模拟后端动态生成路由 +import { defineFakeRoute } from "vite-plugin-fake-server/client"; + +/** + * roles:页面级别权限,这里模拟二种 "admin"、"common" + * admin:管理员角色 + * common:普通角色 + */ +const permissionRouter = { + path: "/doc", + meta: { + title: "API文档", + icon: "ep:document", + rank: 10 + }, + children: [ + { + path: "/doc/api", + name: "DocApi", + meta: { + title: "API文档" + } + } + ] +}; + +export default defineFakeRoute([ + { + url: "/get-async-routes", + method: "get", + response: () => { + return { + success: true, + data: [permissionRouter] + }; + } + } +]); diff --git a/sop-website/sop-website-frontend/mock/login.ts b/sop-website/sop-website-frontend/mock/login.ts new file mode 100644 index 00000000..55897d8f --- /dev/null +++ b/sop-website/sop-website-frontend/mock/login.ts @@ -0,0 +1,42 @@ +// 根据角色动态生成路由 +import { defineFakeRoute } from "vite-plugin-fake-server/client"; + +export default defineFakeRoute([ + { + url: "/login", + method: "post", + response: ({ body }) => { + if (body.username === "admin") { + return { + success: true, + data: { + avatar: "https://avatars.githubusercontent.com/u/44761321", + username: "admin", + nickname: "小铭", + // 一个用户可能有多个角色 + roles: ["admin"], + // 按钮级别权限 + permissions: ["*:*:*"], + accessToken: "eyJhbGciOiJIUzUxMiJ9.admin", + refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh", + expires: "2030/10/30 00:00:00" + } + }; + } else { + return { + success: true, + data: { + avatar: "https://avatars.githubusercontent.com/u/52823142", + username: "common", + nickname: "小林", + roles: ["common"], + permissions: ["permission:btn:add", "permission:btn:edit"], + accessToken: "eyJhbGciOiJIUzUxMiJ9.common", + refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh", + expires: "2030/10/30 00:00:00" + } + }; + } + } + } +]); diff --git a/sop-website/sop-website-frontend/mock/refreshToken.ts b/sop-website/sop-website-frontend/mock/refreshToken.ts new file mode 100644 index 00000000..34d0e876 --- /dev/null +++ b/sop-website/sop-website-frontend/mock/refreshToken.ts @@ -0,0 +1,27 @@ +import { defineFakeRoute } from "vite-plugin-fake-server/client"; + +// 模拟刷新token接口 +export default defineFakeRoute([ + { + url: "/refresh-token", + method: "post", + response: ({ body }) => { + if (body.refreshToken) { + return { + success: true, + data: { + accessToken: "eyJhbGciOiJIUzUxMiJ9.newAdmin", + refreshToken: "eyJhbGciOiJIUzUxMiJ9.newAdminRefresh", + // `expires`选择这种日期格式是为了方便调试,后端直接设置时间戳或许更方便(每次都应该递增)。如果后端返回的是时间戳格式,前端开发请来到这个目录`src/utils/auth.ts`,把第`38`行的代码换成expires = data.expires即可。 + expires: "2030/10/30 23:59:59" + } + }; + } else { + return { + success: false, + data: {} + }; + } + } + } +]); diff --git a/sop-website/sop-website-frontend/package.json b/sop-website/sop-website-frontend/package.json new file mode 100644 index 00000000..462f1755 --- /dev/null +++ b/sop-website/sop-website-frontend/package.json @@ -0,0 +1,162 @@ +{ + "name": "pure-admin-thin", + "version": "5.8.0", + "private": true, + "type": "module", + "scripts": { + "dev": "NODE_OPTIONS=--max-old-space-size=4096 vite", + "serve": "pnpm dev", + "build": "rimraf dist && NODE_OPTIONS=--max-old-space-size=8192 vite build", + "build:staging": "rimraf dist && vite build --mode staging", + "report": "rimraf dist && vite build", + "preview": "vite preview", + "preview:build": "pnpm build && vite preview", + "typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck", + "svgo": "svgo -f . -r", + "clean:cache": "rimraf .eslintcache && rimraf pnpm-lock.yaml && rimraf node_modules && pnpm store prune && pnpm install", + "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix", + "lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"", + "lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/", + "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint", + "prepare": "husky", + "preinstall": "npx only-allow pnpm" + }, + "keywords": [ + "pure-admin-thin", + "vue-pure-admin", + "element-plus", + "tailwindcss", + "pure-admin", + "typescript", + "pinia", + "vue3", + "vite", + "esm" + ], + "homepage": "https://github.com/pure-admin/pure-admin-thin/tree/i18n", + "repository": { + "type": "git", + "url": "git+https://github.com/pure-admin/pure-admin-thin.git" + }, + "bugs": { + "url": "https://github.com/pure-admin/vue-pure-admin/issues" + }, + "license": "MIT", + "author": { + "name": "xiaoxian521", + "email": "pureadmin@163.com", + "url": "https://github.com/xiaoxian521" + }, + "dependencies": { + "@pureadmin/descriptions": "^1.2.1", + "@pureadmin/table": "^3.2.0", + "@pureadmin/utils": "^2.4.8", + "@vueuse/core": "^10.11.1", + "@vueuse/motion": "^2.2.3", + "animate.css": "^4.1.1", + "axios": "^1.7.4", + "dayjs": "^1.11.12", + "echarts": "^5.5.1", + "element-plus": "^2.8.0", + "js-cookie": "^3.0.5", + "localforage": "^1.10.0", + "mavon-editor": "2.7.7", + "mitt": "^3.0.1", + "nprogress": "^0.2.0", + "path": "^0.12.7", + "pinia": "^2.2.2", + "pinyin-pro": "^3.24.2", + "qs": "^6.13.0", + "responsive-storage": "^2.2.0", + "sortablejs": "^1.15.2", + "vue": "^3.4.38", + "vue-i18n": "^9.14.0", + "vue-router": "^4.4.3", + "vue-tippy": "^6.4.4", + "vue-types": "^5.1.3" + }, + "devDependencies": { + "@commitlint/cli": "^19.4.0", + "@commitlint/config-conventional": "^19.2.2", + "@commitlint/types": "^19.0.3", + "@eslint/js": "^9.9.0", + "@faker-js/faker": "^8.4.1", + "@iconify-icons/ep": "^1.2.12", + "@iconify-icons/ri": "^1.2.10", + "@iconify/vue": "^4.1.2", + "@intlify/unplugin-vue-i18n": "^4.0.0", + "@pureadmin/theme": "^3.2.0", + "@types/gradient-string": "^1.1.6", + "@types/js-cookie": "^3.0.6", + "@types/node": "^20.16.1", + "@types/nprogress": "^0.2.3", + "@types/qs": "^6.9.15", + "@types/sortablejs": "^1.15.8", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "@vitejs/plugin-vue": "^5.1.2", + "@vitejs/plugin-vue-jsx": "^4.0.1", + "autoprefixer": "^10.4.20", + "boxen": "^7.1.1", + "cssnano": "^7.0.5", + "eslint": "^9.9.0", + "eslint-config-prettier": "^9.1.0", + "eslint-define-config": "^2.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-vue": "^9.27.0", + "gradient-string": "^2.0.2", + "husky": "^9.1.4", + "lint-staged": "^15.2.9", + "postcss": "^8.4.41", + "postcss-html": "^1.7.0", + "postcss-import": "^16.1.0", + "postcss-scss": "^4.0.9", + "prettier": "^3.3.3", + "rimraf": "^5.0.10", + "rollup-plugin-visualizer": "^5.12.0", + "sass": "^1.77.8", + "stylelint": "^16.8.2", + "stylelint-config-recess-order": "^5.0.1", + "stylelint-config-recommended-vue": "^1.5.0", + "stylelint-config-standard-scss": "^13.1.0", + "stylelint-prettier": "^5.0.2", + "svgo": "^3.3.2", + "tailwindcss": "^3.4.10", + "typescript": "^5.5.4", + "vite": "^5.4.1", + "vite-plugin-cdn-import": "^1.0.1", + "vite-plugin-checker": "^0.7.2", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-fake-server": "^2.1.1", + "vite-plugin-remove-console": "^2.2.0", + "vite-plugin-router-warn": "^1.0.0", + "vite-plugin-vue-inspector": "^5.1.3", + "vite-svg-loader": "^5.1.0", + "vue-eslint-parser": "^9.4.3", + "vue-tsc": "^2.0.29" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0", + "pnpm": ">=9" + }, + "pnpm": { + "allowedDeprecatedVersions": { + "are-we-there-yet": "*", + "sourcemap-codec": "*", + "domexception": "*", + "w3c-hr-time": "*", + "inflight": "*", + "npmlog": "*", + "rimraf": "*", + "stable": "*", + "gauge": "*", + "abab": "*", + "glob": "*" + }, + "peerDependencyRules": { + "allowedVersions": { + "eslint": "9" + } + } + } +} diff --git a/sop-website/sop-website-frontend/pnpm-lock.yaml b/sop-website/sop-website-frontend/pnpm-lock.yaml new file mode 100644 index 00000000..45dfc7db --- /dev/null +++ b/sop-website/sop-website-frontend/pnpm-lock.yaml @@ -0,0 +1,7852 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@pureadmin/descriptions': + specifier: ^1.2.1 + version: 1.2.1(echarts@5.5.1)(element-plus@2.8.0(vue@3.4.38(typescript@5.5.4)))(typescript@5.5.4) + '@pureadmin/table': + specifier: ^3.2.0 + version: 3.2.0(element-plus@2.8.0(vue@3.4.38(typescript@5.5.4)))(typescript@5.5.4) + '@pureadmin/utils': + specifier: ^2.4.8 + version: 2.4.8(echarts@5.5.1)(vue@3.4.38(typescript@5.5.4)) + '@vueuse/core': + specifier: ^10.11.1 + version: 10.11.1(vue@3.4.38(typescript@5.5.4)) + '@vueuse/motion': + specifier: ^2.2.3 + version: 2.2.3(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4)) + animate.css: + specifier: ^4.1.1 + version: 4.1.1 + axios: + specifier: ^1.7.4 + version: 1.7.4 + dayjs: + specifier: ^1.11.12 + version: 1.11.12 + echarts: + specifier: ^5.5.1 + version: 5.5.1 + element-plus: + specifier: ^2.8.0 + version: 2.8.0(vue@3.4.38(typescript@5.5.4)) + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 + localforage: + specifier: ^1.10.0 + version: 1.10.0 + mavon-editor: + specifier: 2.7.7 + version: 2.7.7 + mitt: + specifier: ^3.0.1 + version: 3.0.1 + nprogress: + specifier: ^0.2.0 + version: 0.2.0 + path: + specifier: ^0.12.7 + version: 0.12.7 + pinia: + specifier: ^2.2.2 + version: 2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + pinyin-pro: + specifier: ^3.24.2 + version: 3.24.2 + qs: + specifier: ^6.13.0 + version: 6.13.0 + responsive-storage: + specifier: ^2.2.0 + version: 2.2.0 + sortablejs: + specifier: ^1.15.2 + version: 1.15.2 + vue: + specifier: ^3.4.38 + version: 3.4.38(typescript@5.5.4) + vue-i18n: + specifier: ^9.14.0 + version: 9.14.0(vue@3.4.38(typescript@5.5.4)) + vue-router: + specifier: ^4.4.3 + version: 4.4.3(vue@3.4.38(typescript@5.5.4)) + vue-tippy: + specifier: ^6.4.4 + version: 6.4.4(vue@3.4.38(typescript@5.5.4)) + vue-types: + specifier: ^5.1.3 + version: 5.1.3(vue@3.4.38(typescript@5.5.4)) + devDependencies: + '@commitlint/cli': + specifier: ^19.4.0 + version: 19.4.0(@types/node@20.16.1)(typescript@5.5.4) + '@commitlint/config-conventional': + specifier: ^19.2.2 + version: 19.2.2 + '@commitlint/types': + specifier: ^19.0.3 + version: 19.0.3 + '@eslint/js': + specifier: ^9.9.0 + version: 9.9.0 + '@faker-js/faker': + specifier: ^8.4.1 + version: 8.4.1 + '@iconify-icons/ep': + specifier: ^1.2.12 + version: 1.2.12 + '@iconify-icons/ri': + specifier: ^1.2.10 + version: 1.2.10 + '@iconify/vue': + specifier: ^4.1.2 + version: 4.1.2(vue@3.4.38(typescript@5.5.4)) + '@intlify/unplugin-vue-i18n': + specifier: ^4.0.0 + version: 4.0.0(rollup@4.21.0)(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4))) + '@pureadmin/theme': + specifier: ^3.2.0 + version: 3.2.0 + '@types/gradient-string': + specifier: ^1.1.6 + version: 1.1.6 + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 + '@types/node': + specifier: ^20.16.1 + version: 20.16.1 + '@types/nprogress': + specifier: ^0.2.3 + version: 0.2.3 + '@types/qs': + specifier: ^6.9.15 + version: 6.9.15 + '@types/sortablejs': + specifier: ^1.15.8 + version: 1.15.8 + '@typescript-eslint/eslint-plugin': + specifier: ^7.18.0 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/parser': + specifier: ^7.18.0 + version: 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4) + '@vitejs/plugin-vue': + specifier: ^5.1.2 + version: 5.1.2(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8))(vue@3.4.38(typescript@5.5.4)) + '@vitejs/plugin-vue-jsx': + specifier: ^4.0.1 + version: 4.0.1(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8))(vue@3.4.38(typescript@5.5.4)) + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.41) + boxen: + specifier: ^7.1.1 + version: 7.1.1 + cssnano: + specifier: ^7.0.5 + version: 7.0.5(postcss@8.4.41) + eslint: + specifier: ^9.9.0 + version: 9.9.0(jiti@1.21.6) + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@9.9.0(jiti@1.21.6)) + eslint-define-config: + specifier: ^2.1.0 + version: 2.1.0 + eslint-plugin-prettier: + specifier: ^5.2.1 + version: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.9.0(jiti@1.21.6)))(eslint@9.9.0(jiti@1.21.6))(prettier@3.3.3) + eslint-plugin-vue: + specifier: ^9.27.0 + version: 9.27.0(eslint@9.9.0(jiti@1.21.6)) + gradient-string: + specifier: ^2.0.2 + version: 2.0.2 + husky: + specifier: ^9.1.4 + version: 9.1.4 + lint-staged: + specifier: ^15.2.9 + version: 15.2.9 + postcss: + specifier: ^8.4.41 + version: 8.4.41 + postcss-html: + specifier: ^1.7.0 + version: 1.7.0 + postcss-import: + specifier: ^16.1.0 + version: 16.1.0(postcss@8.4.41) + postcss-scss: + specifier: ^4.0.9 + version: 4.0.9(postcss@8.4.41) + prettier: + specifier: ^3.3.3 + version: 3.3.3 + rimraf: + specifier: ^5.0.10 + version: 5.0.10 + rollup-plugin-visualizer: + specifier: ^5.12.0 + version: 5.12.0(rollup@4.21.0) + sass: + specifier: ^1.77.8 + version: 1.77.8 + stylelint: + specifier: ^16.8.2 + version: 16.8.2(typescript@5.5.4) + stylelint-config-recess-order: + specifier: ^5.0.1 + version: 5.0.1(stylelint@16.8.2(typescript@5.5.4)) + stylelint-config-recommended-vue: + specifier: ^1.5.0 + version: 1.5.0(postcss-html@1.7.0)(stylelint@16.8.2(typescript@5.5.4)) + stylelint-config-standard-scss: + specifier: ^13.1.0 + version: 13.1.0(postcss@8.4.41)(stylelint@16.8.2(typescript@5.5.4)) + stylelint-prettier: + specifier: ^5.0.2 + version: 5.0.2(prettier@3.3.3)(stylelint@16.8.2(typescript@5.5.4)) + svgo: + specifier: ^3.3.2 + version: 3.3.2 + tailwindcss: + specifier: ^3.4.10 + version: 3.4.10 + typescript: + specifier: ^5.5.4 + version: 5.5.4 + vite: + specifier: ^5.4.1 + version: 5.4.1(@types/node@20.16.1)(sass@1.77.8) + vite-plugin-cdn-import: + specifier: ^1.0.1 + version: 1.0.1(rollup@4.21.0)(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)) + vite-plugin-checker: + specifier: ^0.7.2 + version: 0.7.2(eslint@9.9.0(jiti@1.21.6))(optionator@0.9.4)(stylelint@16.8.2(typescript@5.5.4))(typescript@5.5.4)(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8))(vue-tsc@2.0.29(typescript@5.5.4)) + vite-plugin-compression: + specifier: ^0.5.1 + version: 0.5.1(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)) + vite-plugin-fake-server: + specifier: ^2.1.1 + version: 2.1.1 + vite-plugin-remove-console: + specifier: ^2.2.0 + version: 2.2.0 + vite-plugin-router-warn: + specifier: ^1.0.0 + version: 1.0.0 + vite-plugin-vue-inspector: + specifier: ^5.1.3 + version: 5.1.3(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)) + vite-svg-loader: + specifier: ^5.1.0 + version: 5.1.0(vue@3.4.38(typescript@5.5.4)) + vue-eslint-parser: + specifier: ^9.4.3 + version: 9.4.3(eslint@9.9.0(jiti@1.21.6)) + vue-tsc: + specifier: ^2.0.29 + version: 2.0.29(typescript@5.5.4) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.25.2': + resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.25.2': + resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.25.0': + resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.24.7': + resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.2': + resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.25.0': + resolution: {integrity: sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-member-expression-to-functions@7.24.8': + resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.22.15': + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.25.2': + resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.24.7': + resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.24.8': + resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.25.0': + resolution: {integrity: sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.25.0': + resolution: {integrity: sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.25.3': + resolution: {integrity: sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-decorators@7.24.7': + resolution: {integrity: sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.24.7': + resolution: {integrity: sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.24.7': + resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.24.7': + resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.24.7': + resolution: {integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.25.2': + resolution: {integrity: sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/standalone@7.25.3': + resolution: {integrity: sha512-uR+EoBqIIIvKGCG7fOj7HKupu3zVObiMfdEwoPZfVCPpcWJaZ1PkshaP5/6cl6BKAm1Zcv25O1rf+uoQ7V8nqA==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.0': + resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.3': + resolution: {integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.25.2': + resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} + engines: {node: '>=6.9.0'} + + '@commitlint/cli@19.4.0': + resolution: {integrity: sha512-sJX4J9UioVwZHq7JWM9tjT5bgWYaIN3rC4FP7YwfEwBYiIO+wMyRttRvQLNkow0vCdM0D67r9NEWU0Ui03I4Eg==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@19.2.2': + resolution: {integrity: sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@19.0.3': + resolution: {integrity: sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==} + engines: {node: '>=v18'} + + '@commitlint/ensure@19.0.3': + resolution: {integrity: sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@19.0.0': + resolution: {integrity: sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==} + engines: {node: '>=v18'} + + '@commitlint/format@19.3.0': + resolution: {integrity: sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@19.2.2': + resolution: {integrity: sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==} + engines: {node: '>=v18'} + + '@commitlint/lint@19.2.2': + resolution: {integrity: sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==} + engines: {node: '>=v18'} + + '@commitlint/load@19.4.0': + resolution: {integrity: sha512-I4lCWaEZYQJ1y+Y+gdvbGAx9pYPavqZAZ3/7/8BpWh+QjscAn8AjsUpLV2PycBsEx7gupq5gM4BViV9xwTIJuw==} + engines: {node: '>=v18'} + + '@commitlint/message@19.0.0': + resolution: {integrity: sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==} + engines: {node: '>=v18'} + + '@commitlint/parse@19.0.3': + resolution: {integrity: sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==} + engines: {node: '>=v18'} + + '@commitlint/read@19.4.0': + resolution: {integrity: sha512-r95jLOEZzKDakXtnQub+zR3xjdnrl2XzerPwm7ch1/cc5JGq04tyaNpa6ty0CRCWdVrk4CZHhqHozb8yZwy2+g==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@19.1.0': + resolution: {integrity: sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==} + engines: {node: '>=v18'} + + '@commitlint/rules@19.0.3': + resolution: {integrity: sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@19.0.0': + resolution: {integrity: sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==} + engines: {node: '>=v18'} + + '@commitlint/top-level@19.0.0': + resolution: {integrity: sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==} + engines: {node: '>=v18'} + + '@commitlint/types@19.0.3': + resolution: {integrity: sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==} + engines: {node: '>=v18'} + + '@csstools/css-parser-algorithms@3.0.1': + resolution: {integrity: sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.1 + + '@csstools/css-tokenizer@3.0.1': + resolution: {integrity: sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@3.0.1': + resolution: {integrity: sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.1 + '@csstools/css-tokenizer': ^3.0.1 + + '@csstools/selector-specificity@4.0.0': + resolution: {integrity: sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^6.1.0 + + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + + '@dual-bundle/import-meta-resolve@4.1.0': + resolution: {integrity: sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==} + + '@element-plus/icons-vue@2.3.1': + resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==} + peerDependencies: + vue: ^3.2.0 + + '@esbuild/aix-ppc64@0.19.12': + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.19.12': + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.19.12': + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.19.12': + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.19.12': + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.19.12': + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.19.12': + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.19.12': + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.19.12': + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.19.12': + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.19.12': + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.19.12': + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.19.12': + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.19.12': + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.19.12': + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.19.12': + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.19.12': + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.19.12': + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.19.12': + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.19.12': + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.19.12': + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.19.12': + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.19.12': + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.17.1': + resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.9.0': + resolution: {integrity: sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.4': + resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@faker-js/faker@8.4.1': + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + + '@floating-ui/core@1.6.7': + resolution: {integrity: sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==} + + '@floating-ui/dom@1.6.10': + resolution: {integrity: sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==} + + '@floating-ui/utils@0.2.7': + resolution: {integrity: sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} + + '@iconify-icons/ep@1.2.12': + resolution: {integrity: sha512-8EJULn048sQq3fvytpQ5j40omnVOdBKpo+sXdYM35NRrqCe1BihxBesMcCOLWaocqkWia6uTQ3cnRHff4ZA11w==} + + '@iconify-icons/ri@1.2.10': + resolution: {integrity: sha512-wNaXsQYK55WDUWCbcjvnwnODV4Jtsp+VC0duPanibEVu876TUYf6kdgTGtH7/GErBCNdJuJJbncG7vbOaeQi7w==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/vue@4.1.2': + resolution: {integrity: sha512-CQnYqLiQD5LOAaXhBrmj1mdL2/NCJvwcC4jtW2Z8ukhThiFkLDkutarTOV2trfc9EXqUqRs0KqXOL9pZ/IyysA==} + peerDependencies: + vue: '>=3' + + '@intlify/bundle-utils@8.0.0': + resolution: {integrity: sha512-1B++zykRnMwQ+20SpsZI1JCnV/YJt9Oq7AGlEurzkWJOFtFAVqaGc/oV36PBRYeiKnTbY9VYfjBimr2Vt42wLQ==} + engines: {node: '>= 14.16'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/core-base@9.14.0': + resolution: {integrity: sha512-zJn0imh9HIsZZUtt9v8T16PeVstPv6bP2YzlrYJwoF8F30gs4brZBwW2KK6EI5WYKFi3NeqX6+UU4gniz5TkGg==} + engines: {node: '>= 16'} + + '@intlify/message-compiler@9.14.0': + resolution: {integrity: sha512-sXNsoMI0YsipSXW8SR75drmVK56tnJHoYbPXUv2Cf9lz6FzvwsosFm6JtC1oQZI/kU+n7qx0qRrEWkeYFTgETA==} + engines: {node: '>= 16'} + + '@intlify/shared@9.14.0': + resolution: {integrity: sha512-r+N8KRQL7LgN1TMTs1A2svfuAU0J94Wu9wWdJVJqYsoMMLIeJxrPjazihfHpmJqfgZq0ah3Y9Q4pgWV2O90Fyg==} + engines: {node: '>= 16'} + + '@intlify/unplugin-vue-i18n@4.0.0': + resolution: {integrity: sha512-q2Mhqa/mLi0tulfLFO4fMXXvEbkSZpI5yGhNNsLTNJJ41icEGUuyDe+j5zRZIKSkOJRgX6YbCyibTDJdRsukmw==} + engines: {node: '>= 14.16'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + vue-i18n-bridge: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + vue-i18n-bridge: + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nuxt/kit@3.12.4': + resolution: {integrity: sha512-aNRD1ylzijY0oYolldNcZJXVyxdGzNTl+Xd0UYyFQCu9f4wqUZqQ9l+b7arCEzchr96pMK0xdpvLcS3xo1wDcw==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/schema@3.12.4': + resolution: {integrity: sha512-H7FwBV4ChssMaeiLyPdVLOLUa0326ebp3pNbJfGgFt7rSoKh1MmgjorecA8JMxOQZziy3w6EELf4+5cgLh/F1w==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.1.1': + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@pureadmin/descriptions@1.2.1': + resolution: {integrity: sha512-7jDJuqz8xnhcmwXdWQnBzOYeX2WK27TRFaVgs9AdiRr+DnKb9W+krHByJwQtxo5lg4qyRh4/IWQGEMfhC2ljeQ==} + peerDependencies: + element-plus: ^2.0.0 + + '@pureadmin/table@3.2.0': + resolution: {integrity: sha512-5H09KrQDeGpxVxOKCISi65WcT7mSuyhGTHiDcHUcaZFXMkB874ZikDBuNFMUp4ccqyztoD6AAo9Tk/cqCMhbuQ==} + peerDependencies: + element-plus: ^2.0.0 + + '@pureadmin/theme@3.2.0': + resolution: {integrity: sha512-SBlTvEl0rmfqTW/mOJUPftvZe4yF+38CJdlBOvVITpehzCytqlG5i8XKpcs8aAR9SVfhcrLVS5Q6xh7xDVQcJQ==} + + '@pureadmin/utils@2.4.8': + resolution: {integrity: sha512-7baOiunmno3mlvk7oKIrEvIEVAbJgIrvvMw9EJMOvTmebfn31F5ps9JyIeDzsnVRABZLSEg0iIVVfYe+DGAeqg==} + peerDependencies: + echarts: '*' + vue: '*' + peerDependenciesMeta: + echarts: + optional: true + vue: + optional: true + + '@rollup/pluginutils@5.1.0': + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.21.0': + resolution: {integrity: sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.21.0': + resolution: {integrity: sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.21.0': + resolution: {integrity: sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.21.0': + resolution: {integrity: sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.21.0': + resolution: {integrity: sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.21.0': + resolution: {integrity: sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.21.0': + resolution: {integrity: sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.21.0': + resolution: {integrity: sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-powerpc64le-gnu@4.21.0': + resolution: {integrity: sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.21.0': + resolution: {integrity: sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-s390x-gnu@4.21.0': + resolution: {integrity: sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.21.0': + resolution: {integrity: sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.21.0': + resolution: {integrity: sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-win32-arm64-msvc@4.21.0': + resolution: {integrity: sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.21.0': + resolution: {integrity: sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.21.0': + resolution: {integrity: sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==} + cpu: [x64] + os: [win32] + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@sxzz/popperjs-es@2.11.7': + resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@types/conventional-commits-parser@5.0.0': + resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/gradient-string@1.1.6': + resolution: {integrity: sha512-LkaYxluY4G5wR1M4AKQUal2q61Di1yVVCw42ImFTuaIoQVgmV0WP1xUaLB8zwb47mp82vWTpePI9JmrjEnJ7nQ==} + + '@types/js-cookie@3.0.6': + resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.7': + resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} + + '@types/node@20.16.1': + resolution: {integrity: sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==} + + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/sortablejs@1.15.8': + resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==} + + '@types/tinycolor2@1.4.6': + resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} + + '@types/web-bluetooth@0.0.16': + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + + '@types/web-bluetooth@0.0.20': + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@vitejs/plugin-vue-jsx@4.0.1': + resolution: {integrity: sha512-7mg9HFGnFHMEwCdB6AY83cVK4A6sCqnrjFYF4WIlebYAQVVJ/sC/CiTruVdrRlhrFoeZ8rlMxY9wYpPTIRhhAg==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.0.0 + + '@vitejs/plugin-vue@5.1.2': + resolution: {integrity: sha512-nY9IwH12qeiJqumTCLJLE7IiNx7HZ39cbHaysEUd+Myvbz9KAqd2yq+U01Kab1R/H1BmiyM2ShTYlNH32Fzo3A==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.0': + resolution: {integrity: sha512-FTla+khE+sYK0qJP+6hwPAAUwiNHVMph4RUXpxf/FIPKUP61NFrVZorml4mjFShnueR2y9/j8/vnh09YwVdH7A==} + + '@volar/source-map@2.4.0': + resolution: {integrity: sha512-2ceY8/NEZvN6F44TXw2qRP6AQsvCYhV2bxaBPWxV9HqIfkbRydSksTFObCF1DBDNBfKiZTS8G/4vqV6cvjdOIQ==} + + '@volar/typescript@2.4.0': + resolution: {integrity: sha512-9zx3lQWgHmVd+JRRAHUSRiEhe4TlzL7U7e6ulWXOxHH/WNYxzKwCvZD7WYWEZFdw4dHfTD9vUR0yPQO6GilCaQ==} + + '@vue/babel-helper-vue-transform-on@1.2.2': + resolution: {integrity: sha512-nOttamHUR3YzdEqdM/XXDyCSdxMA9VizUKoroLX6yTyRtggzQMHXcmwh8a7ZErcJttIBIc9s68a1B8GZ+Dmvsw==} + + '@vue/babel-plugin-jsx@1.2.2': + resolution: {integrity: sha512-nYTkZUVTu4nhP199UoORePsql0l+wj7v/oyQjtThUVhJl1U+6qHuoVhIvR3bf7eVKjbCK+Cs2AWd7mi9Mpz9rA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-resolve-type@1.2.2': + resolution: {integrity: sha512-EntyroPwNg5IPVdUJupqs0CFzuf6lUrVvCspmv2J1FITLeGnUCuoGNNk78dgCusxEiYj6RMkTJflGSxk5aIC4A==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-core@3.4.38': + resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==} + + '@vue/compiler-dom@3.4.38': + resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==} + + '@vue/compiler-sfc@3.4.38': + resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==} + + '@vue/compiler-ssr@3.4.38': + resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.3': + resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==} + + '@vue/language-core@2.0.29': + resolution: {integrity: sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.4.38': + resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==} + + '@vue/runtime-core@3.4.38': + resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==} + + '@vue/runtime-dom@3.4.38': + resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==} + + '@vue/server-renderer@3.4.38': + resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==} + peerDependencies: + vue: 3.4.38 + + '@vue/shared@3.4.38': + resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==} + + '@vueuse/core@10.11.1': + resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} + + '@vueuse/core@9.13.0': + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + + '@vueuse/metadata@10.11.1': + resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} + + '@vueuse/metadata@9.13.0': + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + + '@vueuse/motion@2.2.3': + resolution: {integrity: sha512-QsS9P7MDKFF2j4eDPuo6lezUDPE7MQp4+unBeR2Ym8RkhtMEw/oVbnHPHL986rsmnnlUZkHVSv5kKoHJzjbtmQ==} + peerDependencies: + vue: '>=3.0.0' + + '@vueuse/shared@10.11.1': + resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} + + '@vueuse/shared@9.13.0': + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + + '@zougt/some-loader-utils@1.4.3': + resolution: {integrity: sha512-0FsoqSTQ+qOyp6x5Q6LZQ7xVwquEgLYiIStG3L8p0Q2GsGGYKDkOZ0mIpMt67aNdr8XLsbxXjzTl/iHtTz5zcA==} + engines: {node: '>= 10.13.0'} + hasBin: true + + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + animate.css@4.1.1: + resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axios@1.7.4: + resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bundle-import@0.0.1: + resolution: {integrity: sha512-L0z0iPX7t7ff5eZsK7oMEH+Ly2lzJczFKPHwrta6X8SF64a20R3wOrAOYK1MzHZVaWWugg9qlSTVfVwqvQJ2dw==} + + c12@1.11.1: + resolution: {integrity: sha512-KDU0TvSvVdaYcQKQ6iPHATGz/7p/KiVjPg4vQrB6Jg/wX9R0yl5RZxWm9IoZqaIHD2+6PZd81+KMGwRr/lRIUg==} + peerDependencies: + magicast: ^0.3.4 + peerDependenciesMeta: + magicast: + optional: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001651: + resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + compatx@0.1.8: + resolution: {integrity: sha512-jcbsEAR81Bt5s1qOFymBufmCbXCXbk0Ql+K5ouj6gCyx2yHlu6AgmGIi9HxfKixpUDO5bCFJUHQ5uM6ecbTebw==} + + computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cosmiconfig-typescript-loader@5.0.0: + resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} + engines: {node: '>=v16'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=8.2' + typescript: '>=4' + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + css-declaration-sorter@6.4.1: + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: ^8.0.9 + + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-functions-list@3.2.2: + resolution: {integrity: sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==} + engines: {node: '>=12 || >=16'} + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@5.2.14: + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano-preset-default@7.0.5: + resolution: {integrity: sha512-Jbzja0xaKwc5JzxPQoc+fotKpYtWEu4wQLMQe29CM0FjjdRjA4omvbGHl2DTGgARKxSTpPssBsok+ixv8uTBqw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano-preset-lite@2.1.3: + resolution: {integrity: sha512-samvnCll/DUVZu0Qc+JH36nt7dlaOT7WjOgg8SbLJ78sp51JZ12s2hyerxrarjPBG4O53rErUtOY2IYLYgBGEQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano-utils@3.1.0: + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano-utils@5.0.0: + resolution: {integrity: sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano@5.1.15: + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano@7.0.5: + resolution: {integrity: sha512-Aq0vqBLtpTT5Yxj+hLlLfNPFuRQCDIjx5JQAhhaedQKLNDvDGeVziF24PS+S1f0Z5KCxWvw0QVI3VNHNBITxVQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + + dayjs@1.11.12: + resolution: {integrity: sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + destr@2.0.3: + resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + echarts@5.5.1: + resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==} + + electron-to-chromium@1.5.11: + resolution: {integrity: sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew==} + + element-plus@2.8.0: + resolution: {integrity: sha512-7ngapVlVlQAjocVqD4MUKvKXlBneT9DSDk2mmBOSLRFWNm/HLDT15ozmsvUBfy18sajnyUeSIHTtINE8gfrGMg==} + peerDependencies: + vue: ^3.2.0 + + emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@0.4.1: + resolution: {integrity: sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==} + + esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-define-config@2.1.0: + resolution: {integrity: sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>=8.6.0'} + + eslint-plugin-prettier@5.2.1: + resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-vue@9.27.0: + resolution: {integrity: sha512-5Dw3yxEyuBSXTzT5/Ge1X5kIkRTQ3nvBn/VwPwInNiZBSJOO/timWMUaflONnFBzU6NhB68lxnCda7ULV5N7LA==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-scope@8.0.2: + resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.9.0: + resolution: {integrity: sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-entry-cache@9.0.0: + resolution: {integrity: sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==} + engines: {node: '>=18'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat-cache@5.0.0: + resolution: {integrity: sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==} + engines: {node: '>=18'} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + framesync@6.1.2: + resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-tsconfig@4.7.6: + resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} + + giget@1.2.3: + resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==} + hasBin: true + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} + engines: {node: '>=18'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + gradient-string@2.0.2: + resolution: {integrity: sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==} + engines: {node: '>=10'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + hash-sum@2.0.0: + resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + + highlight.js-async-webpack@1.0.4: + resolution: {integrity: sha512-IC0AwUgNr7BU8pqheaCEvOQvOtIZwO3I4rtbmT489Ndz8loE31IRmBmT6C4qDCbfZjzNO+k2w3VceZjTQ3JQ8Q==} + + highlight.js@9.18.5: + resolution: {integrity: sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==} + deprecated: Support has ended for 9.x series. Upgrade to @latest + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.4: + resolution: {integrity: sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==} + engines: {node: '>=18'} + hasBin: true + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-from-string@0.0.4: + resolution: {integrity: sha512-ZmtWHOGv55OEFb3HxfQH4L4vAR7g3HUm82N5LmvXugiXlaZ1j/epItoUDjQ+gJ+MjNl+apczmCnqGFq8q2CM6w==} + + import-meta-resolve@3.1.1: + resolution: {integrity: sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.15.0: + resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + knitwork@1.1.0: + resolution: {integrity: sha512-oHnmiBUVHz1V+URE77PNot2lv3QiYU2zQf1JjOVkMt3YDKGbu8NAFr+c4mcNOhdsGrB/VpVbRwPwhiXrPhxQbw==} + + known-css-properties@0.34.0: + resolution: {integrity: sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.1.1: + resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@15.2.9: + resolution: {integrity: sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.2.4: + resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} + engines: {node: '>=18.0.0'} + + local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + + localforage@1.10.0: + resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash-unified@1.0.3: + resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==} + peerDependencies: + '@types/lodash-es': '*' + lodash: '*' + lodash-es: '*' + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + mavon-editor@2.7.7: + resolution: {integrity: sha512-lXnYe+dztKepbv8bi2nedRqG/AwyUDF8gmkv9lHD3fpVJ1+pzAS6YILRIryKCvO9qPIOPEThHsda2DxtlzRsZA==} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-fetch-native@1.6.4: + resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + normalize-wheel-es@1.2.0: + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nprogress@0.2.0: + resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nypm@0.3.9: + resolution: {integrity: sha512-BI2SdqqTHg2d4wJh8P9A1W+bslg33vOE9IZDY6eR2QC+Pu1iNBVZUqczrd43rJb+fMzHU7ltAYKsEFY/kHMFcw==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + ohash@1.1.3: + resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@6.2.2: + resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + path-type@5.0.0: + resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} + engines: {node: '>=12'} + + path@0.12.7: + resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pinia@2.2.2: + resolution: {integrity: sha512-ja2XqFWZC36mupU4z1ZzxeTApV7DOw44cV4dhQ9sGwun+N89v/XP7+j7q6TanS1u1tdbK4r+1BUx7heMaIdagA==} + peerDependencies: + '@vue/composition-api': ^1.4.0 + typescript: '>=4.4.4' + vue: ^2.6.14 || ^3.3.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + typescript: + optional: true + + pinyin-pro@3.24.2: + resolution: {integrity: sha512-5tPyLhxT4CZ9dWqQRqm3X5ADdS18Sb2w0ranNBgr6jCrqO4O8gtfuyqG7Y6+1Mre+0n2VlhKDz+3P5oqSLrkOw==} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + popmotion@11.0.5: + resolution: {integrity: sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA==} + + postcss-calc@10.0.2: + resolution: {integrity: sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.38 + + postcss-calc@8.2.4: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.2.2 + + postcss-colormin@5.3.1: + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-colormin@7.0.2: + resolution: {integrity: sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-convert-values@5.1.3: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-convert-values@7.0.3: + resolution: {integrity: sha512-yJhocjCs2SQer0uZ9lXTMOwDowbxvhwFVrZeS6NPEij/XXthl73ggUmfwVvJM+Vaj5gtCKJV1jiUu4IhAUkX/Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-comments@5.1.2: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-comments@7.0.2: + resolution: {integrity: sha512-/Hje9Ls1IYcB9duELO/AyDUJI6aQVY3h5Rj1ziXgaLYCTi1iVBLnjg/TS0D6NszR/kDG6I86OwLmAYe+bvJjiQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-duplicates@5.1.0: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-duplicates@7.0.1: + resolution: {integrity: sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-empty@5.1.1: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-empty@7.0.0: + resolution: {integrity: sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-overridden@5.1.0: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-overridden@7.0.0: + resolution: {integrity: sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-html@1.7.0: + resolution: {integrity: sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==} + engines: {node: ^12 || >=14} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-import@16.1.0: + resolution: {integrity: sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==} + engines: {node: '>=18.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + + postcss-merge-longhand@5.1.7: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-merge-longhand@7.0.3: + resolution: {integrity: sha512-8waYomFxshdv6M9Em3QRM9MettRLDRcH2JQi2l0Z1KlYD/vhal3gbkeSES0NuACXOlZBB0V/B0AseHZaklzWOA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-merge-rules@5.1.4: + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-merge-rules@7.0.3: + resolution: {integrity: sha512-2eSas2p3voPxNfdI5sQrvIkMaeUHpVc3EezgVs18hz/wRTQAC9U99tp9j3W5Jx9/L3qHkEDvizEx/LdnmumIvQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-font-values@5.1.0: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-font-values@7.0.0: + resolution: {integrity: sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-gradients@5.1.1: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-gradients@7.0.0: + resolution: {integrity: sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-params@5.1.4: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-params@7.0.2: + resolution: {integrity: sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-selectors@5.2.1: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-selectors@7.0.3: + resolution: {integrity: sha512-SxTgUQSgBk6wEqzQZKEv1xQYIp9UBju6no9q+npohzSdhuSICQdkqmD1UMKkZWItS3olJSJMDDEY9WOJ5oGJew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-normalize-charset@5.1.0: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-charset@7.0.0: + resolution: {integrity: sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-display-values@5.1.0: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-display-values@7.0.0: + resolution: {integrity: sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-positions@5.1.1: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-positions@7.0.0: + resolution: {integrity: sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-repeat-style@5.1.1: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-repeat-style@7.0.0: + resolution: {integrity: sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-string@5.1.0: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-string@7.0.0: + resolution: {integrity: sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-timing-functions@5.1.0: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-timing-functions@7.0.0: + resolution: {integrity: sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-unicode@5.1.1: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-unicode@7.0.2: + resolution: {integrity: sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-url@5.1.0: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-url@7.0.0: + resolution: {integrity: sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-whitespace@5.1.1: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-whitespace@7.0.0: + resolution: {integrity: sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-ordered-values@5.1.3: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-ordered-values@7.0.1: + resolution: {integrity: sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-initial@5.1.2: + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-reduce-initial@7.0.2: + resolution: {integrity: sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-transforms@5.1.0: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-reduce-transforms@7.0.0: + resolution: {integrity: sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-safe-parser@7.0.0: + resolution: {integrity: sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-sorting@8.0.2: + resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} + peerDependencies: + postcss: ^8.4.20 + + postcss-svgo@5.1.0: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-svgo@7.0.1: + resolution: {integrity: sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} + peerDependencies: + postcss: ^8.4.31 + + postcss-unique-selectors@5.1.1: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-unique-selectors@7.0.2: + resolution: {integrity: sha512-CjSam+7Vf8cflJQsHrMS0P2hmy9u0+n/P001kb5eAszLmhjMqrt/i5AqQuNFihhViwDvEAezqTmXqaYXL2ugMw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.41: + resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + responsive-storage@2.2.0: + resolution: {integrity: sha512-94W5Chr2F5kDBT6J+OCOeJguEkSTDc3jPOUQXYmzNG64DCNl5p7hoBDF7bx7u6EXAEcpUKF64OZR4b7Nn8h/Gg==} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rollup-plugin-external-globals@0.10.0: + resolution: {integrity: sha512-RXlupZrmn97AaaS5dWnktkjM+Iy+od0E+8L0mUkMIs3iuoUXNJebueQocQKV7Ircd54fSGGmkBaXwNzY05J1yQ==} + peerDependencies: + rollup: ^2.25.0 || ^3.3.0 || ^4.1.4 + + rollup-plugin-visualizer@5.12.0: + resolution: {integrity: sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rollup: + optional: true + + rollup@4.21.0: + resolution: {integrity: sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + sass@1.77.8: + resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + sortablejs@1.15.2: + resolution: {integrity: sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-hash@1.1.3: + resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + + style-value-types@5.1.2: + resolution: {integrity: sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q==} + + stylehacks@5.1.1: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + stylehacks@7.0.3: + resolution: {integrity: sha512-4DqtecvI/Nd+2BCvW9YEF6lhBN5UM50IJ1R3rnEAhBwbCKf4VehRf+uqvnVArnBayjYD/WtT3g0G/HSRxWfTRg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + stylelint-config-html@1.1.0: + resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recess-order@5.0.1: + resolution: {integrity: sha512-rKbGkoa3h0rINrGln9TFVowvSCLgPJC5O0EuPiqlqWcJMb1lImEtXktcjFCVz+hwtSUiHD3ijJc3vP9muFOgJg==} + peerDependencies: + stylelint: '>=16' + + stylelint-config-recommended-scss@14.1.0: + resolution: {integrity: sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==} + engines: {node: '>=18.12.0'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^16.6.1 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-recommended-vue@1.5.0: + resolution: {integrity: sha512-65TAK/clUqkNtkZLcuytoxU0URQYlml+30Nhop7sRkCZ/mtWdXt7T+spPSB3KMKlb+82aEVJ4OrcstyDBdbosg==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recommended@14.0.1: + resolution: {integrity: sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.1.0 + + stylelint-config-standard-scss@13.1.0: + resolution: {integrity: sha512-Eo5w7/XvwGHWkeGLtdm2FZLOMYoZl1omP2/jgFCXyl2x5yNz7/8vv4Tj6slHvMSSUNTaGoam/GAZ0ZhukvalfA==} + engines: {node: '>=18.12.0'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^16.3.1 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-standard@36.0.1: + resolution: {integrity: sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.1.0 + + stylelint-order@6.0.4: + resolution: {integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1 + + stylelint-prettier@5.0.2: + resolution: {integrity: sha512-qJ+BN+1T2ZcKz9WIrv0x+eFGHzSUnXfXd5gL///T6XoJvr3D8/ztzz2fhtmXef7Vb8P33zBXmLTTveByr0nwBw==} + engines: {node: '>=18.12.0'} + peerDependencies: + prettier: '>=3.0.0' + stylelint: '>=16.0.0' + + stylelint-scss@6.5.0: + resolution: {integrity: sha512-yOnYlr71wrTPT3rYyUurgTj6Rw7JUtzsZQsiPEjvs+k/yqoYHdweqpw6XN/ARpxjAuvJpddoMUvV8aAIpvUwTg==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.0.2 + + stylelint@16.8.2: + resolution: {integrity: sha512-fInKATippQhcSm7AB+T32GpI+626yohrg33GkFT/5jzliUw5qhlwZq2UQQwgl3HsHrf09oeARi0ZwgY/UWEv9A==} + engines: {node: '>=18.12.0'} + hasBin: true + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.0.0: + resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + synckit@0.9.1: + resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} + engines: {node: ^14.18.0 || >=16.0.0} + + table@6.8.2: + resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} + engines: {node: '>=10.0.0'} + + tailwindcss@3.4.10: + resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==} + engines: {node: '>=14.0.0'} + hasBin: true + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + + tinygradient@1.1.5: + resolution: {integrity: sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==} + + tippy.js@6.3.7: + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + unctx@2.3.1: + resolution: {integrity: sha512-PhKke8ZYauiqh3FEMVNm7ljvzQiph0Mt3GBRve03IJm7ukfaON2OBK795tLwhbyfzknuRRkW0+Ze+CQUmzOZ+A==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unimport@3.10.0: + resolution: {integrity: sha512-/UvKRfWx3mNDWwWQhR62HsoM3wxHwYdTq8ellZzMOHnnw4Dp8tovgthyW7DjTrbjDL+i4idOp06voz2VKlvrLw==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unplugin@1.12.2: + resolution: {integrity: sha512-bEqQxeC7rxtxPZ3M5V4Djcc4lQqKPgGe3mAWZvxcSmX5jhGxll19NliaRzQSQPrk4xJZSGniK3puLWpRuZN7VQ==} + engines: {node: '>=14.0.0'} + + untyped@1.4.2: + resolution: {integrity: sha512-nC5q0DnPEPVURPhfPQLahhSTnemVtPzdx7ofiRxXpOB2SYnb3MfdU3DVGyJdS8Lx+tBWeAePO8BfU/3EgksM7Q==} + hasBin: true + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + vite-plugin-cdn-import@1.0.1: + resolution: {integrity: sha512-lgjLxgwFSKvJLbqjVBirUZ0rQo00GpUGJzRpgQu8RyBw9LA7jaqG6fUMQzBC9qWmTGabPC3iOzwCcoi7PseRAQ==} + + vite-plugin-checker@0.7.2: + resolution: {integrity: sha512-xeYeJbG0gaCaT0QcUC4B2Zo4y5NR8ZhYenc5gPbttrZvraRFwkEADCYwq+BfEHl9zYz7yf85TxsiGoYwyyIjhw==} + engines: {node: '>=14.16'} + peerDependencies: + '@biomejs/biome': '>=1.7' + eslint: '>=7' + meow: ^9.0.0 + optionator: ^0.9.1 + stylelint: '>=13' + typescript: '*' + vite: '>=2.0.0' + vls: '*' + vti: '*' + vue-tsc: '>=2.0.0' + peerDependenciesMeta: + '@biomejs/biome': + optional: true + eslint: + optional: true + meow: + optional: true + optionator: + optional: true + stylelint: + optional: true + typescript: + optional: true + vls: + optional: true + vti: + optional: true + vue-tsc: + optional: true + + vite-plugin-compression@0.5.1: + resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-externals@0.6.2: + resolution: {integrity: sha512-R5oVY8xDJjLXLTs2XDYzvYbc/RTZuIwOx2xcFbYf+/VXB6eJuatDgt8jzQ7kZ+IrgwQhe6tU8U2fTyy72C25CQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-fake-server@2.1.1: + resolution: {integrity: sha512-QUgssvE7jI9XU1WuDZ3gkzzi9GzVeapELIlFNMvmE2swDKL7O2y2nV0kRZ9VYOsD+hV312uSJyzHBJvcmBw7UQ==} + + vite-plugin-remove-console@2.2.0: + resolution: {integrity: sha512-qgjh5pz75MdE9Kzs8J0kBwaCfifHV0ezRbB9rpGsIOxam+ilcGV7WOk91vFJXquzRmiKrFh3Hxlh0JJWAmXTbQ==} + + vite-plugin-router-warn@1.0.0: + resolution: {integrity: sha512-jnr7faHJPkKxukBXVpg7Ui1UDqhmxD7xU6JGidq8ivSHTsNAPqzSpPpwW8O1PBP/0+Owq4bLfNNk11drOkz4xA==} + + vite-plugin-vue-inspector@5.1.3: + resolution: {integrity: sha512-pMrseXIDP1Gb38mOevY+BvtNGNqiqmqa2pKB99lnLsADQww9w9xMbAfT4GB6RUoaOkSPrtlXqpq2Fq+Dj2AgFg==} + peerDependencies: + vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 + + vite-svg-loader@5.1.0: + resolution: {integrity: sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==} + peerDependencies: + vue: '>=3.2.13' + + vite@5.4.1: + resolution: {integrity: sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vscode-jsonrpc@6.0.0: + resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} + engines: {node: '>=8.0.0 || >=10.0.0'} + + vscode-languageclient@7.0.0: + resolution: {integrity: sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==} + engines: {vscode: ^1.52.0} + + vscode-languageserver-protocol@3.16.0: + resolution: {integrity: sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.16.0: + resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==} + + vscode-languageserver@7.0.0: + resolution: {integrity: sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==} + hasBin: true + + vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-i18n@9.14.0: + resolution: {integrity: sha512-LxmpRuCt2rI8gqU+kxeflRZMQn4D5+4M3oP3PWZdowW/ePJraHqhF7p4CuaME52mUxdw3Mmy2yAUKgfZYgCRjA==} + engines: {node: '>= 16'} + peerDependencies: + vue: ^3.0.0 + + vue-router@4.4.3: + resolution: {integrity: sha512-sv6wmNKx2j3aqJQDMxLFzs/u/mjA9Z5LCgy6BE0f7yFWMjrPLnS/sPNn8ARY/FXw6byV18EFutn5lTO6+UsV5A==} + peerDependencies: + vue: ^3.2.0 + + vue-tippy@6.4.4: + resolution: {integrity: sha512-0C5TSU482FvjhEeKrPkz08tzyC/KJC0CiEbm3yW9oS+n3fa03ajEzU2QcxI9oR6Hwlg8NOP0U6T4EsGuccq6YQ==} + peerDependencies: + vue: ^3.2.0 + + vue-tsc@2.0.29: + resolution: {integrity: sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue-types@5.1.3: + resolution: {integrity: sha512-3Wy6QcZl0VusCCHX3vYrWSILFlrOB2EQDoySnuYmASM5cUp1FivJGfkS5lp1CutDgyRb41g32r/1QCmiBj5i1Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + vue: ^2.0.0 || ^3.0.0 + peerDependenciesMeta: + vue: + optional: true + + vue@3.4.38: + resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml-eslint-parser@1.2.3: + resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==} + engines: {node: ^14.17.0 || >=16.0.0} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.5.0: + resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + zrender@5.6.0: + resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + + '@babel/compat-data@7.25.2': {} + + '@babel/core@7.25.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.0 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helpers': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + convert-source-map: 2.0.0 + debug: 4.3.6 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.25.0': + dependencies: + '@babel/types': 7.25.2 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-annotate-as-pure@7.24.7': + dependencies: + '@babel/types': 7.25.2 + + '@babel/helper-compilation-targets@7.25.2': + dependencies: + '@babel/compat-data': 7.25.2 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.25.0(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.8 + '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/traverse': 7.25.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-member-expression-to-functions@7.24.8': + dependencies: + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.22.15': + dependencies: + '@babel/types': 7.25.2 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.24.7': + dependencies: + '@babel/types': 7.25.2 + + '@babel/helper-plugin-utils@7.24.8': {} + + '@babel/helper-replace-supers@7.25.0(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-member-expression-to-functions': 7.24.8 + '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + dependencies: + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.24.8': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.8': {} + + '@babel/helpers@7.25.0': + dependencies: + '@babel/template': 7.25.0 + '@babel/types': 7.25.2 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.25.3': + dependencies: + '@babel/types': 7.25.2 + + '@babel/plugin-proposal-decorators@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-decorators': 7.24.7(@babel/core@7.25.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-syntax-decorators@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-transform-typescript@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.25.2) + transitivePeerDependencies: + - supports-color + + '@babel/standalone@7.25.3': + optional: true + + '@babel/template@7.25.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.25.3 + '@babel/types': 7.25.2 + + '@babel/traverse@7.25.3': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/types': 7.25.2 + debug: 4.3.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.25.2': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@commitlint/cli@19.4.0(@types/node@20.16.1)(typescript@5.5.4)': + dependencies: + '@commitlint/format': 19.3.0 + '@commitlint/lint': 19.2.2 + '@commitlint/load': 19.4.0(@types/node@20.16.1)(typescript@5.5.4) + '@commitlint/read': 19.4.0 + '@commitlint/types': 19.0.3 + execa: 8.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@19.2.2': + dependencies: + '@commitlint/types': 19.0.3 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@19.0.3': + dependencies: + '@commitlint/types': 19.0.3 + ajv: 8.17.1 + + '@commitlint/ensure@19.0.3': + dependencies: + '@commitlint/types': 19.0.3 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@19.0.0': {} + + '@commitlint/format@19.3.0': + dependencies: + '@commitlint/types': 19.0.3 + chalk: 5.3.0 + + '@commitlint/is-ignored@19.2.2': + dependencies: + '@commitlint/types': 19.0.3 + semver: 7.6.3 + + '@commitlint/lint@19.2.2': + dependencies: + '@commitlint/is-ignored': 19.2.2 + '@commitlint/parse': 19.0.3 + '@commitlint/rules': 19.0.3 + '@commitlint/types': 19.0.3 + + '@commitlint/load@19.4.0(@types/node@20.16.1)(typescript@5.5.4)': + dependencies: + '@commitlint/config-validator': 19.0.3 + '@commitlint/execute-rule': 19.0.0 + '@commitlint/resolve-extends': 19.1.0 + '@commitlint/types': 19.0.3 + chalk: 5.3.0 + cosmiconfig: 9.0.0(typescript@5.5.4) + cosmiconfig-typescript-loader: 5.0.0(@types/node@20.16.1)(cosmiconfig@9.0.0(typescript@5.5.4))(typescript@5.5.4) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@19.0.0': {} + + '@commitlint/parse@19.0.3': + dependencies: + '@commitlint/types': 19.0.3 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/read@19.4.0': + dependencies: + '@commitlint/top-level': 19.0.0 + '@commitlint/types': 19.0.3 + execa: 8.0.1 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + + '@commitlint/resolve-extends@19.1.0': + dependencies: + '@commitlint/config-validator': 19.0.3 + '@commitlint/types': 19.0.3 + global-directory: 4.0.1 + import-meta-resolve: 4.1.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@19.0.3': + dependencies: + '@commitlint/ensure': 19.0.3 + '@commitlint/message': 19.0.0 + '@commitlint/to-lines': 19.0.0 + '@commitlint/types': 19.0.3 + execa: 8.0.1 + + '@commitlint/to-lines@19.0.0': {} + + '@commitlint/top-level@19.0.0': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@19.0.3': + dependencies: + '@types/conventional-commits-parser': 5.0.0 + chalk: 5.3.0 + + '@csstools/css-parser-algorithms@3.0.1(@csstools/css-tokenizer@3.0.1)': + dependencies: + '@csstools/css-tokenizer': 3.0.1 + + '@csstools/css-tokenizer@3.0.1': {} + + '@csstools/media-query-list-parser@3.0.1(@csstools/css-parser-algorithms@3.0.1(@csstools/css-tokenizer@3.0.1))(@csstools/css-tokenizer@3.0.1)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.1(@csstools/css-tokenizer@3.0.1) + '@csstools/css-tokenizer': 3.0.1 + + '@csstools/selector-specificity@4.0.0(postcss-selector-parser@6.1.2)': + dependencies: + postcss-selector-parser: 6.1.2 + + '@ctrl/tinycolor@3.6.1': {} + + '@dual-bundle/import-meta-resolve@4.1.0': {} + + '@element-plus/icons-vue@2.3.1(vue@3.4.38(typescript@5.5.4))': + dependencies: + vue: 3.4.38(typescript@5.5.4) + + '@esbuild/aix-ppc64@0.19.12': + optional: true + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.19.12': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.19.12': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.19.12': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.19.12': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.19.12': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.19.12': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.19.12': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.19.12': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.19.12': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.19.12': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.19.12': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.19.12': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.19.12': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.19.12': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.19.12': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.19.12': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.19.12': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.19.12': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.19.12': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.19.12': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.19.12': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.19.12': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@9.9.0(jiti@1.21.6))': + dependencies: + eslint: 9.9.0(jiti@1.21.6) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.0': {} + + '@eslint/config-array@0.17.1': + dependencies: + '@eslint/object-schema': 2.1.4 + debug: 4.3.6 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.1.0': + dependencies: + ajv: 6.12.6 + debug: 4.3.6 + espree: 10.1.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.9.0': {} + + '@eslint/object-schema@2.1.4': {} + + '@faker-js/faker@8.4.1': {} + + '@floating-ui/core@1.6.7': + dependencies: + '@floating-ui/utils': 0.2.7 + + '@floating-ui/dom@1.6.10': + dependencies: + '@floating-ui/core': 1.6.7 + '@floating-ui/utils': 0.2.7 + + '@floating-ui/utils@0.2.7': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.0': {} + + '@iconify-icons/ep@1.2.12': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-icons/ri@1.2.10': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + + '@iconify/vue@4.1.2(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.4.38(typescript@5.5.4) + + '@intlify/bundle-utils@8.0.0(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4)))': + dependencies: + '@intlify/message-compiler': 9.14.0 + '@intlify/shared': 9.14.0 + acorn: 8.12.1 + escodegen: 2.1.0 + estree-walker: 2.0.2 + jsonc-eslint-parser: 2.4.0 + mlly: 1.7.1 + source-map-js: 1.2.0 + yaml-eslint-parser: 1.2.3 + optionalDependencies: + vue-i18n: 9.14.0(vue@3.4.38(typescript@5.5.4)) + + '@intlify/core-base@9.14.0': + dependencies: + '@intlify/message-compiler': 9.14.0 + '@intlify/shared': 9.14.0 + + '@intlify/message-compiler@9.14.0': + dependencies: + '@intlify/shared': 9.14.0 + source-map-js: 1.2.0 + + '@intlify/shared@9.14.0': {} + + '@intlify/unplugin-vue-i18n@4.0.0(rollup@4.21.0)(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4)))': + dependencies: + '@intlify/bundle-utils': 8.0.0(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4))) + '@intlify/shared': 9.14.0 + '@rollup/pluginutils': 5.1.0(rollup@4.21.0) + '@vue/compiler-sfc': 3.4.38 + debug: 4.3.6 + fast-glob: 3.3.2 + js-yaml: 4.1.0 + json5: 2.2.3 + pathe: 1.1.2 + picocolors: 1.0.1 + source-map-js: 1.2.0 + unplugin: 1.12.2 + optionalDependencies: + vue-i18n: 9.14.0(vue@3.4.38(typescript@5.5.4)) + transitivePeerDependencies: + - rollup + - supports-color + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@nuxt/kit@3.12.4(rollup@4.21.0)': + dependencies: + '@nuxt/schema': 3.12.4(rollup@4.21.0) + c12: 1.11.1 + consola: 3.2.3 + defu: 6.1.4 + destr: 2.0.3 + globby: 14.0.2 + hash-sum: 2.0.0 + ignore: 5.3.2 + jiti: 1.21.6 + klona: 2.0.6 + knitwork: 1.1.0 + mlly: 1.7.1 + pathe: 1.1.2 + pkg-types: 1.1.3 + scule: 1.3.0 + semver: 7.6.3 + ufo: 1.5.4 + unctx: 2.3.1 + unimport: 3.10.0(rollup@4.21.0) + untyped: 1.4.2 + transitivePeerDependencies: + - magicast + - rollup + - supports-color + optional: true + + '@nuxt/schema@3.12.4(rollup@4.21.0)': + dependencies: + compatx: 0.1.8 + consola: 3.2.3 + defu: 6.1.4 + hookable: 5.5.3 + pathe: 1.1.2 + pkg-types: 1.1.3 + scule: 1.3.0 + std-env: 3.7.0 + ufo: 1.5.4 + uncrypto: 0.1.3 + unimport: 3.10.0(rollup@4.21.0) + untyped: 1.4.2 + transitivePeerDependencies: + - rollup + - supports-color + optional: true + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.1.1': {} + + '@popperjs/core@2.11.8': {} + + '@pureadmin/descriptions@1.2.1(echarts@5.5.1)(element-plus@2.8.0(vue@3.4.38(typescript@5.5.4)))(typescript@5.5.4)': + dependencies: + '@element-plus/icons-vue': 2.3.1(vue@3.4.38(typescript@5.5.4)) + '@pureadmin/utils': 2.4.8(echarts@5.5.1)(vue@3.4.38(typescript@5.5.4)) + element-plus: 2.8.0(vue@3.4.38(typescript@5.5.4)) + vue: 3.4.38(typescript@5.5.4) + transitivePeerDependencies: + - echarts + - typescript + + '@pureadmin/table@3.2.0(element-plus@2.8.0(vue@3.4.38(typescript@5.5.4)))(typescript@5.5.4)': + dependencies: + element-plus: 2.8.0(vue@3.4.38(typescript@5.5.4)) + vue: 3.4.38(typescript@5.5.4) + transitivePeerDependencies: + - typescript + + '@pureadmin/theme@3.2.0': + dependencies: + '@zougt/some-loader-utils': 1.4.3 + fs-extra: 11.2.0 + string-hash: 1.1.3 + + '@pureadmin/utils@2.4.8(echarts@5.5.1)(vue@3.4.38(typescript@5.5.4))': + optionalDependencies: + echarts: 5.5.1 + vue: 3.4.38(typescript@5.5.4) + + '@rollup/pluginutils@5.1.0(rollup@4.21.0)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.21.0 + + '@rollup/rollup-android-arm-eabi@4.21.0': + optional: true + + '@rollup/rollup-android-arm64@4.21.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.21.0': + optional: true + + '@rollup/rollup-darwin-x64@4.21.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.21.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.21.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.21.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.21.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.21.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.21.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.21.0': + optional: true + + '@sindresorhus/merge-streams@2.3.0': + optional: true + + '@sxzz/popperjs-es@2.11.7': {} + + '@trysound/sax@0.2.0': {} + + '@types/conventional-commits-parser@5.0.0': + dependencies: + '@types/node': 20.16.1 + + '@types/estree@1.0.5': {} + + '@types/gradient-string@1.1.6': + dependencies: + '@types/tinycolor2': 1.4.6 + + '@types/js-cookie@3.0.6': {} + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.7 + + '@types/lodash@4.17.7': {} + + '@types/node@20.16.1': + dependencies: + undici-types: 6.19.8 + + '@types/nprogress@0.2.3': {} + + '@types/qs@6.9.15': {} + + '@types/sortablejs@1.15.8': {} + + '@types/tinycolor2@1.4.6': {} + + '@types/web-bluetooth@0.0.16': {} + + '@types/web-bluetooth@0.0.20': {} + + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/utils': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 9.9.0(jiti@1.21.6) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4)': + dependencies: + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.3.6 + eslint: 9.9.0(jiti@1.21.6) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + + '@typescript-eslint/type-utils@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4)': + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4) + '@typescript-eslint/utils': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4) + debug: 4.3.6 + eslint: 9.9.0(jiti@1.21.6) + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.18.0': {} + + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.3.6 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0(jiti@1.21.6)) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4) + eslint: 9.9.0(jiti@1.21.6) + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + + '@vitejs/plugin-vue-jsx@4.0.1(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8))(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@babel/core': 7.25.2 + '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2) + '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.25.2) + vite: 5.4.1(@types/node@20.16.1)(sass@1.77.8) + vue: 3.4.38(typescript@5.5.4) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@5.1.2(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8))(vue@3.4.38(typescript@5.5.4))': + dependencies: + vite: 5.4.1(@types/node@20.16.1)(sass@1.77.8) + vue: 3.4.38(typescript@5.5.4) + + '@volar/language-core@2.4.0': + dependencies: + '@volar/source-map': 2.4.0 + + '@volar/source-map@2.4.0': {} + + '@volar/typescript@2.4.0': + dependencies: + '@volar/language-core': 2.4.0 + path-browserify: 1.0.1 + vscode-uri: 3.0.8 + + '@vue/babel-helper-vue-transform-on@1.2.2': {} + + '@vue/babel-plugin-jsx@1.2.2(@babel/core@7.25.2)': + dependencies: + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + '@vue/babel-helper-vue-transform-on': 1.2.2 + '@vue/babel-plugin-resolve-type': 1.2.2(@babel/core@7.25.2) + camelcase: 6.3.0 + html-tags: 3.3.1 + svg-tags: 1.0.0 + optionalDependencies: + '@babel/core': 7.25.2 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@1.2.2(@babel/core@7.25.2)': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/parser': 7.25.3 + '@vue/compiler-sfc': 3.4.38 + + '@vue/compiler-core@3.4.38': + dependencies: + '@babel/parser': 7.25.3 + '@vue/shared': 3.4.38 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + + '@vue/compiler-dom@3.4.38': + dependencies: + '@vue/compiler-core': 3.4.38 + '@vue/shared': 3.4.38 + + '@vue/compiler-sfc@3.4.38': + dependencies: + '@babel/parser': 7.25.3 + '@vue/compiler-core': 3.4.38 + '@vue/compiler-dom': 3.4.38 + '@vue/compiler-ssr': 3.4.38 + '@vue/shared': 3.4.38 + estree-walker: 2.0.2 + magic-string: 0.30.11 + postcss: 8.4.41 + source-map-js: 1.2.0 + + '@vue/compiler-ssr@3.4.38': + dependencies: + '@vue/compiler-dom': 3.4.38 + '@vue/shared': 3.4.38 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.3': {} + + '@vue/language-core@2.0.29(typescript@5.5.4)': + dependencies: + '@volar/language-core': 2.4.0 + '@vue/compiler-dom': 3.4.38 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.4.38 + computeds: 0.0.1 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.5.4 + + '@vue/reactivity@3.4.38': + dependencies: + '@vue/shared': 3.4.38 + + '@vue/runtime-core@3.4.38': + dependencies: + '@vue/reactivity': 3.4.38 + '@vue/shared': 3.4.38 + + '@vue/runtime-dom@3.4.38': + dependencies: + '@vue/reactivity': 3.4.38 + '@vue/runtime-core': 3.4.38 + '@vue/shared': 3.4.38 + csstype: 3.1.3 + + '@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@vue/compiler-ssr': 3.4.38 + '@vue/shared': 3.4.38 + vue: 3.4.38(typescript@5.5.4) + + '@vue/shared@3.4.38': {} + + '@vueuse/core@10.11.1(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 10.11.1 + '@vueuse/shared': 10.11.1(vue@3.4.38(typescript@5.5.4)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/core@9.13.0(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.4.38(typescript@5.5.4)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@10.11.1': {} + + '@vueuse/metadata@9.13.0': {} + + '@vueuse/motion@2.2.3(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) + '@vueuse/shared': 10.11.1(vue@3.4.38(typescript@5.5.4)) + csstype: 3.1.3 + framesync: 6.1.2 + popmotion: 11.0.5 + style-value-types: 5.1.2 + vue: 3.4.38(typescript@5.5.4) + optionalDependencies: + '@nuxt/kit': 3.12.4(rollup@4.21.0) + transitivePeerDependencies: + - '@vue/composition-api' + - magicast + - rollup + - supports-color + + '@vueuse/shared@10.11.1(vue@3.4.38(typescript@5.5.4))': + dependencies: + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/shared@9.13.0(vue@3.4.38(typescript@5.5.4))': + dependencies: + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@zougt/some-loader-utils@1.4.3': + dependencies: + cac: 6.7.14 + color: 4.2.3 + cssnano: 5.1.15(postcss@8.4.41) + cssnano-preset-lite: 2.1.3(postcss@8.4.41) + fs-extra: 10.1.0 + postcss: 8.4.41 + prettier: 2.8.8 + uuid: 8.3.2 + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + animate.css@4.1.1: {} + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + array-ify@1.0.0: {} + + array-union@2.1.0: {} + + astral-regex@2.0.0: {} + + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + autoprefixer@10.4.20(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-lite: 1.0.30001651 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.1 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + axios@1.7.4: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + balanced-match@2.0.0: {} + + binary-extensions@2.3.0: {} + + boolbase@1.0.0: {} + + boxen@7.1.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.3.0 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.23.3: + dependencies: + caniuse-lite: 1.0.30001651 + electron-to-chromium: 1.5.11 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.23.3) + + bundle-import@0.0.1: + dependencies: + get-tsconfig: 4.7.6 + import-from-string: 0.0.4 + + c12@1.11.1: + dependencies: + chokidar: 3.6.0 + confbox: 0.1.7 + defu: 6.1.4 + dotenv: 16.4.5 + giget: 1.2.3 + jiti: 1.21.6 + mlly: 1.7.1 + ohash: 1.1.3 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + pkg-types: 1.1.3 + rc9: 2.1.2 + optional: true + + cac@6.7.14: {} + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + camelcase@6.3.0: {} + + camelcase@7.0.1: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.23.3 + caniuse-lite: 1.0.30001651 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001651: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.3.0: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chownr@2.0.0: + optional: true + + citty@0.1.6: + dependencies: + consola: 3.2.3 + optional: true + + cli-boxes@3.0.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + commander@4.1.1: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + compatx@0.1.8: + optional: true + + computeds@0.0.1: {} + + concat-map@0.0.1: {} + + confbox@0.1.7: {} + + consola@3.2.3: + optional: true + + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@7.0.2: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + + convert-source-map@2.0.0: {} + + cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.1)(cosmiconfig@9.0.0(typescript@5.5.4))(typescript@5.5.4): + dependencies: + '@types/node': 20.16.1 + cosmiconfig: 9.0.0(typescript@5.5.4) + jiti: 1.21.6 + typescript: 5.5.4 + + cosmiconfig@9.0.0(typescript@5.5.4): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.5.4 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-declaration-sorter@6.4.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + css-declaration-sorter@7.2.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + css-functions-list@3.2.2: {} + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.0 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.0 + + css-what@6.1.0: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@5.2.14(postcss@8.4.41): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.4.41) + cssnano-utils: 3.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-calc: 8.2.4(postcss@8.4.41) + postcss-colormin: 5.3.1(postcss@8.4.41) + postcss-convert-values: 5.1.3(postcss@8.4.41) + postcss-discard-comments: 5.1.2(postcss@8.4.41) + postcss-discard-duplicates: 5.1.0(postcss@8.4.41) + postcss-discard-empty: 5.1.1(postcss@8.4.41) + postcss-discard-overridden: 5.1.0(postcss@8.4.41) + postcss-merge-longhand: 5.1.7(postcss@8.4.41) + postcss-merge-rules: 5.1.4(postcss@8.4.41) + postcss-minify-font-values: 5.1.0(postcss@8.4.41) + postcss-minify-gradients: 5.1.1(postcss@8.4.41) + postcss-minify-params: 5.1.4(postcss@8.4.41) + postcss-minify-selectors: 5.2.1(postcss@8.4.41) + postcss-normalize-charset: 5.1.0(postcss@8.4.41) + postcss-normalize-display-values: 5.1.0(postcss@8.4.41) + postcss-normalize-positions: 5.1.1(postcss@8.4.41) + postcss-normalize-repeat-style: 5.1.1(postcss@8.4.41) + postcss-normalize-string: 5.1.0(postcss@8.4.41) + postcss-normalize-timing-functions: 5.1.0(postcss@8.4.41) + postcss-normalize-unicode: 5.1.1(postcss@8.4.41) + postcss-normalize-url: 5.1.0(postcss@8.4.41) + postcss-normalize-whitespace: 5.1.1(postcss@8.4.41) + postcss-ordered-values: 5.1.3(postcss@8.4.41) + postcss-reduce-initial: 5.1.2(postcss@8.4.41) + postcss-reduce-transforms: 5.1.0(postcss@8.4.41) + postcss-svgo: 5.1.0(postcss@8.4.41) + postcss-unique-selectors: 5.1.1(postcss@8.4.41) + + cssnano-preset-default@7.0.5(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + css-declaration-sorter: 7.2.0(postcss@8.4.41) + cssnano-utils: 5.0.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-calc: 10.0.2(postcss@8.4.41) + postcss-colormin: 7.0.2(postcss@8.4.41) + postcss-convert-values: 7.0.3(postcss@8.4.41) + postcss-discard-comments: 7.0.2(postcss@8.4.41) + postcss-discard-duplicates: 7.0.1(postcss@8.4.41) + postcss-discard-empty: 7.0.0(postcss@8.4.41) + postcss-discard-overridden: 7.0.0(postcss@8.4.41) + postcss-merge-longhand: 7.0.3(postcss@8.4.41) + postcss-merge-rules: 7.0.3(postcss@8.4.41) + postcss-minify-font-values: 7.0.0(postcss@8.4.41) + postcss-minify-gradients: 7.0.0(postcss@8.4.41) + postcss-minify-params: 7.0.2(postcss@8.4.41) + postcss-minify-selectors: 7.0.3(postcss@8.4.41) + postcss-normalize-charset: 7.0.0(postcss@8.4.41) + postcss-normalize-display-values: 7.0.0(postcss@8.4.41) + postcss-normalize-positions: 7.0.0(postcss@8.4.41) + postcss-normalize-repeat-style: 7.0.0(postcss@8.4.41) + postcss-normalize-string: 7.0.0(postcss@8.4.41) + postcss-normalize-timing-functions: 7.0.0(postcss@8.4.41) + postcss-normalize-unicode: 7.0.2(postcss@8.4.41) + postcss-normalize-url: 7.0.0(postcss@8.4.41) + postcss-normalize-whitespace: 7.0.0(postcss@8.4.41) + postcss-ordered-values: 7.0.1(postcss@8.4.41) + postcss-reduce-initial: 7.0.2(postcss@8.4.41) + postcss-reduce-transforms: 7.0.0(postcss@8.4.41) + postcss-svgo: 7.0.1(postcss@8.4.41) + postcss-unique-selectors: 7.0.2(postcss@8.4.41) + + cssnano-preset-lite@2.1.3(postcss@8.4.41): + dependencies: + cssnano-utils: 3.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-discard-comments: 5.1.2(postcss@8.4.41) + postcss-discard-empty: 5.1.1(postcss@8.4.41) + postcss-normalize-whitespace: 5.1.1(postcss@8.4.41) + + cssnano-utils@3.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + cssnano-utils@5.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + cssnano@5.1.15(postcss@8.4.41): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.4.41) + lilconfig: 2.1.0 + postcss: 8.4.41 + yaml: 1.10.2 + + cssnano@7.0.5(postcss@8.4.41): + dependencies: + cssnano-preset-default: 7.0.5(postcss@8.4.41) + lilconfig: 3.1.2 + postcss: 8.4.41 + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.1.3: {} + + dargs@8.1.0: {} + + dayjs@1.11.12: {} + + de-indent@1.0.2: {} + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-lazy-prop@2.0.0: {} + + defu@6.1.4: + optional: true + + delayed-stream@1.0.0: {} + + destr@2.0.3: + optional: true + + didyoumean@1.2.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dotenv@16.4.5: + optional: true + + eastasianwidth@0.2.0: {} + + echarts@5.5.1: + dependencies: + tslib: 2.3.0 + zrender: 5.6.0 + + electron-to-chromium@1.5.11: {} + + element-plus@2.8.0(vue@3.4.38(typescript@5.5.4)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 2.3.1(vue@3.4.38(typescript@5.5.4)) + '@floating-ui/dom': 1.6.10 + '@popperjs/core': '@sxzz/popperjs-es@2.11.7' + '@types/lodash': 4.17.7 + '@types/lodash-es': 4.17.12 + '@vueuse/core': 9.13.0(vue@3.4.38(typescript@5.5.4)) + async-validator: 4.2.5 + dayjs: 1.11.12 + escape-html: 1.0.3 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.4.38(typescript@5.5.4) + transitivePeerDependencies: + - '@vue/composition-api' + + emoji-regex@10.3.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@2.2.0: {} + + entities@4.5.0: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + es-module-lexer@0.4.1: {} + + esbuild@0.19.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.1.2: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: + optional: true + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@9.1.0(eslint@9.9.0(jiti@1.21.6)): + dependencies: + eslint: 9.9.0(jiti@1.21.6) + + eslint-define-config@2.1.0: {} + + eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@9.9.0(jiti@1.21.6)))(eslint@9.9.0(jiti@1.21.6))(prettier@3.3.3): + dependencies: + eslint: 9.9.0(jiti@1.21.6) + prettier: 3.3.3 + prettier-linter-helpers: 1.0.0 + synckit: 0.9.1 + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@9.9.0(jiti@1.21.6)) + + eslint-plugin-vue@9.27.0(eslint@9.9.0(jiti@1.21.6)): + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0(jiti@1.21.6)) + eslint: 9.9.0(jiti@1.21.6) + globals: 13.24.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.2 + semver: 7.6.3 + vue-eslint-parser: 9.4.3(eslint@9.9.0(jiti@1.21.6)) + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@8.0.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.0.0: {} + + eslint@9.9.0(jiti@1.21.6): + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0(jiti@1.21.6)) + '@eslint-community/regexpp': 4.11.0 + '@eslint/config-array': 0.17.1 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.9.0 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.6 + escape-string-regexp: 4.0.0 + eslint-scope: 8.0.2 + eslint-visitor-keys: 4.0.0 + espree: 10.1.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + optionalDependencies: + jiti: 1.21.6 + transitivePeerDependencies: + - supports-color + + espree@10.1.0: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 4.0.0 + + espree@9.6.1: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 + + esutils@2.0.3: {} + + eventemitter3@5.0.1: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.0.1: {} + + fastest-levenshtein@1.0.16: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-entry-cache@9.0.0: + dependencies: + flat-cache: 5.0.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + + flat-cache@5.0.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + + flatted@3.3.1: {} + + follow-redirects@1.15.6: {} + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fraction.js@4.3.7: {} + + framesync@6.1.2: + dependencies: + tslib: 2.4.0 + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.2.0: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-stream@8.0.1: {} + + get-tsconfig@4.7.6: + dependencies: + resolve-pkg-maps: 1.0.0 + + giget@1.2.3: + dependencies: + citty: 0.1.6 + consola: 3.2.3 + defu: 6.1.4 + node-fetch-native: 1.6.4 + nypm: 0.3.9 + ohash: 1.1.3 + pathe: 1.1.2 + tar: 6.2.1 + optional: true + + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globals@14.0.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@14.0.2: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.2 + ignore: 5.3.2 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 + optional: true + + globjoin@0.1.4: {} + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + graceful-fs@4.2.11: {} + + gradient-string@2.0.2: + dependencies: + chalk: 4.1.2 + tinygradient: 1.1.5 + + graphemer@1.4.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + hash-sum@2.0.0: + optional: true + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + hey-listen@1.0.8: {} + + highlight.js-async-webpack@1.0.4: {} + + highlight.js@9.18.5: {} + + hookable@5.5.3: + optional: true + + html-tags@3.3.1: {} + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + + human-signals@5.0.0: {} + + husky@9.1.4: {} + + ignore@5.3.2: {} + + immediate@3.0.6: {} + + immutable@4.3.7: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-from-string@0.0.4: + dependencies: + esbuild: 0.19.12 + import-meta-resolve: 3.1.1 + + import-meta-resolve@3.1.1: {} + + import-meta-resolve@4.1.0: {} + + imurmurhash@0.1.4: {} + + inherits@2.0.3: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.15.0: + dependencies: + hasown: 2.0.2 + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.2.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-object@5.0.0: {} + + is-reference@3.0.2: + dependencies: + '@types/estree': 1.0.5 + + is-stream@3.0.0: {} + + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.6: {} + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@2.5.2: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.0: + dependencies: + acorn: 8.12.1 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.6.3 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@6.0.3: {} + + klona@2.0.6: + optional: true + + knitwork@1.1.0: + optional: true + + known-css-properties@0.34.0: {} + + kolorist@1.8.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.1.1: + dependencies: + immediate: 3.0.6 + + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} + + lines-and-columns@1.2.4: {} + + lint-staged@15.2.9: + dependencies: + chalk: 5.3.0 + commander: 12.1.0 + debug: 4.3.6 + execa: 8.0.1 + lilconfig: 3.1.2 + listr2: 8.2.4 + micromatch: 4.0.7 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.5.0 + transitivePeerDependencies: + - supports-color + + listr2@8.2.4: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + local-pkg@0.5.0: + dependencies: + mlly: 1.7.1 + pkg-types: 1.1.3 + optional: true + + localforage@1.10.0: + dependencies: + lie: 3.1.1 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.17.21: {} + + lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21): + dependencies: + '@types/lodash-es': 4.17.12 + lodash: 4.17.21 + lodash-es: 4.17.21 + + lodash.camelcase@4.3.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.kebabcase@4.1.1: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash.startcase@4.4.0: {} + + lodash.truncate@4.4.2: {} + + lodash.uniq@4.5.0: {} + + lodash.upperfirst@4.3.1: {} + + lodash@4.17.21: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + mathml-tag-names@2.1.3: {} + + mavon-editor@2.7.7: + dependencies: + highlight.js: 9.18.5 + highlight.js-async-webpack: 1.0.4 + + mdn-data@2.0.14: {} + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + memoize-one@6.0.0: {} + + meow@12.1.1: {} + + meow@13.2.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + optional: true + + minipass@5.0.0: + optional: true + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + optional: true + + mitt@3.0.1: {} + + mkdirp@1.0.4: + optional: true + + mlly@1.7.1: + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.1.3 + ufo: 1.5.4 + + mri@1.2.0: + optional: true + + ms@2.1.2: {} + + muggle-string@0.4.1: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + node-fetch-native@1.6.4: + optional: true + + node-releases@2.0.18: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-url@6.1.0: {} + + normalize-wheel-es@1.2.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nypm@0.3.9: + dependencies: + citty: 0.1.6 + consola: 3.2.3 + execa: 8.0.1 + pathe: 1.1.2 + pkg-types: 1.1.3 + ufo: 1.5.4 + optional: true + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.2: {} + + ohash@1.1.3: + optional: true + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.1.1 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + package-json-from-dist@1.0.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.7 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@6.2.2: {} + + path-type@4.0.0: {} + + path-type@5.0.0: + optional: true + + path@0.12.7: + dependencies: + process: 0.11.10 + util: 0.10.4 + + pathe@1.1.2: {} + + perfect-debounce@1.0.0: + optional: true + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pidtree@0.6.0: {} + + pify@2.3.0: {} + + pinia@2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)): + dependencies: + '@vue/devtools-api': 6.6.3 + vue: 3.4.38(typescript@5.5.4) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + optionalDependencies: + typescript: 5.5.4 + + pinyin-pro@3.24.2: {} + + pirates@4.0.6: {} + + pkg-types@1.1.3: + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + + popmotion@11.0.5: + dependencies: + framesync: 6.1.2 + hey-listen: 1.0.8 + style-value-types: 5.1.2 + tslib: 2.4.0 + + postcss-calc@10.0.2(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-calc@8.2.4(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-colormin@5.3.1(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-colormin@7.0.2(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-convert-values@5.1.3(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-convert-values@7.0.3(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-discard-comments@5.1.2(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-discard-comments@7.0.2(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-discard-duplicates@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-discard-duplicates@7.0.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-discard-empty@5.1.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-discard-empty@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-discard-overridden@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-discard-overridden@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-html@1.7.0: + dependencies: + htmlparser2: 8.0.2 + js-tokens: 9.0.0 + postcss: 8.4.41 + postcss-safe-parser: 6.0.0(postcss@8.4.41) + + postcss-import@15.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-import@16.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.41): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.41 + + postcss-load-config@4.0.2(postcss@8.4.41): + dependencies: + lilconfig: 3.1.2 + yaml: 2.5.0 + optionalDependencies: + postcss: 8.4.41 + + postcss-media-query-parser@0.2.3: {} + + postcss-merge-longhand@5.1.7(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1(postcss@8.4.41) + + postcss-merge-longhand@7.0.3(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + stylehacks: 7.0.3(postcss@8.4.41) + + postcss-merge-rules@5.1.4(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-merge-rules@7.0.3(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-api: 3.0.0 + cssnano-utils: 5.0.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-minify-font-values@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-minify-font-values@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@5.1.1(postcss@8.4.41): + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@7.0.0(postcss@8.4.41): + dependencies: + colord: 2.9.3 + cssnano-utils: 5.0.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-minify-params@5.1.4(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + cssnano-utils: 3.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-minify-params@7.0.2(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + cssnano-utils: 5.0.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@5.2.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-minify-selectors@7.0.3(postcss@8.4.41): + dependencies: + cssesc: 3.0.0 + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-nested@6.2.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-normalize-charset@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-normalize-charset@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-normalize-display-values@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-display-values@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@5.1.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@5.1.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@5.1.1(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@7.0.2(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@5.1.0(postcss@8.4.41): + dependencies: + normalize-url: 6.1.0 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@5.1.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@5.1.3(postcss@8.4.41): + dependencies: + cssnano-utils: 3.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@7.0.1(postcss@8.4.41): + dependencies: + cssnano-utils: 5.0.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@5.1.2(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-api: 3.0.0 + postcss: 8.4.41 + + postcss-reduce-initial@7.0.2(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-api: 3.0.0 + postcss: 8.4.41 + + postcss-reduce-transforms@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-reduce-transforms@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@6.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-safe-parser@7.0.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-scss@4.0.9(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-sorting@8.0.2(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + + postcss-svgo@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + svgo: 2.8.0 + + postcss-svgo@7.0.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + svgo: 3.3.2 + + postcss-unique-selectors@5.1.1(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-unique-selectors@7.0.2(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.41: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@2.8.8: {} + + prettier@3.3.3: {} + + process@0.11.10: {} + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + qs@6.13.0: + dependencies: + side-channel: 1.0.6 + + queue-microtask@1.2.3: {} + + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.3 + optional: true + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + responsive-storage@2.2.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.0.4: {} + + rfdc@1.4.1: {} + + rimraf@5.0.10: + dependencies: + glob: 10.4.5 + + rollup-plugin-external-globals@0.10.0(rollup@4.21.0): + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.21.0) + estree-walker: 3.0.3 + is-reference: 3.0.2 + magic-string: 0.30.11 + rollup: 4.21.0 + + rollup-plugin-visualizer@5.12.0(rollup@4.21.0): + dependencies: + open: 8.4.2 + picomatch: 2.3.1 + source-map: 0.7.4 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.21.0 + + rollup@4.21.0: + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.21.0 + '@rollup/rollup-android-arm64': 4.21.0 + '@rollup/rollup-darwin-arm64': 4.21.0 + '@rollup/rollup-darwin-x64': 4.21.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.21.0 + '@rollup/rollup-linux-arm-musleabihf': 4.21.0 + '@rollup/rollup-linux-arm64-gnu': 4.21.0 + '@rollup/rollup-linux-arm64-musl': 4.21.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.21.0 + '@rollup/rollup-linux-riscv64-gnu': 4.21.0 + '@rollup/rollup-linux-s390x-gnu': 4.21.0 + '@rollup/rollup-linux-x64-gnu': 4.21.0 + '@rollup/rollup-linux-x64-musl': 4.21.0 + '@rollup/rollup-win32-arm64-msvc': 4.21.0 + '@rollup/rollup-win32-ia32-msvc': 4.21.0 + '@rollup/rollup-win32-x64-msvc': 4.21.0 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + sass@1.77.8: + dependencies: + chokidar: 3.6.0 + immutable: 4.3.7 + source-map-js: 1.2.0 + + scule@1.3.0: + optional: true + + semver@6.3.1: {} + + semver@7.6.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 + + signal-exit@4.1.0: {} + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + slash@3.0.0: {} + + slash@5.1.0: + optional: true + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + sortablejs@1.15.2: {} + + source-map-js@1.2.0: {} + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + sourcemap-codec@1.4.8: {} + + split2@4.2.0: {} + + stable@0.1.8: {} + + std-env@3.7.0: + optional: true + + string-argv@0.3.2: {} + + string-hash@1.1.3: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.3.0 + get-east-asian-width: 1.2.0 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + strip-final-newline@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strip-literal@2.1.0: + dependencies: + js-tokens: 9.0.0 + optional: true + + style-value-types@5.1.2: + dependencies: + hey-listen: 1.0.8 + tslib: 2.4.0 + + stylehacks@5.1.1(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + stylehacks@7.0.3(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + stylelint-config-html@1.1.0(postcss-html@1.7.0)(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + postcss-html: 1.7.0 + stylelint: 16.8.2(typescript@5.5.4) + + stylelint-config-recess-order@5.0.1(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + stylelint: 16.8.2(typescript@5.5.4) + stylelint-order: 6.0.4(stylelint@16.8.2(typescript@5.5.4)) + + stylelint-config-recommended-scss@14.1.0(postcss@8.4.41)(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + postcss-scss: 4.0.9(postcss@8.4.41) + stylelint: 16.8.2(typescript@5.5.4) + stylelint-config-recommended: 14.0.1(stylelint@16.8.2(typescript@5.5.4)) + stylelint-scss: 6.5.0(stylelint@16.8.2(typescript@5.5.4)) + optionalDependencies: + postcss: 8.4.41 + + stylelint-config-recommended-vue@1.5.0(postcss-html@1.7.0)(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + postcss-html: 1.7.0 + semver: 7.6.3 + stylelint: 16.8.2(typescript@5.5.4) + stylelint-config-html: 1.1.0(postcss-html@1.7.0)(stylelint@16.8.2(typescript@5.5.4)) + stylelint-config-recommended: 14.0.1(stylelint@16.8.2(typescript@5.5.4)) + + stylelint-config-recommended@14.0.1(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + stylelint: 16.8.2(typescript@5.5.4) + + stylelint-config-standard-scss@13.1.0(postcss@8.4.41)(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + stylelint: 16.8.2(typescript@5.5.4) + stylelint-config-recommended-scss: 14.1.0(postcss@8.4.41)(stylelint@16.8.2(typescript@5.5.4)) + stylelint-config-standard: 36.0.1(stylelint@16.8.2(typescript@5.5.4)) + optionalDependencies: + postcss: 8.4.41 + + stylelint-config-standard@36.0.1(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + stylelint: 16.8.2(typescript@5.5.4) + stylelint-config-recommended: 14.0.1(stylelint@16.8.2(typescript@5.5.4)) + + stylelint-order@6.0.4(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + postcss: 8.4.41 + postcss-sorting: 8.0.2(postcss@8.4.41) + stylelint: 16.8.2(typescript@5.5.4) + + stylelint-prettier@5.0.2(prettier@3.3.3)(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + prettier: 3.3.3 + prettier-linter-helpers: 1.0.0 + stylelint: 16.8.2(typescript@5.5.4) + + stylelint-scss@6.5.0(stylelint@16.8.2(typescript@5.5.4)): + dependencies: + css-tree: 2.3.1 + is-plain-object: 5.0.0 + known-css-properties: 0.34.0 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + stylelint: 16.8.2(typescript@5.5.4) + + stylelint@16.8.2(typescript@5.5.4): + dependencies: + '@csstools/css-parser-algorithms': 3.0.1(@csstools/css-tokenizer@3.0.1) + '@csstools/css-tokenizer': 3.0.1 + '@csstools/media-query-list-parser': 3.0.1(@csstools/css-parser-algorithms@3.0.1(@csstools/css-tokenizer@3.0.1))(@csstools/css-tokenizer@3.0.1) + '@csstools/selector-specificity': 4.0.0(postcss-selector-parser@6.1.2) + '@dual-bundle/import-meta-resolve': 4.1.0 + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 9.0.0(typescript@5.5.4) + css-functions-list: 3.2.2 + css-tree: 2.3.1 + debug: 4.3.6 + fast-glob: 3.3.2 + fastest-levenshtein: 1.0.16 + file-entry-cache: 9.0.0 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.3.1 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.34.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 + micromatch: 4.0.7 + normalize-path: 3.0.0 + picocolors: 1.0.1 + postcss: 8.4.41 + postcss-resolve-nested-selector: 0.1.6 + postcss-safe-parser: 7.0.0(postcss@8.4.41) + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + strip-ansi: 7.1.0 + supports-hyperlinks: 3.0.0 + svg-tags: 1.0.0 + table: 6.8.2 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@3.0.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-tags@1.0.0: {} + + svgo@2.8.0: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.1 + stable: 0.1.8 + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.0.1 + + synckit@0.9.1: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.6.3 + + table@6.8.2: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tailwindcss@3.4.10: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.7 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.1 + postcss: 8.4.41 + postcss-import: 15.1.0(postcss@8.4.41) + postcss-js: 4.0.1(postcss@8.4.41) + postcss-load-config: 4.0.2(postcss@8.4.41) + postcss-nested: 6.2.0(postcss@8.4.41) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + optional: true + + text-extensions@2.4.0: {} + + text-table@0.2.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + through@2.3.8: {} + + tiny-invariant@1.3.3: {} + + tinycolor2@1.6.0: {} + + tinygradient@1.1.5: + dependencies: + '@types/tinycolor2': 1.4.6 + tinycolor2: 1.6.0 + + tippy.js@6.3.7: + dependencies: + '@popperjs/core': 2.11.8 + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@1.3.0(typescript@5.5.4): + dependencies: + typescript: 5.5.4 + + ts-interface-checker@0.1.13: {} + + tslib@2.3.0: {} + + tslib@2.4.0: {} + + tslib@2.6.3: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@2.19.0: {} + + typescript@5.5.4: {} + + ufo@1.5.4: {} + + uncrypto@0.1.3: + optional: true + + unctx@2.3.1: + dependencies: + acorn: 8.12.1 + estree-walker: 3.0.3 + magic-string: 0.30.11 + unplugin: 1.12.2 + optional: true + + undici-types@6.19.8: {} + + unicorn-magic@0.1.0: {} + + unimport@3.10.0(rollup@4.21.0): + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.21.0) + acorn: 8.12.1 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + fast-glob: 3.3.2 + local-pkg: 0.5.0 + magic-string: 0.30.11 + mlly: 1.7.1 + pathe: 1.1.2 + pkg-types: 1.1.3 + scule: 1.3.0 + strip-literal: 2.1.0 + unplugin: 1.12.2 + transitivePeerDependencies: + - rollup + optional: true + + universalify@2.0.1: {} + + unplugin@1.12.2: + dependencies: + acorn: 8.12.1 + chokidar: 3.6.0 + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.6.2 + + untyped@1.4.2: + dependencies: + '@babel/core': 7.25.2 + '@babel/standalone': 7.25.3 + '@babel/types': 7.25.2 + defu: 6.1.4 + jiti: 1.21.6 + mri: 1.2.0 + scule: 1.3.0 + transitivePeerDependencies: + - supports-color + optional: true + + update-browserslist-db@1.1.0(browserslist@4.23.3): + dependencies: + browserslist: 4.23.3 + escalade: 3.1.2 + picocolors: 1.0.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + util@0.10.4: + dependencies: + inherits: 2.0.3 + + uuid@8.3.2: {} + + vite-plugin-cdn-import@1.0.1(rollup@4.21.0)(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)): + dependencies: + rollup-plugin-external-globals: 0.10.0(rollup@4.21.0) + vite-plugin-externals: 0.6.2(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)) + transitivePeerDependencies: + - rollup + - vite + + vite-plugin-checker@0.7.2(eslint@9.9.0(jiti@1.21.6))(optionator@0.9.4)(stylelint@16.8.2(typescript@5.5.4))(typescript@5.5.4)(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8))(vue-tsc@2.0.29(typescript@5.5.4)): + dependencies: + '@babel/code-frame': 7.24.7 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + chokidar: 3.6.0 + commander: 8.3.0 + fast-glob: 3.3.2 + fs-extra: 11.2.0 + npm-run-path: 4.0.1 + strip-ansi: 6.0.1 + tiny-invariant: 1.3.3 + vite: 5.4.1(@types/node@20.16.1)(sass@1.77.8) + vscode-languageclient: 7.0.0 + vscode-languageserver: 7.0.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + optionalDependencies: + eslint: 9.9.0(jiti@1.21.6) + optionator: 0.9.4 + stylelint: 16.8.2(typescript@5.5.4) + typescript: 5.5.4 + vue-tsc: 2.0.29(typescript@5.5.4) + + vite-plugin-compression@0.5.1(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)): + dependencies: + chalk: 4.1.2 + debug: 4.3.6 + fs-extra: 10.1.0 + vite: 5.4.1(@types/node@20.16.1)(sass@1.77.8) + transitivePeerDependencies: + - supports-color + + vite-plugin-externals@0.6.2(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)): + dependencies: + acorn: 8.12.1 + es-module-lexer: 0.4.1 + fs-extra: 10.1.0 + magic-string: 0.25.9 + vite: 5.4.1(@types/node@20.16.1)(sass@1.77.8) + + vite-plugin-fake-server@2.1.1: + dependencies: + bundle-import: 0.0.1 + chokidar: 3.6.0 + fast-glob: 3.3.2 + path-to-regexp: 6.2.2 + picocolors: 1.0.1 + + vite-plugin-remove-console@2.2.0: {} + + vite-plugin-router-warn@1.0.0: {} + + vite-plugin-vue-inspector@5.1.3(vite@5.4.1(@types/node@20.16.1)(sass@1.77.8)): + dependencies: + '@babel/core': 7.25.2 + '@babel/plugin-proposal-decorators': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.2) + '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2) + '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.25.2) + '@vue/compiler-dom': 3.4.38 + kolorist: 1.8.0 + magic-string: 0.30.11 + vite: 5.4.1(@types/node@20.16.1)(sass@1.77.8) + transitivePeerDependencies: + - supports-color + + vite-svg-loader@5.1.0(vue@3.4.38(typescript@5.5.4)): + dependencies: + svgo: 3.3.2 + vue: 3.4.38(typescript@5.5.4) + + vite@5.4.1(@types/node@20.16.1)(sass@1.77.8): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.41 + rollup: 4.21.0 + optionalDependencies: + '@types/node': 20.16.1 + fsevents: 2.3.3 + sass: 1.77.8 + + vscode-jsonrpc@6.0.0: {} + + vscode-languageclient@7.0.0: + dependencies: + minimatch: 3.1.2 + semver: 7.6.3 + vscode-languageserver-protocol: 3.16.0 + + vscode-languageserver-protocol@3.16.0: + dependencies: + vscode-jsonrpc: 6.0.0 + vscode-languageserver-types: 3.16.0 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.16.0: {} + + vscode-languageserver@7.0.0: + dependencies: + vscode-languageserver-protocol: 3.16.0 + + vscode-uri@3.0.8: {} + + vue-demi@0.14.10(vue@3.4.38(typescript@5.5.4)): + dependencies: + vue: 3.4.38(typescript@5.5.4) + + vue-eslint-parser@9.4.3(eslint@9.9.0(jiti@1.21.6)): + dependencies: + debug: 4.3.6 + eslint: 9.9.0(jiti@1.21.6) + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + lodash: 4.17.21 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4)): + dependencies: + '@intlify/core-base': 9.14.0 + '@intlify/shared': 9.14.0 + '@vue/devtools-api': 6.6.3 + vue: 3.4.38(typescript@5.5.4) + + vue-router@4.4.3(vue@3.4.38(typescript@5.5.4)): + dependencies: + '@vue/devtools-api': 6.6.3 + vue: 3.4.38(typescript@5.5.4) + + vue-tippy@6.4.4(vue@3.4.38(typescript@5.5.4)): + dependencies: + tippy.js: 6.3.7 + vue: 3.4.38(typescript@5.5.4) + + vue-tsc@2.0.29(typescript@5.5.4): + dependencies: + '@volar/typescript': 2.4.0 + '@vue/language-core': 2.0.29(typescript@5.5.4) + semver: 7.6.3 + typescript: 5.5.4 + + vue-types@5.1.3(vue@3.4.38(typescript@5.5.4)): + dependencies: + is-plain-object: 5.0.0 + optionalDependencies: + vue: 3.4.38(typescript@5.5.4) + + vue@3.4.38(typescript@5.5.4): + dependencies: + '@vue/compiler-dom': 3.4.38 + '@vue/compiler-sfc': 3.4.38 + '@vue/runtime-dom': 3.4.38 + '@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.5.4)) + '@vue/shared': 3.4.38 + optionalDependencies: + typescript: 5.5.4 + + webpack-sources@3.2.3: {} + + webpack-virtual-modules@0.6.2: {} + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + widest-line@4.0.1: + dependencies: + string-width: 5.1.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + xml-name-validator@4.0.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: + optional: true + + yaml-eslint-parser@1.2.3: + dependencies: + eslint-visitor-keys: 3.4.3 + lodash: 4.17.21 + yaml: 2.5.0 + + yaml@1.10.2: {} + + yaml@2.5.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + yocto-queue@1.1.1: {} + + zrender@5.6.0: + dependencies: + tslib: 2.3.0 diff --git a/sop-website/sop-website-frontend/postcss.config.js b/sop-website/sop-website-frontend/postcss.config.js new file mode 100644 index 00000000..86239486 --- /dev/null +++ b/sop-website/sop-website-frontend/postcss.config.js @@ -0,0 +1,12 @@ +// @ts-check + +/** @type {import('postcss-load-config').Config} */ +export default { + plugins: { + "postcss-import": {}, + "tailwindcss/nesting": {}, + tailwindcss: {}, + autoprefixer: {}, + ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}) + } +}; diff --git a/sop-website/sop-website-frontend/public/favicon.ico b/sop-website/sop-website-frontend/public/favicon.ico new file mode 100644 index 00000000..bef93d4b Binary files /dev/null and b/sop-website/sop-website-frontend/public/favicon.ico differ diff --git a/sop-website/sop-website-frontend/public/logo.svg b/sop-website/sop-website-frontend/public/logo.svg new file mode 100644 index 00000000..a63d2b1a --- /dev/null +++ b/sop-website/sop-website-frontend/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/public/platform-config.json b/sop-website/sop-website-frontend/public/platform-config.json new file mode 100644 index 00000000..6dfe2028 --- /dev/null +++ b/sop-website/sop-website-frontend/public/platform-config.json @@ -0,0 +1,27 @@ +{ + "Version": "5.8.0", + "Title": "XX开放平台", + "FixedHeader": true, + "HiddenSideBar": false, + "MultiTagsCache": false, + "KeepAlive": true, + "Locale": "zh", + "Layout": "horizontal", + "Theme": "light", + "DarkMode": false, + "OverallStyle": "light", + "Grey": false, + "Weak": false, + "HideTabs": true, + "HideFooter": true, + "Stretch": false, + "SidebarStatus": true, + "EpThemeColor": "#409EFF", + "ShowLogo": true, + "ShowModel": "smart", + "MenuArrowIconNoTransition": false, + "CachingAsyncRoutes": false, + "TooltipEffect": "light", + "ResponsiveStorageNameSpace": "responsive-", + "MenuSearchHistory": 6 +} diff --git a/sop-website/sop-website-frontend/src/App.vue b/sop-website/sop-website-frontend/src/App.vue new file mode 100644 index 00000000..9204c7cb --- /dev/null +++ b/sop-website/sop-website-frontend/src/App.vue @@ -0,0 +1,27 @@ + + + diff --git a/sop-website/sop-website-frontend/src/api/routes.ts b/sop-website/sop-website-frontend/src/api/routes.ts new file mode 100644 index 00000000..501ea3c7 --- /dev/null +++ b/sop-website/sop-website-frontend/src/api/routes.ts @@ -0,0 +1,10 @@ +import { http } from "@/utils/http"; + +type Result = { + success: boolean; + data: Array; +}; + +export const getAsyncRoutes = () => { + return http.request("get", "/get-async-routes"); +}; diff --git a/sop-website/sop-website-frontend/src/api/user.ts b/sop-website/sop-website-frontend/src/api/user.ts new file mode 100644 index 00000000..87184b50 --- /dev/null +++ b/sop-website/sop-website-frontend/src/api/user.ts @@ -0,0 +1,45 @@ +import { http } from "@/utils/http"; + +export type UserResult = { + success: boolean; + data: { + /** 头像 */ + avatar: string; + /** 用户名 */ + username: string; + /** 昵称 */ + nickname: string; + /** 当前登录用户的角色 */ + roles: Array; + /** 按钮级别权限 */ + permissions: Array; + /** `token` */ + accessToken: string; + /** 用于调用刷新`accessToken`的接口时所需的`token` */ + refreshToken: string; + /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */ + expires: Date; + }; +}; + +export type RefreshTokenResult = { + success: boolean; + data: { + /** `token` */ + accessToken: string; + /** 用于调用刷新`accessToken`的接口时所需的`token` */ + refreshToken: string; + /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */ + expires: Date; + }; +}; + +/** 登录 */ +export const getLogin = (data?: object) => { + return http.request("post", "/login", { data }); +}; + +/** 刷新`token` */ +export const refreshTokenApi = (data?: object) => { + return http.request("post", "/refresh-token", { data }); +}; diff --git a/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.css b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.css new file mode 100644 index 00000000..9a153df3 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.css @@ -0,0 +1,27 @@ +@font-face { + font-family: "iconfont"; /* Project id 2208059 */ + src: + url("iconfont.woff2?t=1671895108120") format("woff2"), + url("iconfont.woff?t=1671895108120") format("woff"), + url("iconfont.ttf?t=1671895108120") format("truetype"); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.pure-iconfont-tabs:before { + content: "\e63e"; +} + +.pure-iconfont-logo:before { + content: "\e620"; +} + +.pure-iconfont-new:before { + content: "\e615"; +} diff --git a/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.js b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.js new file mode 100644 index 00000000..64d8bd84 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.js @@ -0,0 +1,69 @@ +(window._iconfont_svg_string_2208059 = + ''), + (function (e) { + var t = (t = document.getElementsByTagName("script"))[t.length - 1], + c = t.getAttribute("data-injectcss"), + t = t.getAttribute("data-disable-injectsvg"); + if (!t) { + var n, + l, + i, + o, + a, + h = function (t, c) { + c.parentNode.insertBefore(t, c); + }; + if (c && !e.__iconfont__svg__cssinject__) { + e.__iconfont__svg__cssinject__ = !0; + try { + document.write( + "" + ); + } catch (t) { + console && console.log(t); + } + } + (n = function () { + var t, + c = document.createElement("div"); + (c.innerHTML = e._iconfont_svg_string_2208059), + (c = c.getElementsByTagName("svg")[0]) && + (c.setAttribute("aria-hidden", "true"), + (c.style.position = "absolute"), + (c.style.width = 0), + (c.style.height = 0), + (c.style.overflow = "hidden"), + (c = c), + (t = document.body).firstChild + ? h(c, t.firstChild) + : t.appendChild(c)); + }), + document.addEventListener + ? ~["complete", "loaded", "interactive"].indexOf(document.readyState) + ? setTimeout(n, 0) + : ((l = function () { + document.removeEventListener("DOMContentLoaded", l, !1), n(); + }), + document.addEventListener("DOMContentLoaded", l, !1)) + : document.attachEvent && + ((i = n), + (o = e.document), + (a = !1), + v(), + (o.onreadystatechange = function () { + "complete" == o.readyState && + ((o.onreadystatechange = null), d()); + })); + } + function d() { + a || ((a = !0), i()); + } + function v() { + try { + o.documentElement.doScroll("left"); + } catch (t) { + return void setTimeout(v, 50); + } + d(); + } + })(window); diff --git a/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.json b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.json new file mode 100644 index 00000000..cec48060 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.json @@ -0,0 +1,30 @@ +{ + "id": "2208059", + "name": "pure-admin", + "font_family": "iconfont", + "css_prefix_text": "pure-iconfont-", + "description": "pure-admin-iconfont", + "glyphs": [ + { + "icon_id": "20594647", + "name": "Tabs", + "font_class": "tabs", + "unicode": "e63e", + "unicode_decimal": 58942 + }, + { + "icon_id": "22129506", + "name": "PureLogo", + "font_class": "logo", + "unicode": "e620", + "unicode_decimal": 58912 + }, + { + "icon_id": "7795615", + "name": "New", + "font_class": "new", + "unicode": "e615", + "unicode_decimal": 58901 + } + ] +} diff --git a/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.ttf b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.ttf new file mode 100644 index 00000000..82efcf8f Binary files /dev/null and b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.ttf differ diff --git a/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.woff b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.woff new file mode 100644 index 00000000..0fdaa0a4 Binary files /dev/null and b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.woff differ diff --git a/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.woff2 b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.woff2 new file mode 100644 index 00000000..e957d747 Binary files /dev/null and b/sop-website/sop-website-frontend/src/assets/iconfont/iconfont.woff2 differ diff --git a/sop-website/sop-website-frontend/src/assets/login/avatar.svg b/sop-website/sop-website-frontend/src/assets/login/avatar.svg new file mode 100644 index 00000000..a63d2b1a --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/login/avatar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/login/bg.png b/sop-website/sop-website-frontend/src/assets/login/bg.png new file mode 100644 index 00000000..8cdd3001 Binary files /dev/null and b/sop-website/sop-website-frontend/src/assets/login/bg.png differ diff --git a/sop-website/sop-website-frontend/src/assets/login/illustration.svg b/sop-website/sop-website-frontend/src/assets/login/illustration.svg new file mode 100644 index 00000000..b58ffd08 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/login/illustration.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/status/403.svg b/sop-website/sop-website-frontend/src/assets/status/403.svg new file mode 100644 index 00000000..ba3ce293 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/status/403.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/status/404.svg b/sop-website/sop-website-frontend/src/assets/status/404.svg new file mode 100644 index 00000000..aacb7402 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/status/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/status/500.svg b/sop-website/sop-website-frontend/src/assets/status/500.svg new file mode 100644 index 00000000..ea23a378 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/status/500.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/back_top.svg b/sop-website/sop-website-frontend/src/assets/svg/back_top.svg new file mode 100644 index 00000000..f8e6aa02 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/back_top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/dark.svg b/sop-website/sop-website-frontend/src/assets/svg/dark.svg new file mode 100644 index 00000000..b5c4d2d5 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/day.svg b/sop-website/sop-website-frontend/src/assets/svg/day.svg new file mode 100644 index 00000000..b7600345 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/day.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/enter_outlined.svg b/sop-website/sop-website-frontend/src/assets/svg/enter_outlined.svg new file mode 100644 index 00000000..45e0bafe --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/enter_outlined.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/exit_screen.svg b/sop-website/sop-website-frontend/src/assets/svg/exit_screen.svg new file mode 100644 index 00000000..007c0b63 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/exit_screen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/full_screen.svg b/sop-website/sop-website-frontend/src/assets/svg/full_screen.svg new file mode 100644 index 00000000..fff93a5d --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/full_screen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/globalization.svg b/sop-website/sop-website-frontend/src/assets/svg/globalization.svg new file mode 100644 index 00000000..5f6bce6b --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/globalization.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/keyboard_esc.svg b/sop-website/sop-website-frontend/src/assets/svg/keyboard_esc.svg new file mode 100644 index 00000000..bd671654 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/keyboard_esc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/svg/system.svg b/sop-website/sop-website-frontend/src/assets/svg/system.svg new file mode 100644 index 00000000..9ad39a56 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/svg/system.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/table-bar/collapse.svg b/sop-website/sop-website-frontend/src/assets/table-bar/collapse.svg new file mode 100644 index 00000000..0823ae63 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/table-bar/collapse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/table-bar/drag.svg b/sop-website/sop-website-frontend/src/assets/table-bar/drag.svg new file mode 100644 index 00000000..8ac32a7b --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/table-bar/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/table-bar/expand.svg b/sop-website/sop-website-frontend/src/assets/table-bar/expand.svg new file mode 100644 index 00000000..bb41c350 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/table-bar/expand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/table-bar/refresh.svg b/sop-website/sop-website-frontend/src/assets/table-bar/refresh.svg new file mode 100644 index 00000000..140288cd --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/table-bar/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/table-bar/settings.svg b/sop-website/sop-website-frontend/src/assets/table-bar/settings.svg new file mode 100644 index 00000000..4ecd0779 --- /dev/null +++ b/sop-website/sop-website-frontend/src/assets/table-bar/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-frontend/src/assets/user.jpg b/sop-website/sop-website-frontend/src/assets/user.jpg new file mode 100644 index 00000000..a2973ace Binary files /dev/null and b/sop-website/sop-website-frontend/src/assets/user.jpg differ diff --git a/sop-website/sop-website-frontend/src/components/ApiParamTable/index.ts b/sop-website/sop-website-frontend/src/components/ApiParamTable/index.ts new file mode 100644 index 00000000..5d6c7f49 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ApiParamTable/index.ts @@ -0,0 +1,12 @@ +import apiParamTable from "@/components/ApiParamTable/index.vue"; +import { withInstall } from "@pureadmin/utils"; + +export function hasNoParentAndChildren(row) { + const children = row.children; + const noChildren = !children || children.length === 0; + return !row.parentId && noChildren; +} + +const ApiParamTable = withInstall(apiParamTable); + +export { ApiParamTable }; diff --git a/sop-website/sop-website-frontend/src/components/ApiParamTable/index.vue b/sop-website/sop-website-frontend/src/components/ApiParamTable/index.vue new file mode 100644 index 00000000..fb132cf6 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ApiParamTable/index.vue @@ -0,0 +1,75 @@ + + diff --git a/sop-website/sop-website-frontend/src/components/ReAuth/index.ts b/sop-website/sop-website-frontend/src/components/ReAuth/index.ts new file mode 100644 index 00000000..975ed2ca --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReAuth/index.ts @@ -0,0 +1,5 @@ +import auth from "./src/auth"; + +const Auth = auth; + +export { Auth }; diff --git a/sop-website/sop-website-frontend/src/components/ReAuth/src/auth.tsx b/sop-website/sop-website-frontend/src/components/ReAuth/src/auth.tsx new file mode 100644 index 00000000..d2cf9b35 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReAuth/src/auth.tsx @@ -0,0 +1,20 @@ +import { defineComponent, Fragment } from "vue"; +import { hasAuth } from "@/router/utils"; + +export default defineComponent({ + name: "Auth", + props: { + value: { + type: undefined, + default: [] + } + }, + setup(props, { slots }) { + return () => { + if (!slots) return null; + return hasAuth(props.value) ? ( + {slots.default?.()} + ) : null; + }; + } +}); diff --git a/sop-website/sop-website-frontend/src/components/ReCol/index.ts b/sop-website/sop-website-frontend/src/components/ReCol/index.ts new file mode 100644 index 00000000..7a6c9374 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReCol/index.ts @@ -0,0 +1,29 @@ +import { ElCol } from "element-plus"; +import { h, defineComponent } from "vue"; + +// 封装element-plus的el-col组件 +export default defineComponent({ + name: "ReCol", + props: { + value: { + type: Number, + default: 24 + } + }, + render() { + const attrs = this.$attrs; + const val = this.value; + return h( + ElCol, + { + xs: val, + sm: val, + md: val, + lg: val, + xl: val, + ...attrs + }, + { default: () => this.$slots.default() } + ); + } +}); diff --git a/sop-website/sop-website-frontend/src/components/ReDialog/index.ts b/sop-website/sop-website-frontend/src/components/ReDialog/index.ts new file mode 100644 index 00000000..b471764b --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReDialog/index.ts @@ -0,0 +1,69 @@ +import { ref } from "vue"; +import reDialog from "./index.vue"; +import { useTimeoutFn } from "@vueuse/core"; +import { withInstall } from "@pureadmin/utils"; +import type { + EventType, + ArgsType, + DialogProps, + ButtonProps, + DialogOptions +} from "./type"; + +const dialogStore = ref>([]); + +/** 打开弹框 */ +const addDialog = (options: DialogOptions) => { + const open = () => + dialogStore.value.push(Object.assign(options, { visible: true })); + if (options?.openDelay) { + useTimeoutFn(() => { + open(); + }, options.openDelay); + } else { + open(); + } +}; + +/** 关闭弹框 */ +const closeDialog = (options: DialogOptions, index: number, args?: any) => { + dialogStore.value[index].visible = false; + options.closeCallBack && options.closeCallBack({ options, index, args }); + + const closeDelay = options?.closeDelay ?? 200; + useTimeoutFn(() => { + dialogStore.value.splice(index, 1); + }, closeDelay); +}; + +/** + * @description 更改弹框自身属性值 + * @param value 属性值 + * @param key 属性,默认`title` + * @param index 弹框索引(默认`0`,代表只有一个弹框,对于嵌套弹框要改哪个弹框的属性值就把该弹框索引赋给`index`) + */ +const updateDialog = (value: any, key = "title", index = 0) => { + dialogStore.value[index][key] = value; +}; + +/** 关闭所有弹框 */ +const closeAllDialog = () => { + dialogStore.value = []; +}; + +/** 千万别忘了在下面这三处引入并注册下,放心注册,不使用`addDialog`调用就不会被挂载 + * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L4 + * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L12 + * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L22 + */ +const ReDialog = withInstall(reDialog); + +export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions }; +export { + ReDialog, + dialogStore, + addDialog, + closeDialog, + updateDialog, + closeAllDialog +}; diff --git a/sop-website/sop-website-frontend/src/components/ReDialog/index.vue b/sop-website/sop-website-frontend/src/components/ReDialog/index.vue new file mode 100644 index 00000000..23a0106e --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReDialog/index.vue @@ -0,0 +1,206 @@ + + + diff --git a/sop-website/sop-website-frontend/src/components/ReDialog/type.ts b/sop-website/sop-website-frontend/src/components/ReDialog/type.ts new file mode 100644 index 00000000..7efbe201 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReDialog/type.ts @@ -0,0 +1,275 @@ +import type { CSSProperties, VNode, Component } from "vue"; + +type DoneFn = (cancel?: boolean) => void; +type EventType = + | "open" + | "close" + | "openAutoFocus" + | "closeAutoFocus" + | "fullscreenCallBack"; +type ArgsType = { + /** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */ + command: "cancel" | "sure" | "close"; +}; +type ButtonType = + | "primary" + | "success" + | "warning" + | "danger" + | "info" + | "text"; + +/** https://element-plus.org/zh-CN/component/dialog.html#attributes */ +type DialogProps = { + /** `Dialog` 的显示与隐藏 */ + visible?: boolean; + /** `Dialog` 的标题 */ + title?: string; + /** `Dialog` 的宽度,默认 `50%` */ + width?: string | number; + /** 是否为全屏 `Dialog`(会一直处于全屏状态,除非弹框关闭),默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */ + fullscreen?: boolean; + /** 是否显示全屏操作图标,默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */ + fullscreenIcon?: boolean; + /** `Dialog CSS` 中的 `margin-top` 值,默认 `15vh` */ + top?: string; + /** 是否需要遮罩层,默认 `true` */ + modal?: boolean; + /** `Dialog` 自身是否插入至 `body` 元素上。嵌套的 `Dialog` 必须指定该属性并赋值为 `true`,默认 `false` */ + appendToBody?: boolean; + /** 是否在 `Dialog` 出现时将 `body` 滚动锁定,默认 `true` */ + lockScroll?: boolean; + /** `Dialog` 的自定义类名 */ + class?: string; + /** `Dialog` 的自定义样式 */ + style?: CSSProperties; + /** `Dialog` 打开的延时时间,单位毫秒,默认 `0` */ + openDelay?: number; + /** `Dialog` 关闭的延时时间,单位毫秒,默认 `0` */ + closeDelay?: number; + /** 是否可以通过点击 `modal` 关闭 `Dialog`,默认 `true` */ + closeOnClickModal?: boolean; + /** 是否可以通过按下 `ESC` 关闭 `Dialog`,默认 `true` */ + closeOnPressEscape?: boolean; + /** 是否显示关闭按钮,默认 `true` */ + showClose?: boolean; + /** 关闭前的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */ + beforeClose?: (done: DoneFn) => void; + /** 为 `Dialog` 启用可拖拽功能,默认 `false` */ + draggable?: boolean; + /** 是否让 `Dialog` 的 `header` 和 `footer` 部分居中排列,默认 `false` */ + center?: boolean; + /** 是否水平垂直对齐对话框,默认 `false` */ + alignCenter?: boolean; + /** 当关闭 `Dialog` 时,销毁其中的元素,默认 `false` */ + destroyOnClose?: boolean; +}; + +//element-plus.org/zh-CN/component/popconfirm.html#attributes +type Popconfirm = { + /** 标题 */ + title?: string; + /** 确定按钮文字 */ + confirmButtonText?: string; + /** 取消按钮文字 */ + cancelButtonText?: string; + /** 确定按钮类型,默认 `primary` */ + confirmButtonType?: ButtonType; + /** 取消按钮类型,默认 `text` */ + cancelButtonType?: ButtonType; + /** 自定义图标,默认 `QuestionFilled` */ + icon?: string | Component; + /** `Icon` 颜色,默认 `#f90` */ + iconColor?: string; + /** 是否隐藏 `Icon`,默认 `false` */ + hideIcon?: boolean; + /** 关闭时的延迟,默认 `200` */ + hideAfter?: number; + /** 是否将 `popover` 的下拉列表插入至 `body` 元素,默认 `true` */ + teleported?: boolean; + /** 当 `popover` 组件长时间不触发且 `persistent` 属性设置为 `false` 时, `popover` 将会被删除,默认 `false` */ + persistent?: boolean; + /** 弹层宽度,最小宽度 `150px`,默认 `150` */ + width?: string | number; +}; + +type BtnClickDialog = { + options?: DialogOptions; + index?: number; +}; +type BtnClickButton = { + btn?: ButtonProps; + index?: number; +}; +/** https://element-plus.org/zh-CN/component/button.html#button-attributes */ +type ButtonProps = { + /** 按钮文字 */ + label: string; + /** 按钮尺寸 */ + size?: "large" | "default" | "small"; + /** 按钮类型 */ + type?: "primary" | "success" | "warning" | "danger" | "info"; + /** 是否为朴素按钮,默认 `false` */ + plain?: boolean; + /** 是否为文字按钮,默认 `false` */ + text?: boolean; + /** 是否显示文字按钮背景颜色,默认 `false` */ + bg?: boolean; + /** 是否为链接按钮,默认 `false` */ + link?: boolean; + /** 是否为圆角按钮,默认 `false` */ + round?: boolean; + /** 是否为圆形按钮,默认 `false` */ + circle?: boolean; + /** 确定按钮的 `Popconfirm` 气泡确认框相关配置 */ + popconfirm?: Popconfirm; + /** 是否为加载中状态,默认 `false` */ + loading?: boolean; + /** 自定义加载中状态图标组件 */ + loadingIcon?: string | Component; + /** 按钮是否为禁用状态,默认 `false` */ + disabled?: boolean; + /** 图标组件 */ + icon?: string | Component; + /** 是否开启原生 `autofocus` 属性,默认 `false` */ + autofocus?: boolean; + /** 原生 `type` 属性,默认 `button` */ + nativeType?: "button" | "submit" | "reset"; + /** 自动在两个中文字符之间插入空格 */ + autoInsertSpace?: boolean; + /** 自定义按钮颜色, 并自动计算 `hover` 和 `active` 触发后的颜色 */ + color?: string; + /** `dark` 模式, 意味着自动设置 `color` 为 `dark` 模式的颜色,默认 `false` */ + dark?: boolean; + /** 自定义元素标签 */ + tag?: string | Component; + /** 点击按钮后触发的回调 */ + btnClick?: ({ + dialog, + button + }: { + /** 当前 `Dialog` 信息 */ + dialog: BtnClickDialog; + /** 当前 `button` 信息 */ + button: BtnClickButton; + }) => void; +}; + +interface DialogOptions extends DialogProps { + /** 内容区组件的 `props`,可通过 `defineProps` 接收 */ + props?: any; + /** 是否隐藏 `Dialog` 按钮操作区的内容 */ + hideFooter?: boolean; + /** 确定按钮的 `Popconfirm` 气泡确认框相关配置 */ + popconfirm?: Popconfirm; + /** 点击确定按钮后是否开启 `loading` 加载动画 */ + sureBtnLoading?: boolean; + /** + * @description 自定义对话框标题的内容渲染器 + * @see {@link https://element-plus.org/zh-CN/component/dialog.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%B4%E9%83%A8} + */ + headerRenderer?: ({ + close, + titleId, + titleClass + }: { + close: Function; + titleId: string; + titleClass: string; + }) => VNode | Component; + /** 自定义内容渲染器 */ + contentRenderer?: ({ + options, + index + }: { + options: DialogOptions; + index: number; + }) => VNode | Component; + /** 自定义按钮操作区的内容渲染器,会覆盖`footerButtons`以及默认的 `取消` 和 `确定` 按钮 */ + footerRenderer?: ({ + options, + index + }: { + options: DialogOptions; + index: number; + }) => VNode | Component; + /** 自定义底部按钮操作 */ + footerButtons?: Array; + /** `Dialog` 打开后的回调 */ + open?: ({ + options, + index + }: { + options: DialogOptions; + index: number; + }) => void; + /** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或空白页或按下了esc键关闭页面时才会触发) */ + close?: ({ + options, + index + }: { + options: DialogOptions; + index: number; + }) => void; + /** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */ + closeCallBack?: ({ + options, + index, + args + }: { + options: DialogOptions; + index: number; + args: any; + }) => void; + /** 点击全屏按钮时的回调 */ + fullscreenCallBack?: ({ + options, + index + }: { + options: DialogOptions; + index: number; + }) => void; + /** 输入焦点聚焦在 `Dialog` 内容时的回调 */ + openAutoFocus?: ({ + options, + index + }: { + options: DialogOptions; + index: number; + }) => void; + /** 输入焦点从 `Dialog` 内容失焦时的回调 */ + closeAutoFocus?: ({ + options, + index + }: { + options: DialogOptions; + index: number; + }) => void; + /** 点击底部取消按钮的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */ + beforeCancel?: ( + done: Function, + { + options, + index + }: { + options: DialogOptions; + index: number; + } + ) => void; + /** 点击底部确定按钮的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */ + beforeSure?: ( + done: Function, + { + options, + index, + closeLoading + }: { + options: DialogOptions; + index: number; + /** 关闭确定按钮的 `loading` 加载动画 */ + closeLoading: Function; + } + ) => void; +} + +export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions }; diff --git a/sop-website/sop-website-frontend/src/components/ReIcon/index.ts b/sop-website/sop-website-frontend/src/components/ReIcon/index.ts new file mode 100644 index 00000000..86efe721 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReIcon/index.ts @@ -0,0 +1,12 @@ +import iconifyIconOffline from "./src/iconifyIconOffline"; +import iconifyIconOnline from "./src/iconifyIconOnline"; +import fontIcon from "./src/iconfont"; + +/** 本地图标组件 */ +const IconifyIconOffline = iconifyIconOffline; +/** 在线图标组件 */ +const IconifyIconOnline = iconifyIconOnline; +/** `iconfont`组件 */ +const FontIcon = fontIcon; + +export { IconifyIconOffline, IconifyIconOnline, FontIcon }; diff --git a/sop-website/sop-website-frontend/src/components/ReIcon/src/hooks.ts b/sop-website/sop-website-frontend/src/components/ReIcon/src/hooks.ts new file mode 100644 index 00000000..5a377dac --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReIcon/src/hooks.ts @@ -0,0 +1,61 @@ +import type { iconType } from "./types"; +import { h, defineComponent, type Component } from "vue"; +import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index"; + +/** + * 支持 `iconfont`、自定义 `svg` 以及 `iconify` 中所有的图标 + * @see 点击查看文档图标篇 {@link https://pure-admin.github.io/pure-admin-doc/pages/icon/} + * @param icon 必传 图标 + * @param attrs 可选 iconType 属性 + * @returns Component + */ +export function useRenderIcon(icon: any, attrs?: iconType): Component { + // iconfont + const ifReg = /^IF-/; + // typeof icon === "function" 属于SVG + if (ifReg.test(icon)) { + // iconfont + const name = icon.split(ifReg)[1]; + const iconName = name.slice( + 0, + name.indexOf(" ") == -1 ? name.length : name.indexOf(" ") + ); + const iconType = name.slice(name.indexOf(" ") + 1, name.length); + return defineComponent({ + name: "FontIcon", + render() { + return h(FontIcon, { + icon: iconName, + iconType, + ...attrs + }); + } + }); + } else if (typeof icon === "function" || typeof icon?.render === "function") { + // svg + return attrs ? h(icon, { ...attrs }) : icon; + } else if (typeof icon === "object") { + return defineComponent({ + name: "OfflineIcon", + render() { + return h(IconifyIconOffline, { + icon: icon, + ...attrs + }); + } + }); + } else { + // 通过是否存在 : 符号来判断是在线还是本地图标,存在即是在线图标,反之 + return defineComponent({ + name: "Icon", + render() { + const IconifyIcon = + icon && icon.includes(":") ? IconifyIconOnline : IconifyIconOffline; + return h(IconifyIcon, { + icon: icon, + ...attrs + }); + } + }); + } +} diff --git a/sop-website/sop-website-frontend/src/components/ReIcon/src/iconfont.ts b/sop-website/sop-website-frontend/src/components/ReIcon/src/iconfont.ts new file mode 100644 index 00000000..c1104519 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReIcon/src/iconfont.ts @@ -0,0 +1,48 @@ +import { h, defineComponent } from "vue"; + +// 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code) +export default defineComponent({ + name: "FontIcon", + props: { + icon: { + type: String, + default: "" + } + }, + render() { + const attrs = this.$attrs; + if (Object.keys(attrs).includes("uni") || attrs?.iconType === "uni") { + return h( + "i", + { + class: "iconfont", + ...attrs + }, + this.icon + ); + } else if ( + Object.keys(attrs).includes("svg") || + attrs?.iconType === "svg" + ) { + return h( + "svg", + { + class: "icon-svg", + "aria-hidden": true + }, + { + default: () => [ + h("use", { + "xlink:href": `#${this.icon}` + }) + ] + } + ); + } else { + return h("i", { + class: `iconfont ${this.icon}`, + ...attrs + }); + } + } +}); diff --git a/sop-website/sop-website-frontend/src/components/ReIcon/src/iconifyIconOffline.ts b/sop-website/sop-website-frontend/src/components/ReIcon/src/iconifyIconOffline.ts new file mode 100644 index 00000000..b47aa99a --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReIcon/src/iconifyIconOffline.ts @@ -0,0 +1,30 @@ +import { h, defineComponent } from "vue"; +import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline"; + +// Iconify Icon在Vue里本地使用(用于内网环境) +export default defineComponent({ + name: "IconifyIconOffline", + components: { IconifyIcon }, + props: { + icon: { + default: null + } + }, + render() { + if (typeof this.icon === "object") addIcon(this.icon, this.icon); + const attrs = this.$attrs; + return h( + IconifyIcon, + { + icon: this.icon, + style: attrs?.style + ? Object.assign(attrs.style, { outline: "none" }) + : { outline: "none" }, + ...attrs + }, + { + default: () => [] + } + ); + } +}); diff --git a/sop-website/sop-website-frontend/src/components/ReIcon/src/iconifyIconOnline.ts b/sop-website/sop-website-frontend/src/components/ReIcon/src/iconifyIconOnline.ts new file mode 100644 index 00000000..a5f5822d --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReIcon/src/iconifyIconOnline.ts @@ -0,0 +1,30 @@ +import { h, defineComponent } from "vue"; +import { Icon as IconifyIcon } from "@iconify/vue"; + +// Iconify Icon在Vue里在线使用(用于外网环境) +export default defineComponent({ + name: "IconifyIconOnline", + components: { IconifyIcon }, + props: { + icon: { + type: String, + default: "" + } + }, + render() { + const attrs = this.$attrs; + return h( + IconifyIcon, + { + icon: `${this.icon}`, + style: attrs?.style + ? Object.assign(attrs.style, { outline: "none" }) + : { outline: "none" }, + ...attrs + }, + { + default: () => [] + } + ); + } +}); diff --git a/sop-website/sop-website-frontend/src/components/ReIcon/src/offlineIcon.ts b/sop-website/sop-website-frontend/src/components/ReIcon/src/offlineIcon.ts new file mode 100644 index 00000000..fc5f9120 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReIcon/src/offlineIcon.ts @@ -0,0 +1,14 @@ +// 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载 +import { addIcon } from "@iconify/vue/dist/offline"; + +// 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标 +// @iconify-icons/ep +import Lollipop from "@iconify-icons/ep/lollipop"; +import HomeFilled from "@iconify-icons/ep/home-filled"; +addIcon("ep:lollipop", Lollipop); +addIcon("ep:home-filled", HomeFilled); +// @iconify-icons/ri +import Search from "@iconify-icons/ri/search-line"; +import InformationLine from "@iconify-icons/ri/information-line"; +addIcon("ri:search-line", Search); +addIcon("ri:information-line", InformationLine); diff --git a/sop-website/sop-website-frontend/src/components/ReIcon/src/types.ts b/sop-website/sop-website-frontend/src/components/ReIcon/src/types.ts new file mode 100644 index 00000000..000bdc59 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReIcon/src/types.ts @@ -0,0 +1,20 @@ +export interface iconType { + // iconify (https://docs.iconify.design/icon-components/vue/#properties) + inline?: boolean; + width?: string | number; + height?: string | number; + horizontalFlip?: boolean; + verticalFlip?: boolean; + flip?: string; + rotate?: number | string; + color?: string; + horizontalAlign?: boolean; + verticalAlign?: boolean; + align?: string; + onLoad?: Function; + includes?: Function; + // svg 需要什么SVG属性自行添加 + fill?: string; + // all icon + style?: object; +} diff --git a/sop-website/sop-website-frontend/src/components/RePerms/index.ts b/sop-website/sop-website-frontend/src/components/RePerms/index.ts new file mode 100644 index 00000000..3701c3c1 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/RePerms/index.ts @@ -0,0 +1,5 @@ +import perms from "./src/perms"; + +const Perms = perms; + +export { Perms }; diff --git a/sop-website/sop-website-frontend/src/components/RePerms/src/perms.tsx b/sop-website/sop-website-frontend/src/components/RePerms/src/perms.tsx new file mode 100644 index 00000000..da01bc16 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/RePerms/src/perms.tsx @@ -0,0 +1,20 @@ +import { defineComponent, Fragment } from "vue"; +import { hasPerms } from "@/utils/auth"; + +export default defineComponent({ + name: "Perms", + props: { + value: { + type: undefined, + default: [] + } + }, + setup(props, { slots }) { + return () => { + if (!slots) return null; + return hasPerms(props.value) ? ( + {slots.default?.()} + ) : null; + }; + } +}); diff --git a/sop-website/sop-website-frontend/src/components/RePureTableBar/index.ts b/sop-website/sop-website-frontend/src/components/RePureTableBar/index.ts new file mode 100644 index 00000000..31b8a16e --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/RePureTableBar/index.ts @@ -0,0 +1,5 @@ +import pureTableBar from "./src/bar"; +import { withInstall } from "@pureadmin/utils"; + +/** 配合 `@pureadmin/table` 实现快速便捷的表格操作 https://github.com/pure-admin/pure-admin-table */ +export const PureTableBar = withInstall(pureTableBar); diff --git a/sop-website/sop-website-frontend/src/components/RePureTableBar/src/bar.tsx b/sop-website/sop-website-frontend/src/components/RePureTableBar/src/bar.tsx new file mode 100644 index 00000000..5367c6b3 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/RePureTableBar/src/bar.tsx @@ -0,0 +1,393 @@ +import Sortable from "sortablejs"; +import { transformI18n } from "@/plugins/i18n"; +import { useEpThemeStoreHook } from "@/store/modules/epTheme"; +import { + type PropType, + ref, + unref, + computed, + nextTick, + defineComponent, + getCurrentInstance +} from "vue"; +import { + delay, + cloneDeep, + isBoolean, + isFunction, + getKeyList +} from "@pureadmin/utils"; + +import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; +import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill"; +import DragIcon from "@/assets/table-bar/drag.svg?component"; +import ExpandIcon from "@/assets/table-bar/expand.svg?component"; +import RefreshIcon from "@/assets/table-bar/refresh.svg?component"; +import SettingIcon from "@/assets/table-bar/settings.svg?component"; +import CollapseIcon from "@/assets/table-bar/collapse.svg?component"; + +const props = { + /** 头部最左边的标题 */ + title: { + type: String, + default: "列表" + }, + /** 对于树形表格,如果想启用展开和折叠功能,传入当前表格的ref即可 */ + tableRef: { + type: Object as PropType + }, + /** 需要展示的列 */ + columns: { + type: Array as PropType, + default: () => [] + }, + isExpandAll: { + type: Boolean, + default: true + }, + tableKey: { + type: [String, Number] as PropType, + default: "0" + } +}; + +export default defineComponent({ + name: "PureTableBar", + props, + emits: ["refresh"], + setup(props, { emit, slots, attrs }) { + const size = ref("default"); + const loading = ref(false); + const checkAll = ref(true); + const isFullscreen = ref(false); + const isIndeterminate = ref(false); + const instance = getCurrentInstance()!; + const isExpandAll = ref(props.isExpandAll); + const filterColumns = cloneDeep(props?.columns).filter(column => + isBoolean(column?.hide) + ? !column.hide + : !(isFunction(column?.hide) && column?.hide()) + ); + let checkColumnList = getKeyList(cloneDeep(props?.columns), "label"); + const checkedColumns = ref(getKeyList(cloneDeep(filterColumns), "label")); + const dynamicColumns = ref(cloneDeep(props?.columns)); + + const getDropdownItemStyle = computed(() => { + return s => { + return { + background: + s === size.value ? useEpThemeStoreHook().epThemeColor : "", + color: s === size.value ? "#fff" : "var(--el-text-color-primary)" + }; + }; + }); + + const iconClass = computed(() => { + return [ + "text-black", + "dark:text-white", + "duration-100", + "hover:!text-primary", + "cursor-pointer", + "outline-none" + ]; + }); + + const topClass = computed(() => { + return [ + "flex", + "justify-between", + "pt-[3px]", + "px-[11px]", + "border-b-[1px]", + "border-solid", + "border-[#dcdfe6]", + "dark:border-[#303030]" + ]; + }); + + function onReFresh() { + loading.value = true; + emit("refresh"); + delay(500).then(() => (loading.value = false)); + } + + function onExpand() { + isExpandAll.value = !isExpandAll.value; + toggleRowExpansionAll(props.tableRef.data, isExpandAll.value); + } + + function toggleRowExpansionAll(data, isExpansion) { + data.forEach(item => { + props.tableRef.toggleRowExpansion(item, isExpansion); + if (item.children !== undefined && item.children !== null) { + toggleRowExpansionAll(item.children, isExpansion); + } + }); + } + + function handleCheckAllChange(val: boolean) { + checkedColumns.value = val ? checkColumnList : []; + isIndeterminate.value = false; + dynamicColumns.value.map(column => + val ? (column.hide = false) : (column.hide = true) + ); + } + + function handleCheckedColumnsChange(value: string[]) { + checkedColumns.value = value; + const checkedCount = value.length; + checkAll.value = checkedCount === checkColumnList.length; + isIndeterminate.value = + checkedCount > 0 && checkedCount < checkColumnList.length; + } + + function handleCheckColumnListChange(val: boolean, label: string) { + dynamicColumns.value.filter( + item => transformI18n(item.label) === transformI18n(label) + )[0].hide = !val; + } + + async function onReset() { + checkAll.value = true; + isIndeterminate.value = false; + dynamicColumns.value = cloneDeep(props?.columns); + checkColumnList = []; + checkColumnList = await getKeyList(cloneDeep(props?.columns), "label"); + checkedColumns.value = getKeyList(cloneDeep(filterColumns), "label"); + } + + const dropdown = { + dropdown: () => ( + + (size.value = "large")} + > + 宽松 + + (size.value = "default")} + > + 默认 + + (size.value = "small")} + > + 紧凑 + + + ) + }; + + /** 列展示拖拽排序 */ + const rowDrop = (event: { preventDefault: () => void }) => { + event.preventDefault(); + nextTick(() => { + const wrapper: HTMLElement = ( + instance?.proxy?.$refs[`GroupRef${unref(props.tableKey)}`] as any + ).$el.firstElementChild; + Sortable.create(wrapper, { + animation: 300, + handle: ".drag-btn", + onEnd: ({ newIndex, oldIndex, item }) => { + const targetThElem = item; + const wrapperElem = targetThElem.parentNode as HTMLElement; + const oldColumn = dynamicColumns.value[oldIndex]; + const newColumn = dynamicColumns.value[newIndex]; + if (oldColumn?.fixed || newColumn?.fixed) { + // 当前列存在fixed属性 则不可拖拽 + const oldThElem = wrapperElem.children[oldIndex] as HTMLElement; + if (newIndex > oldIndex) { + wrapperElem.insertBefore(targetThElem, oldThElem); + } else { + wrapperElem.insertBefore( + targetThElem, + oldThElem ? oldThElem.nextElementSibling : oldThElem + ); + } + return; + } + const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0]; + dynamicColumns.value.splice(newIndex, 0, currentRow); + } + }); + }); + }; + + const isFixedColumn = (label: string) => { + return dynamicColumns.value.filter( + item => transformI18n(item.label) === transformI18n(label) + )[0].fixed + ? true + : false; + }; + + const rendTippyProps = (content: string) => { + // https://vue-tippy.netlify.app/props + return { + content, + offset: [0, 18], + duration: [300, 0], + followCursor: true, + hideOnClick: "toggle" + }; + }; + + const reference = { + reference: () => ( + + ) + }; + + return () => ( + <> +
    +
    + {slots?.title ? ( + slots.title() + ) : ( +

    {props.title}

    + )} +
    + {slots?.buttons ? ( +
    {slots.buttons()}
    + ) : null} + {props.tableRef?.size ? ( + <> + onExpand()} + /> + + + ) : null} + onReFresh()} + /> + + + + + + + +
    + handleCheckAllChange(value)} + /> + onReset()}> + 重置 + +
    + +
    + + handleCheckedColumnsChange(value)} + > + + {checkColumnList.map((item, index) => { + return ( +
    + void; + }) => rowDrop(event)} + /> + + handleCheckColumnListChange(value, item) + } + > + + {transformI18n(item)} + + +
    + ); + })} +
    +
    +
    +
    +
    + + + (isFullscreen.value = !isFullscreen.value)} + /> +
    +
    + {slots.default({ + size: size.value, + dynamicColumns: dynamicColumns.value + })} +
    + + ); + } +}); diff --git a/sop-website/sop-website-frontend/src/components/ReSegmented/index.ts b/sop-website/sop-website-frontend/src/components/ReSegmented/index.ts new file mode 100644 index 00000000..de4253c4 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReSegmented/index.ts @@ -0,0 +1,8 @@ +import reSegmented from "./src/index"; +import { withInstall } from "@pureadmin/utils"; + +/** 分段控制器组件 */ +export const ReSegmented = withInstall(reSegmented); + +export default ReSegmented; +export type { OptionsType } from "./src/type"; diff --git a/sop-website/sop-website-frontend/src/components/ReSegmented/src/index.css b/sop-website/sop-website-frontend/src/components/ReSegmented/src/index.css new file mode 100644 index 00000000..503bbe43 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReSegmented/src/index.css @@ -0,0 +1,157 @@ +.pure-segmented { + --pure-control-padding-horizontal: 12px; + --pure-control-padding-horizontal-sm: 8px; + --pure-segmented-track-padding: 2px; + --pure-segmented-line-width: 1px; + + --pure-segmented-border-radius-small: 4px; + --pure-segmented-border-radius-base: 6px; + --pure-segmented-border-radius-large: 8px; + + box-sizing: border-box; + display: inline-block; + padding: var(--pure-segmented-track-padding); + font-size: var(--el-font-size-base); + color: rgba(0, 0, 0, 0.65); + background-color: rgb(0 0 0 / 4%); + border-radius: var(--pure-segmented-border-radius-base); +} + +.pure-segmented-block { + display: flex; +} + +.pure-segmented-block .pure-segmented-item { + flex: 1; + min-width: 0; +} + +.pure-segmented-block .pure-segmented-item > .pure-segmented-item-label > span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +/* small */ +.pure-segmented.pure-segmented--small { + border-radius: var(--pure-segmented-border-radius-small); +} +.pure-segmented.pure-segmented--small .pure-segmented-item { + border-radius: var(--el-border-radius-small); +} +.pure-segmented.pure-segmented--small .pure-segmented-item > div { + min-height: calc( + var(--el-component-size-small) - var(--pure-segmented-track-padding) * 2 + ); + line-height: calc( + var(--el-component-size-small) - var(--pure-segmented-track-padding) * 2 + ); + padding: 0 + calc( + var(--pure-control-padding-horizontal-sm) - + var(--pure-segmented-line-width) + ); +} + +/* large */ +.pure-segmented.pure-segmented--large { + border-radius: var(--pure-segmented-border-radius-large); +} +.pure-segmented.pure-segmented--large .pure-segmented-item { + border-radius: calc( + var(--el-border-radius-base) + var(--el-border-radius-small) + ); +} +.pure-segmented.pure-segmented--large .pure-segmented-item > div { + min-height: calc( + var(--el-component-size-large) - var(--pure-segmented-track-padding) * 2 + ); + line-height: calc( + var(--el-component-size-large) - var(--pure-segmented-track-padding) * 2 + ); + padding: 0 + calc( + var(--pure-control-padding-horizontal) - var(--pure-segmented-line-width) + ); + font-size: var(--el-font-size-medium); +} + +/* default */ +.pure-segmented-item { + position: relative; + text-align: center; + cursor: pointer; + border-radius: var(--el-border-radius-base); + transition: all 0.1s cubic-bezier(0.645, 0.045, 0.355, 1); +} +.pure-segmented .pure-segmented-item > div { + min-height: calc( + var(--el-component-size) - var(--pure-segmented-track-padding) * 2 + ); + line-height: calc( + var(--el-component-size) - var(--pure-segmented-track-padding) * 2 + ); + padding: 0 + calc( + var(--pure-control-padding-horizontal) - var(--pure-segmented-line-width) + ); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + transition: 0.1s; +} + +.pure-segmented-group { + position: relative; + display: flex; + align-items: stretch; + justify-items: flex-start; + width: 100%; +} + +.pure-segmented-item-selected { + position: absolute; + top: 0; + left: 0; + box-sizing: border-box; + display: none; + width: 0; + height: 100%; + padding: 4px 0; + background-color: #fff; + border-radius: 4px; + box-shadow: + 0 2px 8px -2px rgb(0 0 0 / 5%), + 0 1px 4px -1px rgb(0 0 0 / 7%), + 0 0 1px rgb(0 0 0 / 7%); + transition: + transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1), + width 0.5s cubic-bezier(0.645, 0.045, 0.355, 1); + will-change: transform, width; +} + +.pure-segmented-item > input { + position: absolute; + inset-block-start: 0; + inset-inline-start: 0; + width: 0; + height: 0; + opacity: 0; + pointer-events: none; +} + +.pure-segmented-item-label { + display: flex; + align-items: center; + justify-content: center; +} + +.pure-segmented-item-icon svg { + width: 16px; + height: 16px; +} + +.pure-segmented-item-disabled { + color: rgba(0, 0, 0, 0.25); + cursor: not-allowed; +} diff --git a/sop-website/sop-website-frontend/src/components/ReSegmented/src/index.tsx b/sop-website/sop-website-frontend/src/components/ReSegmented/src/index.tsx new file mode 100644 index 00000000..39580ed8 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReSegmented/src/index.tsx @@ -0,0 +1,216 @@ +import "./index.css"; +import type { OptionsType } from "./type"; +import { useRenderIcon } from "@/components/ReIcon/src/hooks"; +import { + useDark, + isNumber, + isFunction, + useResizeObserver +} from "@pureadmin/utils"; +import { + type PropType, + h, + ref, + toRef, + watch, + nextTick, + defineComponent, + getCurrentInstance +} from "vue"; + +const props = { + options: { + type: Array, + default: () => [] + }, + /** 默认选中,按照第一个索引为 `0` 的模式,可选(`modelValue`只有传`number`类型时才为响应式) */ + modelValue: { + type: undefined, + require: false, + default: "0" + }, + /** 将宽度调整为父元素宽度 */ + block: { + type: Boolean, + default: false + }, + /** 控件尺寸 */ + size: { + type: String as PropType<"small" | "default" | "large"> + }, + /** 是否全局禁用,默认 `false` */ + disabled: { + type: Boolean, + default: false + }, + /** 当内容发生变化时,设置 `resize` 可使其自适应容器位置 */ + resize: { + type: Boolean, + default: false + } +}; + +export default defineComponent({ + name: "ReSegmented", + props, + emits: ["change", "update:modelValue"], + setup(props, { emit }) { + const width = ref(0); + const translateX = ref(0); + const { isDark } = useDark(); + const initStatus = ref(false); + const curMouseActive = ref(-1); + const segmentedItembg = ref(""); + const instance = getCurrentInstance()!; + const curIndex = isNumber(props.modelValue) + ? toRef(props, "modelValue") + : ref(0); + + function handleChange({ option, index }, event: Event) { + if (props.disabled || option.disabled) return; + event.preventDefault(); + isNumber(props.modelValue) + ? emit("update:modelValue", index) + : (curIndex.value = index); + segmentedItembg.value = ""; + emit("change", { index, option }); + } + + function handleMouseenter({ option, index }, event: Event) { + if (props.disabled) return; + event.preventDefault(); + curMouseActive.value = index; + if (option.disabled || curIndex.value === index) { + segmentedItembg.value = ""; + } else { + segmentedItembg.value = isDark.value + ? "#1f1f1f" + : "rgba(0, 0, 0, 0.06)"; + } + } + + function handleMouseleave(_, event: Event) { + if (props.disabled) return; + event.preventDefault(); + curMouseActive.value = -1; + } + + function handleInit(index = curIndex.value) { + nextTick(() => { + const curLabelRef = instance?.proxy?.$refs[`labelRef${index}`] as ElRef; + if (!curLabelRef) return; + width.value = curLabelRef.clientWidth; + translateX.value = curLabelRef.offsetLeft; + initStatus.value = true; + }); + } + + function handleResizeInit() { + useResizeObserver(".pure-segmented", () => { + nextTick(() => { + handleInit(curIndex.value); + }); + }); + } + + (props.block || props.resize) && handleResizeInit(); + + watch( + () => curIndex.value, + index => { + nextTick(() => { + handleInit(index); + }); + }, + { + immediate: true + } + ); + + watch(() => props.size, handleResizeInit, { + immediate: true + }); + + const rendLabel = () => { + return props.options.map((option, index) => { + return ( + + ); + }); + }; + + return () => ( +
    +
    +
    + {rendLabel()} +
    +
    + ); + } +}); diff --git a/sop-website/sop-website-frontend/src/components/ReSegmented/src/type.ts b/sop-website/sop-website-frontend/src/components/ReSegmented/src/type.ts new file mode 100644 index 00000000..205e34dc --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReSegmented/src/type.ts @@ -0,0 +1,20 @@ +import type { VNode, Component } from "vue"; +import type { iconType } from "@/components/ReIcon/src/types.ts"; + +export interface OptionsType { + /** 文字 */ + label?: string | (() => VNode | Component); + /** + * @description 图标,采用平台内置的 `useRenderIcon` 函数渲染 + * @see {@link 用法参考 https://pure-admin.github.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks } + */ + icon?: string | Component; + /** 图标属性、样式配置 */ + iconAttrs?: iconType; + /** 值 */ + value?: any; + /** 是否禁用 */ + disabled?: boolean; + /** `tooltip` 提示 */ + tip?: string; +} diff --git a/sop-website/sop-website-frontend/src/components/ReText/index.ts b/sop-website/sop-website-frontend/src/components/ReText/index.ts new file mode 100644 index 00000000..62135660 --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReText/index.ts @@ -0,0 +1,7 @@ +import reText from "./src/index.vue"; +import { withInstall } from "@pureadmin/utils"; + +/** 支持`Tooltip`提示的文本省略组件 */ +export const ReText = withInstall(reText); + +export default ReText; diff --git a/sop-website/sop-website-frontend/src/components/ReText/src/index.vue b/sop-website/sop-website-frontend/src/components/ReText/src/index.vue new file mode 100644 index 00000000..ecaebdbb --- /dev/null +++ b/sop-website/sop-website-frontend/src/components/ReText/src/index.vue @@ -0,0 +1,66 @@ + + + diff --git a/sop-website/sop-website-frontend/src/config/index.ts b/sop-website/sop-website-frontend/src/config/index.ts new file mode 100644 index 00000000..c81d1c4d --- /dev/null +++ b/sop-website/sop-website-frontend/src/config/index.ts @@ -0,0 +1,55 @@ +import axios from "axios"; +import type { App } from "vue"; + +let config: object = {}; +const { VITE_PUBLIC_PATH } = import.meta.env; + +const setConfig = (cfg?: unknown) => { + config = Object.assign(config, cfg); +}; + +const getConfig = (key?: string): PlatformConfigs => { + if (typeof key === "string") { + const arr = key.split("."); + if (arr && arr.length) { + let data = config; + arr.forEach(v => { + if (data && typeof data[v] !== "undefined") { + data = data[v]; + } else { + data = null; + } + }); + return data; + } + } + return config; +}; + +/** 获取项目动态全局配置 */ +export const getPlatformConfig = async (app: App): Promise => { + app.config.globalProperties.$config = getConfig(); + return axios({ + method: "get", + url: `${VITE_PUBLIC_PATH}platform-config.json` + }) + .then(({ data: config }) => { + let $config = app.config.globalProperties.$config; + // 自动注入系统配置 + if (app && $config && typeof config === "object") { + $config = Object.assign($config, config); + app.config.globalProperties.$config = $config; + // 设置全局配置 + setConfig($config); + } + return $config; + }) + .catch(() => { + throw "请在public文件夹下添加platform-config.json配置文件"; + }); +}; + +/** 本地响应式存储的命名空间 */ +const responsiveStorageNameSpace = () => getConfig().ResponsiveStorageNameSpace; + +export { getConfig, setConfig, responsiveStorageNameSpace }; diff --git a/sop-website/sop-website-frontend/src/directives/auth/index.ts b/sop-website/sop-website-frontend/src/directives/auth/index.ts new file mode 100644 index 00000000..2fc64904 --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/auth/index.ts @@ -0,0 +1,15 @@ +import { hasAuth } from "@/router/utils"; +import type { Directive, DirectiveBinding } from "vue"; + +export const auth: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding>) { + const { value } = binding; + if (value) { + !hasAuth(value) && el.parentNode?.removeChild(el); + } else { + throw new Error( + "[Directive: auth]: need auths! Like v-auth=\"['btn.add','btn.edit']\"" + ); + } + } +}; diff --git a/sop-website/sop-website-frontend/src/directives/copy/index.ts b/sop-website/sop-website-frontend/src/directives/copy/index.ts new file mode 100644 index 00000000..b71fa190 --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/copy/index.ts @@ -0,0 +1,33 @@ +import { message } from "@/utils/message"; +import { useEventListener } from "@vueuse/core"; +import { copyTextToClipboard } from "@pureadmin/utils"; +import type { Directive, DirectiveBinding } from "vue"; + +export interface CopyEl extends HTMLElement { + copyValue: string; +} + +/** 文本复制指令(默认双击复制) */ +export const copy: Directive = { + mounted(el: CopyEl, binding: DirectiveBinding) { + const { value } = binding; + if (value) { + el.copyValue = value; + const arg = binding.arg ?? "dblclick"; + // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + useEventListener(el, arg, () => { + const success = copyTextToClipboard(el.copyValue); + success + ? message("复制成功", { type: "success" }) + : message("复制失败", { type: "error" }); + }); + } else { + throw new Error( + '[Directive: copy]: need value! Like v-copy="modelValue"' + ); + } + }, + updated(el: CopyEl, binding: DirectiveBinding) { + el.copyValue = binding.value; + } +}; diff --git a/sop-website/sop-website-frontend/src/directives/index.ts b/sop-website/sop-website-frontend/src/directives/index.ts new file mode 100644 index 00000000..d01fe714 --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/index.ts @@ -0,0 +1,6 @@ +export * from "./auth"; +export * from "./copy"; +export * from "./longpress"; +export * from "./optimize"; +export * from "./perms"; +export * from "./ripple"; diff --git a/sop-website/sop-website-frontend/src/directives/longpress/index.ts b/sop-website/sop-website-frontend/src/directives/longpress/index.ts new file mode 100644 index 00000000..4eec6a22 --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/longpress/index.ts @@ -0,0 +1,63 @@ +import { useEventListener } from "@vueuse/core"; +import type { Directive, DirectiveBinding } from "vue"; +import { subBefore, subAfter, isFunction } from "@pureadmin/utils"; + +export const longpress: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const cb = binding.value; + if (cb && isFunction(cb)) { + let timer = null; + let interTimer = null; + let num = 500; + let interNum = null; + const isInter = binding?.arg?.includes(":") ?? false; + + if (isInter) { + num = Number(subBefore(binding.arg, ":")); + interNum = Number(subAfter(binding.arg, ":")); + } else if (binding.arg) { + num = Number(binding.arg); + } + + const clear = () => { + if (timer) { + clearTimeout(timer); + timer = null; + } + if (interTimer) { + clearInterval(interTimer); + interTimer = null; + } + }; + + const onDownInter = (ev: PointerEvent) => { + ev.preventDefault(); + if (interTimer === null) { + interTimer = setInterval(() => cb(), interNum); + } + }; + + const onDown = (ev: PointerEvent) => { + clear(); + ev.preventDefault(); + if (timer === null) { + timer = isInter + ? setTimeout(() => { + cb(); + onDownInter(ev); + }, num) + : setTimeout(() => cb(), num); + } + }; + + // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + useEventListener(el, "pointerdown", onDown); + useEventListener(el, "pointerup", clear); + useEventListener(el, "pointerleave", clear); + } else { + throw new Error( + '[Directive: longpress]: need callback and callback must be a function! Like v-longpress="callback"' + ); + } + } +}; diff --git a/sop-website/sop-website-frontend/src/directives/optimize/index.ts b/sop-website/sop-website-frontend/src/directives/optimize/index.ts new file mode 100644 index 00000000..7b92538d --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/optimize/index.ts @@ -0,0 +1,68 @@ +import { + isArray, + throttle, + debounce, + isObject, + isFunction +} from "@pureadmin/utils"; +import { useEventListener } from "@vueuse/core"; +import type { Directive, DirectiveBinding } from "vue"; + +export interface OptimizeOptions { + /** 事件名 */ + event: string; + /** 事件触发的方法 */ + fn: (...params: any) => any; + /** 是否立即执行 */ + immediate?: boolean; + /** 防抖或节流的延迟时间(防抖默认:`200`毫秒、节流默认:`1000`毫秒) */ + timeout?: number; + /** 传递的参数 */ + params?: any; +} + +/** 防抖(v-optimize或v-optimize:debounce)、节流(v-optimize:throttle)指令 */ +export const optimize: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + const optimizeType = binding.arg ?? "debounce"; + const type = ["debounce", "throttle"].find(t => t === optimizeType); + if (type) { + if (value && value.event && isFunction(value.fn)) { + let params = value?.params; + if (params) { + if (isArray(params) || isObject(params)) { + params = isObject(params) ? Array.of(params) : params; + } else { + throw new Error( + "[Directive: optimize]: `params` must be an array or object" + ); + } + } + // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + useEventListener( + el, + value.event, + type === "debounce" + ? debounce( + params ? () => value.fn(...params) : value.fn, + value?.timeout ?? 200, + value?.immediate ?? false + ) + : throttle( + params ? () => value.fn(...params) : value.fn, + value?.timeout ?? 1000 + ) + ); + } else { + throw new Error( + "[Directive: optimize]: `event` and `fn` are required, and `fn` must be a function" + ); + } + } else { + throw new Error( + "[Directive: optimize]: only `debounce` and `throttle` are supported" + ); + } + } +}; diff --git a/sop-website/sop-website-frontend/src/directives/perms/index.ts b/sop-website/sop-website-frontend/src/directives/perms/index.ts new file mode 100644 index 00000000..073c918b --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/perms/index.ts @@ -0,0 +1,15 @@ +import { hasPerms } from "@/utils/auth"; +import type { Directive, DirectiveBinding } from "vue"; + +export const perms: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding>) { + const { value } = binding; + if (value) { + !hasPerms(value) && el.parentNode?.removeChild(el); + } else { + throw new Error( + "[Directive: perms]: need perms! Like v-perms=\"['btn.add','btn.edit']\"" + ); + } + } +}; diff --git a/sop-website/sop-website-frontend/src/directives/ripple/index.scss b/sop-website/sop-website-frontend/src/directives/ripple/index.scss new file mode 100644 index 00000000..061c82c9 --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/ripple/index.scss @@ -0,0 +1,48 @@ +/* stylelint-disable-next-line scss/dollar-variable-colon-space-after */ +$ripple-animation-transition-in: + transform 0.4s cubic-bezier(0, 0, 0.2, 1), + opacity 0.2s cubic-bezier(0, 0, 0.2, 1) !default; +$ripple-animation-transition-out: opacity 0.5s cubic-bezier(0, 0, 0.2, 1) !default; +$ripple-animation-visible-opacity: 0.25 !default; + +.v-ripple { + &__container { + position: absolute; + top: 0; + left: 0; + z-index: 0; + width: 100%; + height: 100%; + overflow: hidden; + pointer-events: none; + border-radius: inherit; + contain: strict; + } + + &__animation { + position: absolute; + top: 0; + left: 0; + overflow: hidden; + pointer-events: none; + background: currentcolor; + border-radius: 50%; + opacity: 0; + will-change: transform, opacity; + + &--enter { + opacity: 0; + transition: none; + } + + &--in { + opacity: $ripple-animation-visible-opacity; + transition: $ripple-animation-transition-in; + } + + &--out { + opacity: 0; + transition: $ripple-animation-transition-out; + } + } +} diff --git a/sop-website/sop-website-frontend/src/directives/ripple/index.ts b/sop-website/sop-website-frontend/src/directives/ripple/index.ts new file mode 100644 index 00000000..3fd94d9c --- /dev/null +++ b/sop-website/sop-website-frontend/src/directives/ripple/index.ts @@ -0,0 +1,229 @@ +import "./index.scss"; +import { isObject } from "@pureadmin/utils"; +import type { Directive, DirectiveBinding } from "vue"; + +export interface RippleOptions { + /** 自定义`ripple`颜色,支持`tailwindcss` */ + class?: string; + /** 是否从中心扩散 */ + center?: boolean; + circle?: boolean; +} + +export interface RippleDirectiveBinding + extends Omit { + value?: boolean | { class: string }; + modifiers: { + center?: boolean; + circle?: boolean; + }; +} + +function transform(el: HTMLElement, value: string) { + el.style.transform = value; + el.style.webkitTransform = value; +} + +const calculate = ( + e: PointerEvent, + el: HTMLElement, + value: RippleOptions = {} +) => { + const offset = el.getBoundingClientRect(); + + // 获取点击位置距离 el 的垂直和水平距离 + let localX = e.clientX - offset.left; + let localY = e.clientY - offset.top; + + let radius = 0; + let scale = 0.3; + // 计算点击位置到 el 顶点最远距离,即为圆的最大半径(勾股定理) + if (el._ripple?.circle) { + scale = 0.15; + radius = el.clientWidth / 2; + radius = value.center + ? radius + : radius + Math.sqrt((localX - radius) ** 2 + (localY - radius) ** 2) / 4; + } else { + radius = Math.sqrt(el.clientWidth ** 2 + el.clientHeight ** 2) / 2; + } + + // 中心点坐标 + const centerX = `${(el.clientWidth - radius * 2) / 2}px`; + const centerY = `${(el.clientHeight - radius * 2) / 2}px`; + + // 点击位置坐标 + const x = value.center ? centerX : `${localX - radius}px`; + const y = value.center ? centerY : `${localY - radius}px`; + + return { radius, scale, x, y, centerX, centerY }; +}; + +const ripples = { + show(e: PointerEvent, el: HTMLElement, value: RippleOptions = {}) { + if (!el?._ripple?.enabled) { + return; + } + + // 创建 ripple 元素和 ripple 父元素 + const container = document.createElement("span"); + const animation = document.createElement("span"); + + container.appendChild(animation); + container.className = "v-ripple__container"; + + if (value.class) { + container.className += ` ${value.class}`; + } + + const { radius, scale, x, y, centerX, centerY } = calculate(e, el, value); + + // ripple 圆大小 + const size = `${radius * 2}px`; + + animation.className = "v-ripple__animation"; + animation.style.width = size; + animation.style.height = size; + + el.appendChild(container); + + // 获取目标元素样式表 + const computed = window.getComputedStyle(el); + // 防止 position 被覆盖导致 ripple 位置有问题 + if (computed && computed.position === "static") { + el.style.position = "relative"; + el.dataset.previousPosition = "static"; + } + + animation.classList.add("v-ripple__animation--enter"); + animation.classList.add("v-ripple__animation--visible"); + transform( + animation, + `translate(${x}, ${y}) scale3d(${scale},${scale},${scale})` + ); + animation.dataset.activated = String(performance.now()); + + setTimeout(() => { + animation.classList.remove("v-ripple__animation--enter"); + animation.classList.add("v-ripple__animation--in"); + transform(animation, `translate(${centerX}, ${centerY}) scale3d(1,1,1)`); + }, 0); + }, + + hide(el: HTMLElement | null) { + if (!el?._ripple?.enabled) return; + + const ripples = el.getElementsByClassName("v-ripple__animation"); + + if (ripples.length === 0) return; + const animation = ripples[ripples.length - 1] as HTMLElement; + + if (animation.dataset.isHiding) return; + else animation.dataset.isHiding = "true"; + + const diff = performance.now() - Number(animation.dataset.activated); + const delay = Math.max(250 - diff, 0); + + setTimeout(() => { + animation.classList.remove("v-ripple__animation--in"); + animation.classList.add("v-ripple__animation--out"); + + setTimeout(() => { + const ripples = el.getElementsByClassName("v-ripple__animation"); + if (ripples.length === 1 && el.dataset.previousPosition) { + el.style.position = el.dataset.previousPosition; + delete el.dataset.previousPosition; + } + + if (animation.parentNode?.parentNode === el) + el.removeChild(animation.parentNode); + }, 300); + }, delay); + } +}; + +function isRippleEnabled(value: any): value is true { + return typeof value === "undefined" || !!value; +} + +function rippleShow(e: PointerEvent) { + const value: RippleOptions = {}; + const element = e.currentTarget as HTMLElement | undefined; + + if (!element?._ripple || element._ripple.touched) return; + + value.center = element._ripple.centered; + if (element._ripple.class) { + value.class = element._ripple.class; + } + + ripples.show(e, element, value); +} + +function rippleHide(e: Event) { + const element = e.currentTarget as HTMLElement | null; + if (!element?._ripple) return; + + window.setTimeout(() => { + if (element._ripple) { + element._ripple.touched = false; + } + }); + ripples.hide(element); +} + +function updateRipple( + el: HTMLElement, + binding: RippleDirectiveBinding, + wasEnabled: boolean +) { + const { value, modifiers } = binding; + const enabled = isRippleEnabled(value); + if (!enabled) { + ripples.hide(el); + } + + el._ripple = el._ripple ?? {}; + el._ripple.enabled = enabled; + el._ripple.centered = modifiers.center; + el._ripple.circle = modifiers.circle; + if (isObject(value) && value.class) { + el._ripple.class = value.class; + } + + if (enabled && !wasEnabled) { + el.addEventListener("pointerdown", rippleShow); + el.addEventListener("pointerup", rippleHide); + } else if (!enabled && wasEnabled) { + removeListeners(el); + } +} + +function removeListeners(el: HTMLElement) { + el.removeEventListener("pointerdown", rippleShow); + el.removeEventListener("pointerup", rippleHide); +} + +function mounted(el: HTMLElement, binding: RippleDirectiveBinding) { + updateRipple(el, binding, false); +} + +function unmounted(el: HTMLElement) { + delete el._ripple; + removeListeners(el); +} + +function updated(el: HTMLElement, binding: RippleDirectiveBinding) { + if (binding.value === binding.oldValue) { + return; + } + + const wasEnabled = isRippleEnabled(binding.oldValue); + updateRipple(el, binding, wasEnabled); +} + +export const Ripple: Directive = { + mounted, + unmounted, + updated +}; diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-content/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-content/index.vue new file mode 100644 index 00000000..5810d665 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-content/index.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-footer/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-footer/index.vue new file mode 100644 index 00000000..77631343 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-footer/index.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-frame/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-frame/index.vue new file mode 100644 index 00000000..b2bb9d51 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-frame/index.vue @@ -0,0 +1,79 @@ + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-navbar/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-navbar/index.vue new file mode 100644 index 00000000..eea685b7 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-navbar/index.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-notice/components/NoticeItem.vue b/sop-website/sop-website-frontend/src/layout/components/lay-notice/components/NoticeItem.vue new file mode 100644 index 00000000..823d9cd8 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-notice/components/NoticeItem.vue @@ -0,0 +1,177 @@ + + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-notice/components/NoticeList.vue b/sop-website/sop-website-frontend/src/layout/components/lay-notice/components/NoticeList.vue new file mode 100644 index 00000000..7bc9922c --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-notice/components/NoticeList.vue @@ -0,0 +1,24 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-notice/data.ts b/sop-website/sop-website-frontend/src/layout/components/lay-notice/data.ts new file mode 100644 index 00000000..bd49f5e5 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-notice/data.ts @@ -0,0 +1,99 @@ +import { $t } from "@/plugins/i18n"; + +export interface ListItem { + avatar: string; + title: string; + datetime: string; + type: string; + description: string; + status?: "primary" | "success" | "warning" | "info" | "danger"; + extra?: string; +} + +export interface TabItem { + key: string; + name: string; + list: ListItem[]; + emptyText: string; +} + +export const noticesData: TabItem[] = [ + { + key: "1", + name: $t("status.pureNotify"), + list: [], + emptyText: $t("status.pureNoNotify") + }, + { + key: "2", + name: $t("status.pureMessage"), + list: [ + { + avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile1.svg", + title: "小铭 评论了你", + description: "诚在于心,信在于行,诚信在于心行合一。", + datetime: "今天", + type: "2" + }, + { + avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile2.svg", + title: "李白 回复了你", + description: "长风破浪会有时,直挂云帆济沧海。", + datetime: "昨天", + type: "2" + }, + { + avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile5.svg", + title: "标题", + description: + "请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容", + datetime: "时间", + type: "2" + } + ], + emptyText: $t("status.pureNoMessage") + }, + { + key: "3", + name: $t("status.pureTodo"), + list: [ + { + avatar: "", + title: "第三方紧急代码变更", + description: + "小林提交于 2024-05-10,需在 2024-05-11 前完成代码变更任务", + datetime: "", + extra: "马上到期", + status: "danger", + type: "3" + }, + { + avatar: "", + title: "版本发布", + description: "指派小铭于 2024-06-18 前完成更新并发布", + datetime: "", + extra: "已耗时 8 天", + status: "warning", + type: "3" + }, + { + avatar: "", + title: "新功能开发", + description: "开发多租户管理", + datetime: "", + extra: "进行中", + type: "3" + }, + { + avatar: "", + title: "任务名称", + description: "任务需要在 2030-10-30 10:00 前启动", + datetime: "", + extra: "未开始", + status: "info", + type: "3" + } + ], + emptyText: $t("status.pureNoTodo") + } +]; diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-notice/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-notice/index.vue new file mode 100644 index 00000000..d85cf0f7 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-notice/index.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-panel/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-panel/index.vue new file mode 100644 index 00000000..fb4fb20a --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-panel/index.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchFooter.vue b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchFooter.vue new file mode 100644 index 00000000..d8350d0f --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchFooter.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchHistory.vue b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchHistory.vue new file mode 100644 index 00000000..87d5488c --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchHistory.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchHistoryItem.vue b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchHistoryItem.vue new file mode 100644 index 00000000..0ee9f194 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchHistoryItem.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchModal.vue b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchModal.vue new file mode 100644 index 00000000..48fb9cf7 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchModal.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchResult.vue b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchResult.vue new file mode 100644 index 00000000..12507b15 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-search/components/SearchResult.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-search/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-search/index.vue new file mode 100644 index 00000000..123d6a66 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-search/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-search/types.ts b/sop-website/sop-website-frontend/src/layout/components/lay-search/types.ts new file mode 100644 index 00000000..a39adbd4 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-search/types.ts @@ -0,0 +1,20 @@ +interface optionsItem { + path: string; + type: "history" | "collect"; + meta: { + icon?: string; + title?: string; + }; +} + +interface dragItem { + oldIndex: number; + newIndex: number; +} + +interface Props { + value: string; + options: Array; +} + +export type { optionsItem, dragItem, Props }; diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-setting/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-setting/index.vue new file mode 100644 index 00000000..18aacf25 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-setting/index.vue @@ -0,0 +1,642 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavHorizontal.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavHorizontal.vue new file mode 100644 index 00000000..c4fa3e5a --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavHorizontal.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavMix.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavMix.vue new file mode 100644 index 00000000..fc50ede5 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavMix.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavVertical.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavVertical.vue new file mode 100644 index 00000000..0e9fa129 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/NavVertical.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarBreadCrumb.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarBreadCrumb.vue new file mode 100644 index 00000000..417b8394 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarBreadCrumb.vue @@ -0,0 +1,121 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarCenterCollapse.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarCenterCollapse.vue new file mode 100644 index 00000000..0f41fcca --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarCenterCollapse.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarExtraIcon.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarExtraIcon.vue new file mode 100644 index 00000000..7cad16e6 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarExtraIcon.vue @@ -0,0 +1,20 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarFullScreen.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarFullScreen.vue new file mode 100644 index 00000000..4d38bd0c --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarFullScreen.vue @@ -0,0 +1,30 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarItem.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarItem.vue new file mode 100644 index 00000000..cffa6771 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarItem.vue @@ -0,0 +1,223 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLeftCollapse.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLeftCollapse.vue new file mode 100644 index 00000000..c007d3b8 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLeftCollapse.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLinkItem.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLinkItem.vue new file mode 100644 index 00000000..8911c122 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLinkItem.vue @@ -0,0 +1,32 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLogo.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLogo.vue new file mode 100644 index 00000000..0441f52f --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarLogo.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarTopCollapse.vue b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarTopCollapse.vue new file mode 100644 index 00000000..c2f1b5ad --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-sidebar/components/SidebarTopCollapse.vue @@ -0,0 +1,38 @@ + + + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-tag/components/TagChrome.vue b/sop-website/sop-website-frontend/src/layout/components/lay-tag/components/TagChrome.vue new file mode 100644 index 00000000..137365b4 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-tag/components/TagChrome.vue @@ -0,0 +1,33 @@ + diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-tag/index.scss b/sop-website/sop-website-frontend/src/layout/components/lay-tag/index.scss new file mode 100644 index 00000000..b8812169 --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-tag/index.scss @@ -0,0 +1,371 @@ +@keyframes schedule-in-width { + from { + width: 0; + } + + to { + width: 100%; + } +} + +@keyframes schedule-out-width { + from { + width: 100%; + } + + to { + width: 0; + } +} + +.tags-view { + position: relative; + display: flex; + align-items: center; + width: 100%; + font-size: 14px; + color: var(--el-text-color-primary); + background: #fff; + box-shadow: 0 0 1px #888; + + .scroll-item { + position: relative; + display: inline-block; + height: 34px; + padding-left: 6px; + line-height: 34px; + cursor: pointer; + transition: all 0.4s; + + &:not(:first-child) { + padding-right: 24px; + } + + &.chrome-item { + padding-right: 0; + padding-left: 0; + margin-right: -18px; + box-shadow: none; + } + + .el-icon-close { + position: absolute; + top: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + color: var(--el-color-primary); + cursor: pointer; + border-radius: 4px; + transition: + background-color 0.12s, + color 0.12s; + transform: translate(0, -50%); + + &:hover { + color: rgb(0 0 0 / 88%) !important; + background-color: rgb(0 0 0 / 6%); + } + } + } + + .tag-title { + padding: 0 4px; + color: var(--el-text-color-primary); + text-decoration: none; + } + + .scroll-container { + position: relative; + flex: 1; + overflow: hidden; + white-space: nowrap; + + &.chrome-scroll-container { + padding-top: 4px; + + .fixed-tag { + padding: 0 !important; + } + } + + .tab { + position: relative; + float: left; + overflow: visible; + white-space: nowrap; + list-style: none; + + .scroll-item { + transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); + + &:nth-child(1) { + padding: 0 12px; + } + + &.chrome-item { + &:nth-child(1) { + padding: 0; + } + } + } + + .fixed-tag { + padding: 0 12px; + } + } + } + + /* 右键菜单 */ + .contextmenu { + position: absolute; + padding: 5px 0; + margin: 0; + font-size: 13px; + font-weight: normal; + color: var(--el-text-color-primary); + white-space: nowrap; + list-style-type: none; + background: #fff; + border-radius: 4px; + outline: 0; + box-shadow: 0 2px 8px rgb(0 0 0 / 15%); + + li { + display: flex; + align-items: center; + width: 100%; + padding: 7px 12px; + margin: 0; + cursor: pointer; + + &:hover { + color: var(--el-color-primary); + } + + svg { + display: block; + margin-right: 0.5em; + } + } + } +} + +.el-dropdown-menu { + li { + display: flex; + align-items: center; + width: 100%; + margin: 0; + cursor: pointer; + + svg { + display: block; + margin-right: 0.5em; + } + } +} + +.el-dropdown-menu__item:not(.is-disabled):hover { + color: #606266; + background: #f0f0f0; +} + +:deep(.el-dropdown-menu__item) i { + margin-right: 10px; +} + +:deep(.el-dropdown-menu__item--divided) { + margin: 1px 0; +} + +.el-dropdown-menu__item--divided::before { + margin: 0; +} + +.el-dropdown-menu__item.is-disabled { + cursor: not-allowed; +} + +.scroll-item.is-active { + position: relative; + color: #fff; + box-shadow: 0 0 0.7px #888; + + .chrome-tab { + z-index: 10; + } + + .chrome-tab__bg { + color: var(--el-color-primary-light-9) !important; + } + + .tag-title { + color: var(--el-color-primary) !important; + } + + .chrome-close-btn { + color: var(--el-color-primary); + + &:hover { + background-color: var(--el-color-primary); + } + } + + .chrome-tab-divider { + opacity: 0; + } +} + +.arrow-left, +.arrow-right, +.arrow-down { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 34px; + color: var(--el-text-color-primary); + + svg { + width: 20px; + height: 20px; + } +} + +.arrow-left { + box-shadow: 5px 0 5px -6px #ccc; + + &:hover { + cursor: w-resize; + } +} + +.arrow-right { + border-right: 0.5px solid #ccc; + box-shadow: -5px 0 5px -6px #ccc; + + &:hover { + cursor: e-resize; + } +} + +/* 卡片模式下鼠标移入显示蓝色边框 */ +.card-in { + color: var(--el-color-primary); + + .tag-title { + color: var(--el-color-primary); + } +} + +/* 卡片模式下鼠标移出隐藏蓝色边框 */ +.card-out { + color: #666; + border: none; + + .tag-title { + color: #666; + } +} + +/* 灵动模式 */ +.schedule-active { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 2px; + background: var(--el-color-primary); +} + +/* 灵动模式下鼠标移入显示蓝色进度条 */ +.schedule-in { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 2px; + background: var(--el-color-primary); + animation: schedule-in-width 200ms ease-in; +} + +/* 灵动模式下鼠标移出隐藏蓝色进度条 */ +.schedule-out { + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--el-color-primary); + animation: schedule-out-width 200ms ease-in; +} + +/* 谷歌风格的页签 */ +.chrome-tab { + position: relative; + display: inline-flex; + gap: 16px; + align-items: center; + justify-content: center; + padding: 0 24px; + white-space: nowrap; + cursor: pointer; + + .tag-title { + padding: 0; + } + + .chrome-tab-divider { + position: absolute; + right: 7px; + width: 1px; + height: 14px; + background-color: #2b2d2f; + } + + &:hover { + z-index: 10; + + .chrome-tab__bg { + color: #dee1e6; + } + + .tag-title { + color: #1f1f1f; + } + + .chrome-tab-divider { + opacity: 0; + } + } + + .chrome-tab__bg { + position: absolute; + top: 0; + left: 0; + z-index: -10; + width: 100%; + height: 100%; + color: transparent; + pointer-events: none; + } + + .chrome-close-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + color: #666; + border-radius: 50%; + + &:hover { + color: white; + background-color: #b1b3b8; + } + } +} diff --git a/sop-website/sop-website-frontend/src/layout/components/lay-tag/index.vue b/sop-website/sop-website-frontend/src/layout/components/lay-tag/index.vue new file mode 100644 index 00000000..87f234fd --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/components/lay-tag/index.vue @@ -0,0 +1,685 @@ + + + + + diff --git a/sop-website/sop-website-frontend/src/layout/frame.vue b/sop-website/sop-website-frontend/src/layout/frame.vue new file mode 100644 index 00000000..4243b57d --- /dev/null +++ b/sop-website/sop-website-frontend/src/layout/frame.vue @@ -0,0 +1,97 @@ + + +