完成前端与后端链接 登录API完成 #1

Merged
minglipro merged 1 commits from dev into master 2025-06-28 22:29:32 +08:00
62 changed files with 7457 additions and 26 deletions

View File

@ -8,14 +8,14 @@ jobs:
Build:
runs-on: ubuntu-dev
steps:
- name: Check out repository code
uses: https://git.mingliqiye.com/Actions/checkout@v4
- name: build-test
- name: Build
run: |
pnpm install
pnpm run build-jar
pnpm run build-jar-auto
- name: Releases
run: |

17
application.yaml Normal file
View File

@ -0,0 +1,17 @@
config:
port: 9963
app-name: maven-repository
app-version: 1.0
data-source:
mysql:
username: pan
password: PhXCRiCfEmiGesEm
host: 10.0.0.4
port: 3307
database: pan
redis:
host: 10.0.0.4
port: 6380
database: 0
username: ''
password: redis_kTJpsa

View File

@ -1,10 +1,12 @@
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import java.util.*
plugins {
java
id("org.springframework.boot") version "3.5.3"
idea
id("io.freefair.lombok") version "8.4"
id("org.springframework.boot") version "3.5.0"
id("io.spring.dependency-management") version "1.1.7"
}
val GROUPSID = project.properties["GROUPSID"] as String
@ -20,19 +22,55 @@ version = VERSIONS
val libDir = rootDir.resolve("build").resolve("libs")
val srcDir = rootDir.resolve("src").resolve("main").resolve("java")
val webDir = rootDir.resolve("src").resolve("main").resolve("resources").resolve("static")
val webDir = rootDir.resolve("src").resolve("main").resolve("resources").resolve("html")
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
allprojects {
configurations.all {
exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
}
}
tasks.test {
useJUnitPlatform()
jvmArgs = listOf(
"-javaagent:${classpath.find { it.name.contains("mockito-core") }?.absolutePath}",
"-XX:+EnableDynamicAgentLoading",
)
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-log4j2")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-actuator")
compileOnly("org.projectlombok:lombok")
testRuntimeOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
implementation("org.jetbrains:annotations:24.0.0")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.1")
testImplementation("org.springframework.boot:spring-boot-starter-test")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("com.mysql:mysql-connector-j")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9")
implementation("cn.dev33:sa-token-redis-jackson:1.44.0")
implementation("cn.hutool:hutool-all:5.8.24")
implementation("cn.dev33:sa-token-jwt:1.44.0")
implementation("org.mindrot:jbcrypt:0.4")
implementation("com.github.f4b6a3:uuid-creator:6.1.0")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.16")
implementation("com.alibaba:druid-spring-boot-3-starter:1.2.25")
implementation("cn.dev33:sa-token-jwt:1.44.0")
implementation("cn.dev33:sa-token-spring-boot3-starter:1.44.0")
implementation("cn.dev33:sa-token-redis-jackson:1.44.0")
implementation("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.12")
}
private fun generateHash(file: File, string: String): String {

View File

@ -5,7 +5,8 @@
"type": "module",
"scripts": {
"dev": "vite",
"build-jar": "run-p \"build-only\" && gradle -Dorg.gradle.java.home=/opt/jdk/21.0.7/ build-jar",
"build-jar": "run-p \"build-only\" && gradle build-jar",
"build-jar-auto": "run-p \"build-only\" && gradle -Dorg.gradle.java.home=/opt/jdk/21.0.7/ build-jar",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",

View File

@ -1,9 +1,13 @@
package com.mingliqiye.disk;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan
@MapperScan("com.mingliqiye.disk.mappers")
public class DiskApplication {
public static void main(String[] args) {

View File

@ -0,0 +1,64 @@
package com.mingliqiye.disk.cache;
import com.mingliqiye.disk.config.ApplicationContextHolder;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.DigestUtils;
public class MyBatisRedisCache implements Cache {
private final String id;
private final RedisTemplate<String, Object> redisTemplate;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public MyBatisRedisCache(String id) {
this.id = id;
this.redisTemplate = ApplicationContextHolder.getBean(RedisTemplate.class);
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
String redisKey = getRedisKey(key);
redisTemplate.opsForValue().set(redisKey, value, 1, TimeUnit.HOURS); // 设置1小时过期
}
@Override
public Object getObject(Object key) {
String redisKey = getRedisKey(key);
return redisTemplate.opsForValue().get(redisKey);
}
@Override
public Object removeObject(Object key) {
String redisKey = getRedisKey(key);
redisTemplate.delete(redisKey);
return null;
}
@Override
public void clear() {
redisTemplate.delete(redisTemplate.keys(id + ":*"));
}
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
private String getRedisKey(Object key) {
return (id + ":" + DigestUtils.md5DigestAsHex(String.valueOf(key).getBytes()));
}
}

View File

@ -0,0 +1,30 @@
package com.mingliqiye.disk.config;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
public static <T> T getBean(Class<T> name) {
return context.getBean(name);
}
public static <T> T getBean(String beanName) {
return (T) context.getBean(beanName);
}
public static <T> T getBean(String beanName, Class<T> beanType) {
return context.getBean(beanName, beanType);
}
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}

View File

@ -0,0 +1,23 @@
package com.mingliqiye.disk.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Primary
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}

View File

@ -0,0 +1,23 @@
package com.mingliqiye.disk.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForSimple();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}

View File

@ -0,0 +1,51 @@
package com.mingliqiye.disk.config;
import io.swagger.v3.oas.annotations.ExternalDocumentation;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@OpenAPIDefinition(
externalDocs = @ExternalDocumentation(
description = "@git.mingliqiye",
url = "https://git.mingliqiye.com/mingliqiye/pan-disk"
)
)
@SecurityScheme(
name = "Authorization-Bearer-Token", // 认证方案名称
type = SecuritySchemeType.HTTP, // 认证类型当前为http认证
description = "Authorization: bearer {token}", // 描述信息
in = SecuritySchemeIn.HEADER, // 代表在http请求头部
scheme = "bearer", // 认证方案Authorization: bearer token信息
bearerFormat = "JWT"
) // 表示使用 JWT 格式作为 Bearer Token 的格式
@Configuration
public class SpringDocConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
// 配置接口文档基本信息
.info(this.getApiInfo());
}
private Info getApiInfo() {
return new Info()
.title("pan-disk")
.description("SpringBoot3 pan-disk Swagger3 ApiDoc")
.contact(
new Contact().name("mingliqiye").url("https://www.mingliqiye.com").email("minglipro@mingliqiye.com")
)
.license(new License().name("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0"))
.summary("ApiDoc")
.termsOfService("https://pan.mingliqiye.com/")
.version("1.0");
}
}

View File

@ -0,0 +1,14 @@
package com.mingliqiye.disk.configuration;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "config")
public class Config {
private String appName;
private String appVersion;
private Integer port;
private DataSource dataSource;
}

View File

@ -0,0 +1,12 @@
package com.mingliqiye.disk.configuration;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "config.data-source")
public class DataSource {
private Mysql mysql;
private Redis redis;
}

View File

@ -0,0 +1,15 @@
package com.mingliqiye.disk.configuration;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "config.data-source.mysql")
public class Mysql {
private String host;
private Integer port;
private String database;
private String username;
private String password;
}

View File

@ -0,0 +1,15 @@
package com.mingliqiye.disk.configuration;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "config.data-source.redis")
public class Redis {
private String host;
private Integer port;
private Integer database;
private String username;
private String password;
}

View File

@ -0,0 +1,56 @@
package com.mingliqiye.disk.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mingliqiye.disk.dto.auth.Login;
import com.mingliqiye.disk.exception.ExceptionCode;
import com.mingliqiye.disk.http.Respose;
import com.mingliqiye.disk.mappers.UserMapper;
import com.mingliqiye.disk.model.User;
import com.mingliqiye.disk.util.PanStpUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/apis/auth")
@Tag(name = "权限管理", description = "权限和用户管理")
public class AuthController {
private final UserMapper userMapper;
public AuthController(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Operation(summary = "登陆")
@PostMapping("/login")
public Respose<String> login(@Valid @RequestBody Login loginBody) {
User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", loginBody.getUsername()));
if (user != null && BCrypt.checkpw(loginBody.getPassword(), user.getPassword())) {
return Respose.builder(PanStpUtil.login(user.getId()));
}
return Respose.error(String.class, ExceptionCode.ERROR_INTERNAL_SERVER, "用户名或密码错误");
}
@Operation(summary = "获取当前登陆用户的信息")
@SecurityRequirement(name = "Authorization-Bearer-Token")
@GetMapping("/who-is-me")
public Respose<User> whoIsMe() {
return Respose.builder().setData(userMapper.selectById(PanStpUtil.getLoginId()).setPasswordNull());
}
@Operation(summary = "登出", security = @SecurityRequirement(name = "Authorization-Bearer-Token"))
@DeleteMapping("/logout")
@SaCheckLogin
public Respose<Object> logout() {
StpUtil.logout();
return Respose.builder();
}
}

View File

@ -0,0 +1,27 @@
package com.mingliqiye.disk.controller;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.io.InputStream;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@RestController
@RequestMapping
@Tag(name = "前端路由", description = "统一匹配路径指向VueRouter")
public class IndexController {
@GetMapping(value = { "/", "/{path:^(?!static|apis).*$}/**" })
public ResponseEntity<StreamingResponseBody> index() {
StreamingResponseBody streamingResponseBody = s -> {
try (InputStream stream = this.getClass().getResourceAsStream("/html/index.html")) {
if (stream != null) {
stream.transferTo(s);
}
}
};
return ResponseEntity.ok().body(streamingResponseBody);
}
}

View File

@ -0,0 +1,14 @@
package com.mingliqiye.disk.dto.auth;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class Login {
@NotNull(message = "用户名不能为空")
private String username;
@NotNull(message = "用户密码不能为空")
private String password;
}

View File

@ -0,0 +1,34 @@
package com.mingliqiye.disk.exception;
import java.io.Serializable;
public class BaseException extends RuntimeException implements BaseExceptionInterface, Serializable {
private Integer code;
private String message;
public BaseException(String message, Integer code, Throwable throwable) {
super(message, throwable);
this.code = code;
this.message = message;
}
public BaseException(String message, Integer code) {
this(message, code, null);
}
@Override
public Integer getCode() {
return code;
}
@Override
public Throwable getThrowable() {
return this.getCause();
}
@Override
public String getMessage() {
return message;
}
}

View File

@ -0,0 +1,103 @@
package com.mingliqiye.disk.exception;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.mingliqiye.disk.http.Respose;
import com.mingliqiye.disk.util.StringUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<Respose<?>> exceptionHandler(BaseException e, HttpServletRequest request) {
return ResponseEntity.status(e.getCode()).body(
Respose.builder().setCode(e.getCode()).setMessage(StringUtil.format("{}", e.getMessage()))
);
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Respose<?> exceptionHandler(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
return Respose.builder()
.setCode(ExceptionCode.ERROR_METHOD_NOT_ALLOWED.getValue())
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()));
}
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<Respose<?>> exceptionHandler(
NoHandlerFoundException e,
HttpServletRequest request,
HttpServletResponse response
) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
Respose.builder()
.setCode(ExceptionCode.ERROR_NOT_FOUND.getValue())
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()))
);
}
@ExceptionHandler(NotLoginException.class)
public Respose<?> exceptionHandler(NotLoginException e, HttpServletRequest request) {
return Respose.builder()
.setCode(ExceptionCode.ERROR_UNAUTHORIZED.getValue())
.setMessage(e.getMessage())
.setData(e.getType());
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public Respose<?> exceptionHandler(HttpMessageNotReadableException e, HttpServletRequest request) {
return Respose.builder()
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()));
}
@ExceptionHandler(NotRoleException.class)
public Respose<?> exceptionHandler(NotRoleException e) {
return Respose.builder()
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
.setMessage(e.getMessage())
.setData(e.getCode());
}
@ExceptionHandler(NotPermissionException.class)
public Respose<?> exceptionHandler(NotPermissionException e) {
return Respose.builder()
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
.setMessage(e.getMessage())
.setData(e.getCode());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Respose<?> exceptionHandler(MethodArgumentNotValidException e) {
return Respose.builder()
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
.setMessage(
StringUtil.join(
",",
e.getBindingResult().getFieldErrors(),
DefaultMessageSourceResolvable::getDefaultMessage
)
);
}
@ExceptionHandler(Exception.class)
public Respose<?> exceptionHandler(Exception e) {
log.error(e.getMessage(), e);
return Respose.builder()
.setCode(ExceptionCode.ERROR_INTERNAL_SERVER.getValue())
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()));
}
}

View File

@ -0,0 +1,19 @@
package com.mingliqiye.disk.exception;
import java.io.Serializable;
public interface BaseExceptionInterface extends Serializable {
String getMessage();
Integer getCode();
StackTraceElement[] getStackTrace();
Throwable getThrowable();
String toString();
default String getClassName() {
return this.getClass().getName();
}
}

View File

@ -0,0 +1,29 @@
package com.mingliqiye.disk.exception;
import lombok.Getter;
public enum ExceptionCode {
ERROR_INTERNAL_SERVER(500),
ERROR_PAYMENT_REQUIRED(402),
ERROR_NOT_FOUND(404),
ERROR_METHOD_NOT_ALLOWED(405),
ERROR_UNAUTHORIZED(401),
ERROR_FORBIDDEN(403),
OK(200);
@Getter
private final int value;
ExceptionCode(int value) {
this.value = value;
}
public static ExceptionCode getExceptionCode(int value) {
for (ExceptionCode exceptionCode : ExceptionCode.values()) {
if (exceptionCode.value == value) {
return exceptionCode;
}
}
return null;
}
}

View File

@ -0,0 +1,14 @@
package com.mingliqiye.disk.exception;
public class InternalServerException extends BaseException {
private static final Integer code = ExceptionCode.ERROR_INTERNAL_SERVER.getValue();
public InternalServerException(String message, Throwable throwable) {
super(message, code, throwable);
}
public InternalServerException(String message) {
super(message, code);
}
}

View File

@ -0,0 +1,14 @@
package com.mingliqiye.disk.exception;
public class NotFoundException extends BaseException {
private static final Integer code = ExceptionCode.ERROR_NOT_FOUND.getValue();
public NotFoundException(String message, Throwable throwable) {
super(message, code, throwable);
}
public NotFoundException(String message) {
super(message, code);
}
}

View File

@ -0,0 +1,6 @@
package com.mingliqiye.disk.function;
@FunctionalInterface
public interface P1Function<P> {
void call(P p);
}

View File

@ -0,0 +1,6 @@
package com.mingliqiye.disk.function;
@FunctionalInterface
public interface P1R1Function<P, R> {
R call(P p);
}

View File

@ -0,0 +1,82 @@
package com.mingliqiye.disk.http;
import com.mingliqiye.disk.exception.ExceptionCode;
import com.mingliqiye.disk.time.DateTime;
public class Respose<T> {
private int code = ExceptionCode.OK.getValue();
private String message = "操作成功";
private T data;
private DateTime dateTime = DateTime.now();
public Respose(int code, String message, T data, DateTime dateTime) {
this.code = code;
this.message = message;
this.data = data;
this.dateTime = dateTime;
}
public Respose(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public Respose(int code, String message) {
this.code = code;
this.message = message;
}
public Respose(T data) {
this.data = data;
}
public Respose() {}
public static <T> Respose<T> builder() {
return new Respose<>();
}
public static <T> Respose<T> builder(T data) {
return new Respose<>().setData(data);
}
public static <T> Respose<T> error(Class<T> type, Integer code, String message) {
return new Respose<T>().setCode(code).setMessage(message);
}
public static <T> Respose<T> error(Class<T> type, ExceptionCode code, String message) {
return error(type, code.getValue(), message);
}
public String getMessage() {
return message;
}
public Respose<T> setMessage(String message) {
this.message = message;
return this;
}
public int getCode() {
return code;
}
public Respose<T> setCode(int code) {
this.code = code;
return this;
}
public DateTime getDateTime() {
return dateTime;
}
public T getData() {
return data;
}
public <TD> Respose<TD> setData(TD data) {
return new Respose<>(code, message, data);
}
}

View File

@ -0,0 +1,9 @@
package com.mingliqiye.disk.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mingliqiye.disk.cache.MyBatisRedisCache;
import com.mingliqiye.disk.model.User;
import org.apache.ibatis.annotations.CacheNamespace;
@CacheNamespace(implementation = MyBatisRedisCache.class)
public interface UserMapper extends BaseMapper<User> {}

View File

@ -0,0 +1,42 @@
package com.mingliqiye.disk.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.mingliqiye.disk.time.DateTime;
import com.mingliqiye.disk.uuid.UUID;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName(value = "users", autoResultMap = true)
public class User {
private UUID id;
private String username;
private String password;
private String nickname;
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> prermissions;
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> roles;
private byte[] icon;
private boolean admin;
private DateTime creationTime;
private DateTime updateTime;
public User setPasswordNull() {
setPassword("***");
return this;
}
}

View File

@ -0,0 +1,241 @@
package com.mingliqiye.disk.time;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import lombok.Getter;
import lombok.Setter;
/**
* com.mingliqiye.libs.Main
* <p>自定义时间类
*
* @author MingLiPro|Armamem0t
* @version 1.0
* @see LocalDateTime
* @see Date
* @see ZoneId
*/
@JsonSerialize(using = DateTimeJsonSerializer.class)
@JsonDeserialize(using = DateTimeJsonDeserializer.class)
@Schema(type = "String")
@JsonView(String.class)
public class DateTime implements Serializable {
@Getter
@Setter
private LocalDateTime localDateTime;
@Getter
@Setter
private ZoneId zoneId = ZoneId.systemDefault();
DateTime() {
this(Instant.now());
}
DateTime(Instant instant) {
this(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
}
DateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
DateTime(Instant instant, ZoneId zoneId) {
this(LocalDateTime.ofInstant(instant, zoneId));
}
DateTime(Date date) {
this(date.toInstant());
}
public static DateTime now() {
return new DateTime();
}
public static DateTime ofCurrentTimeMillis(long time) {
return new DateTime(Instant.ofEpochMilli(time));
}
public static DateTime of(Instant instant) {
return new DateTime(instant);
}
public static String format(Date date, String formater) {
return format(toLocalDateTime(date), formater);
}
public static String format(LocalDateTime date, String formater) {
return DateTimeFormatter.ofPattern(formater).format(date);
}
public static LocalDateTime toLocalDateTime(Date date) {
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
public static String format(LocalDateTime date, Formats formats) {
return format(date, formats.value);
}
public static DateTime parse(String timestr, Formats formater) {
return parse(timestr, formater.value);
}
public static DateTime parse(String timestr, String formater) {
DateTimeFormatter sdf = DateTimeFormatter.ofPattern(formater);
return of(LocalDateTime.parse(timestr, sdf));
}
public static DateTime of(LocalDateTime localDateTime) {
return new DateTime(localDateTime);
}
public static void main(String[] args) {}
@Override
public String toString() {
return (
this.getClass().getName() +
'@' +
Integer.toHexString(hashCode()) +
'(' +
format(Formats.STANDARD_DATETIME_MILLISECOUND7) +
")"
);
}
public String format(Formats formater) {
return format(localDateTime, formater.value);
}
public Instant toInstant() {
return toInstant(localDateTime);
}
public static Instant toInstant(LocalDateTime localDateTime) {
return toInstant(localDateTime, ZoneId.systemDefault());
}
public static Instant toInstant(LocalDateTime localDateTime, ZoneId zoneId) {
return localDateTime.atZone(zoneId).toInstant();
}
public Instant toInstant(ZoneId zoneId) {
return toInstant(localDateTime, zoneId);
}
public Date toDate() {
return toDate(localDateTime);
}
public static Date toDate(LocalDateTime localDateTime) {
return Date.from(toInstant(localDateTime));
}
public String format(String formater) {
return format(localDateTime, formater);
}
public DateTime plusYears(long years) {
return plusYears(this, years);
}
public static DateTime plusYears(DateTime dateTime, long years) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusYears(years));
return dateTime;
}
public DateTime plusMonths(long months) {
return plusMonths(this, months);
}
public static DateTime plusMonths(DateTime dateTime, long months) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusMonths(months));
return dateTime;
}
public DateTime plusWeeks(long weeks) {
return plusWeeks(this, weeks);
}
public static DateTime plusWeeks(DateTime dateTime, long weeks) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusWeeks(weeks));
return dateTime;
}
public DateTime plusDays(long days) {
return plusDays(this, days);
}
public static DateTime plusDays(DateTime dateTime, long days) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusDays(days));
return dateTime;
}
public DateTime plusHours(long hours) {
return plusHours(this, hours);
}
public static DateTime plusHours(DateTime dateTime, long hours) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusHours(hours));
return dateTime;
}
public DateTime plusMinutes(long minutes) {
return plusMinutes(this, minutes);
}
public static DateTime plusMinutes(DateTime dateTime, long minutes) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusMinutes(minutes));
return dateTime;
}
public DateTime plusSeconds(long seconds) {
return plusSeconds(this, seconds);
}
public static DateTime plusSeconds(DateTime dateTime, long seconds) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusSeconds(seconds));
return dateTime;
}
public DateTime plusNanos(long nanos) {
return plusNanos(this, nanos);
}
public static DateTime plusNanos(DateTime dateTime, long nanos) {
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusNanos(nanos));
return dateTime;
}
@Getter
public enum Formats {
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
STANDARD_DATE("yyyy-MM-dd"),
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
COMPACT_DATETIME("yyyyMMddHHmmss");
private final String value;
Formats(String value) {
this.value = value;
}
}
}

View File

@ -0,0 +1,17 @@
package com.mingliqiye.disk.time;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public class DateTimeJsonDeserializer extends JsonDeserializer<DateTime> {
@Override
public DateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.isNaN()) {
return null;
}
return DateTime.parse(p.getValueAsString(), DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6);
}
}

View File

@ -0,0 +1,33 @@
package com.mingliqiye.disk.time;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import java.io.IOException;
public class DateTimeJsonSerializer extends JsonSerializer<DateTime> {
@Override
public void serialize(DateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
gen.writeString(value.format(DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6));
}
@Override
public void serializeWithType(
DateTime value,
JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer
) throws IOException {
WritableTypeId typeId = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_STRING));
serialize(value, gen, serializers);
typeSer.writeTypeSuffix(gen, typeId);
}
}

View File

@ -0,0 +1,45 @@
package com.mingliqiye.disk.typeHandlers;
import com.mingliqiye.disk.time.DateTime;
import java.sql.*;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
@MappedTypes({ DateTime.class })
@MappedJdbcTypes(JdbcType.VARCHAR)
public class DateTimeTypeHandler extends BaseTypeHandler<DateTime> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, DateTime parameter, JdbcType jdbcType)
throws SQLException {
ps.setTimestamp(i, Timestamp.valueOf(parameter.getLocalDateTime()));
}
@Override
public DateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parse(rs.getString(columnName));
}
@Override
public DateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parse(rs.getString(columnIndex));
}
@Override
public DateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parse(cs.getString(columnIndex));
}
public DateTime parse(String s) {
if (s == null) {
return null;
}
return DateTime.parse(s, DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6);
}
public String format(DateTime t) {
return t.format(DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6);
}
}

View File

@ -0,0 +1,55 @@
package com.mingliqiye.disk.typeHandlers;
import com.mingliqiye.disk.uuid.UUID;
import java.nio.ByteBuffer;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
@MappedTypes({ UUID.class })
@MappedJdbcTypes(JdbcType.BINARY)
public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
public static byte[] UUID_TO_BIN(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.GetUUID().getMostSignificantBits());
bb.putLong(uuid.GetUUID().getLeastSignificantBits());
return bb.array();
}
public static UUID BIN_TO_UUID(byte[] bytes) {
if (bytes == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
long mostSig = bb.getLong();
long leastSig = bb.getLong();
return new UUID(mostSig, leastSig);
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType)
throws SQLException {
ps.setBytes(i, UUID_TO_BIN(parameter));
}
@Override
public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnName));
}
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnIndex));
}
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return BIN_TO_UUID(cs.getBytes(columnIndex));
}
}

View File

@ -0,0 +1,264 @@
package com.mingliqiye.disk.util;
import com.mingliqiye.disk.function.P1Function;
import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class FileUtil {
public static String save(InputStream inputStream, Path filepath, P1Function<Long> callback) {
return save(inputStream, filepath.toString(), callback);
}
public static String save(InputStream inputStream, String filepath, P1Function<Long> callback) {
Path filePath = Paths.get(filepath);
Path path1 = filePath.getParent();
if (!path1.toFile().exists()) {
path1.toFile().mkdirs();
}
byte[] buffer = new byte[1024];
int len;
long total = 0;
try {
filePath.toFile().createNewFile();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
try (FileOutputStream fileOutputStream = new FileOutputStream(filePath.toFile())) {
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
total += len;
if (callback != null) {
callback.call(total);
}
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return filePath.toAbsolutePath().toString();
}
public static String save(InputStream inputStream, Path filepath) {
return save(inputStream, filepath.toString(), null);
}
public static String save(InputStream inputStream, String filepath) {
return save(inputStream, filepath, null);
}
public static boolean copy(String fromCopy, String toCopyed) {
return copy(Path.of(fromCopy), Path.of(toCopyed));
}
public static boolean copy(Path fromCopy, Path toCopyed) {
return copy(fromCopy.toFile(), toCopyed.toFile());
}
public static boolean copy(File fromCopy, File toCopyed) {
try (InputStream inputStream = new FileInputStream(fromCopy)) {
try (OutputStream outputStream = new FileOutputStream(toCopyed)) {
inputStream.transferTo(outputStream);
return true;
}
} catch (IOException e) {
return false;
}
}
public static String saveTxt(String txt, String path) {
return saveTxt(txt, Paths.get(path));
}
public static String saveTxt(String txt, Path path) {
return saveTxt(txt, path.toFile());
}
public static String saveTxt(String txt, File path) {
try (OutputStream outputStream = new FileOutputStream(path)) {
outputStream.write(txt.getBytes());
} catch (IOException e) {
return null;
}
return path.getAbsolutePath();
}
public static byte[] readBytes(String path) {
return readBytes(Paths.get(path));
}
public static byte[] readBytes(Path path) {
return readBytes(path.toFile());
}
public static byte[] readBytes(File f) {
try (FileInputStream fileInputStream = new FileInputStream(f)) {
return readBytes(fileInputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static byte[] readBytes(InputStream inputStream) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
inputStream.transferTo(byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String readString(String path) {
return readString(Paths.get(path));
}
public static String readString(Path path) {
return readString(path.toFile());
}
public static String readString(File path) {
byte[] bytes = readBytes(path);
return new String(bytes);
}
public static String readString(InputStream path) {
byte[] bytes = readBytes(path);
return new String(bytes);
}
public static boolean delete(Path filepath) {
try {
Files.delete(filepath);
return true;
} catch (IOException e) {
return false;
}
}
public static void main(String[] args) {
log.info("This is a test message");
}
/**
* 文件范围读取工具类包含起始和结束位置
* 有效范围from [0, filesize-1], to [from, filesize-1]
*/
public class FileRangeReader {
/**
* 读取文件指定范围内容到输出流
*
* @param filePath 文件路径
* @param from 起始位置包含0-based
* @param to 结束位置包含
* @param output 输出流
* @throws IOException 如果发生I/O错误
* @throws IllegalArgumentException 如果范围无效
*/
public static void read(String filePath, long from, long to, OutputStream output)
throws IOException, IllegalArgumentException {
// 参数验证
if (from < 0) {
throw new IllegalArgumentException("起始位置不能小于0");
}
if (to < from) {
throw new IllegalArgumentException("结束位置不能小于起始位置");
}
Path path = Paths.get(filePath);
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
long fileSize = fileChannel.size();
// 自动调整超出文件范围的请求
if (from >= fileSize) {
return; // 起始位置已超出文件范围不读取任何内容
}
if (to >= fileSize) {
to = fileSize - 1; // 调整结束位置为文件末尾
}
try (WritableByteChannel outChannel = Channels.newChannel(output)) {
long position = from;
long remaining = to - from + 1; // +1因为包含结束位置
// 使用零拷贝技术传输数据
while (remaining > 0) {
long transferred = fileChannel.transferTo(position, remaining, outChannel);
if (transferred <= 0) break;
position += transferred;
remaining -= transferred;
}
}
}
}
/**
* 读取文件全部内容到输出流
*
* @param filePath 文件路径
* @param output 输出流
* @throws IOException 如果发生I/O错误
*/
public static void readFull(String filePath, OutputStream output) throws IOException {
Path path = Paths.get(filePath);
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
long fileSize = fileChannel.size();
if (fileSize > 0) {
read(filePath, 0, fileSize - 1, output);
}
}
}
/**
* 读取文件指定范围内容到字节数组
*
* @param filePath 文件路径
* @param from 起始位置
* @param to 结束位置包含
* @return 读取的字节数组
* @throws IOException 如果发生I/O错误
*/
public static byte[] readToByteArray(String filePath, long from, long to) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
read(filePath, from, to, output);
return output.toByteArray();
}
}
/**
* 读取文件全部内容到字节数组
*
* @param filePath 文件路径
* @return 文件内容的字节数组
* @throws IOException 如果发生I/O错误
*/
public static byte[] readFullToByteArray(String filePath) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
readFull(filePath, output);
return output.toByteArray();
}
}
/**
* 获取文件大小
*
* @param filePath 文件路径
* @return 文件大小字节数
* @throws IOException 如果发生I/O错误
*/
public static long getFileSize(String filePath) throws IOException {
return Paths.get(filePath).toFile().length();
}
}
}

View File

@ -0,0 +1,3 @@
package com.mingliqiye.disk.util;
public class HashUtil {}

View File

@ -0,0 +1,17 @@
package com.mingliqiye.disk.util;
import java.util.ArrayList;
import java.util.List;
public class ListUtil {
public static List<String> toStringList(Object[] object) {
List<String> list = new ArrayList<>();
for (Object obj : object) {
list.add(obj.toString());
}
return list;
}
public static class StringList extends ArrayList<String> {}
}

View File

@ -0,0 +1,17 @@
package com.mingliqiye.disk.util;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class NullUtil {
public static class NullJsonSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString("***");
}
}
}

View File

@ -0,0 +1,16 @@
package com.mingliqiye.disk.util;
import cn.dev33.satoken.stp.StpUtil;
import com.mingliqiye.disk.uuid.UUID;
public class PanStpUtil {
public static UUID getLoginId() {
return UUID.ofString((String) StpUtil.getLoginId());
}
public static String login(UUID uuid) {
StpUtil.login(uuid.toUUIDString());
return StpUtil.getTokenValue();
}
}

View File

@ -0,0 +1,66 @@
package com.mingliqiye.disk.util;
import com.mingliqiye.disk.function.P1R1Function;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.helpers.MessageFormatter;
public class StringUtil {
public static String toString(Object obj) {
return obj == null ? "" : obj.toString();
}
public static String format(String format, Object... args) {
return MessageFormatter.arrayFormat(format, args).getMessage();
}
public static boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
public static void main(String[] args) {
String d = joinOf(",", "", "", "1", "", "2", "3", "4", "5", "6", "7", "8", "9");
StringUtil.println(d);
List<String> dd = split(d, ",");
StringUtil.println(dd);
println(10000 / 1000);
}
public static String joinOf(String spec, String... objects) {
return join(spec, Arrays.asList(objects));
}
public static void println(Object... objects) {
if (objects.length == 1) {
System.out.println(objects[0]);
} else {
System.out.println(join(" ", ListUtil.toStringList(objects)));
}
}
public static List<String> split(String str, String separator) {
List<String> data = new ArrayList<>(Arrays.asList(str.split(separator)));
while (!data.isEmpty() && data.getFirst().isEmpty()) data.removeFirst();
return data;
}
public static <P> String join(String separator, List<P> list, P1R1Function<P, String> fun) {
StringBuilder sb = StringUtil.stringBuilder();
for (int i = 0; i < list.size(); i++) {
P item = list.get(i);
if (i == 0) sb.append(fun == null ? item.toString() : fun.call(item));
else sb.append(separator).append(fun == null ? item.toString() : fun.call(item));
}
return sb.toString();
}
public static String join(String separator, List<String> list) {
return join(separator, list, null);
}
public static StringBuilder stringBuilder() {
return new StringBuilder();
}
}

View File

@ -0,0 +1,40 @@
package com.mingliqiye.disk.util;
import lombok.Getter;
public class SystemUtil {
@Getter
private static final String osName = System.getProperties().getProperty("os.name");
public static boolean isWindows() {
return osName != null && osName.startsWith("Windows");
}
public static boolean isMac() {
return osName != null && osName.startsWith("Mac");
}
public static boolean isUnix() {
return ((osName != null && osName.startsWith("Linux")) || (!isWindows() && !isMac()));
}
public static String getJdkVersion() {
return System.getProperty("java.specification.version");
}
public static Integer getJavaVersionFloat() {
String version = getJdkVersion();
String uversion;
if (version.startsWith("1.")) {
uversion = version.substring(2, 3);
} else {
uversion = version.substring(0, 2);
}
return Integer.parseInt(uversion);
}
public static boolean isjdk8plus() {
return getJavaVersionFloat() > 8;
}
}

View File

@ -0,0 +1,82 @@
package com.mingliqiye.disk.util;
/**
* 线程局部变量工具类
*/
public class ThreadLocalDataHolderUtil {
// 静态实例用于存储字符串类型的线程局部变量
public static final ThreadLocalDataHolder<String> LibrarysLocalDataHolderStatic = new ThreadLocalDataHolder<>();
/**
* 泛型线程局部变量持有器
*
* @param <T> 存储的数据类型
*/
public static class ThreadLocalDataHolder<T> {
private final ThreadLocal<T> threadLocal;
public ThreadLocalDataHolder() {
this.threadLocal = new ThreadLocal<>();
}
/**
* 获取当前线程存储的值
*
* @return 当前线程存储的值如果没有则返回null
*/
public T get() {
return threadLocal.get();
}
/**
* 设置当前线程的值
*
* @param value 要存储的值
*/
public void set(T value) {
threadLocal.set(value);
}
/**
* 移除当前线程存储的值
*/
public void remove() {
threadLocal.remove();
}
/**
* 获取当前线程存储的值如果不存在则返回默认值
*
* @param defaultValue 默认值
* @return 当前线程存储的值或默认值
*/
public T getOrDefault(T defaultValue) {
T value = threadLocal.get();
return value != null ? value : defaultValue;
}
/**
* 安全获取值避免NPE
*
* @return 值或null
*/
public T safeGet() {
try {
return threadLocal.get();
} catch (Exception e) {
return null;
}
}
/**
* 检查当前线程是否有值
*
* @return 是否有值
*/
public boolean isPresent() {
return threadLocal.get() != null;
}
}
}

View File

@ -0,0 +1,141 @@
package com.mingliqiye.disk.uuid;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.github.f4b6a3.uuid.UuidCreator;
import com.mingliqiye.disk.time.DateTime;
import com.mingliqiye.disk.util.StringUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Locale;
import java.util.Objects;
import lombok.Setter;
@Setter
@JsonSerialize(using = UUIDJsonSerializer.class)
@JsonDeserialize(using = UUIDJsonDeserializer.class)
@JsonView(String.class)
@Schema(type = "String")
public class UUID implements Serializable {
private java.util.UUID uuid;
public UUID(long msb, long lsb) {
uuid = new java.util.UUID(msb, lsb);
}
public UUID() {
uuid = UuidCreator.getTimeBased();
}
public UUID(java.util.UUID uuid) {
this.uuid = uuid;
}
public UUID(String uuid) {
this.uuid = java.util.UUID.fromString(uuid);
}
public static UUID of(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
long msb = bb.getLong();
long lsb = bb.getLong();
return new UUID(msb, lsb);
}
public static UUID ofString(String data) {
try {
java.util.UUID uuid1 = java.util.UUID.fromString(data);
UUID uuid = new UUID();
uuid.setUuid(uuid1);
return uuid;
} catch (Exception e) {
throw new UUIDException(e.getMessage(), e);
}
}
public byte[] toBytes() {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
public java.util.UUID GetUUID() {
return uuid;
}
public String toUUIDString() {
return toUUIDString(false);
}
public String toUUIDString(boolean u) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (u) {
return uuid.toString().toUpperCase(Locale.ROOT);
}
return uuid.toString();
}
@Override
public int hashCode() {
return Objects.hash(uuid);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UUID uuid = (UUID) o;
return Objects.equals(this.uuid, uuid.uuid);
}
@Override
public String toString() {
if (uuid == null) return "UUID(null)";
if (uuid.version() == 1) return StringUtil.format(
"UUID(uuid={},time={},mac={},version={})",
toUUIDString(true),
getDateTime().format(DateTime.Formats.STANDARD_DATETIME),
extractMACFromUUID(),
uuid.version()
);
return StringUtil.format("UUID(uuid={},version={})", toUUIDString(true), uuid.version());
}
public DateTime getDateTime() {
if (uuid == null) {
return null;
}
return DateTime.ofCurrentTimeMillis(uuid.timestamp() / 10_000).plusDays(-141427L);
}
public String extractMACFromUUID() {
return extractMACFromUUID(null);
}
public String extractMACFromUUID(String spec) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (spec == null) {
spec = ":";
}
long leastSigBits = uuid.getLeastSignificantBits();
long macLong = leastSigBits & 0xFFFFFFFFFFFFL;
byte[] macBytes = new byte[6];
for (int i = 0; i < 6; i++) {
macBytes[5 - i] = (byte) (macLong >> (8 * i));
}
StringBuilder mac = new StringBuilder();
for (int i = 0; i < 6; i++) {
mac.append(String.format("%02X", macBytes[i]));
if (i < 5) mac.append(spec);
}
return mac.toString();
}
}

View File

@ -0,0 +1,12 @@
package com.mingliqiye.disk.uuid;
public class UUIDException extends RuntimeException {
public UUIDException(String message) {
super(message);
}
public UUIDException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,17 @@
package com.mingliqiye.disk.uuid;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public class UUIDJsonDeserializer extends JsonDeserializer<UUID> {
@Override
public UUID deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.isNaN()) {
return null;
}
return new UUID(p.getValueAsString());
}
}

View File

@ -0,0 +1,34 @@
package com.mingliqiye.disk.uuid;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import java.io.IOException;
public class UUIDJsonSerializer extends JsonSerializer<UUID> {
@Override
public void serialize(UUID uuid, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws UUIDException, IOException {
if (uuid == null) {
jsonGenerator.writeNull();
return;
}
jsonGenerator.writeString(uuid.toUUIDString());
}
@Override
public void serializeWithType(
UUID value,
JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer
) throws IOException {
WritableTypeId typeId = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_STRING));
serialize(value, gen, serializers);
typeSer.writeTypeSuffix(gen, typeId);
}
}

View File

@ -0,0 +1,90 @@
config:
port: 9963
app-name: maven-repository
app-version: 1.0
data-source:
mysql:
username: pan
password: PhXCRiCfEmiGesEm
host: 10.0.0.4
port: 3307
database: pan
redis:
host: 10.0.0.4
port: 6380
database: 0
username: ''
password: redis_kTJpsa
spring:
application:
name: ${config.app-name}
version: ${config.app-version}
banner:
location: classpath:banner/banner.txt
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${config.data-source.mysql.host}:${config.data-source.mysql.port}/${config.data-source.mysql.database}?rewriteBatchedStatements=true
username: ${config.data-source.mysql.username}
password: ${config.data-source.mysql.password}
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 0
min-idle: 0
maxActive: 1024
connect-timeout: 1000
data:
redis:
host: ${config.data-source.redis.host}
port: ${config.data-source.redis.port}
database: ${config.data-source.redis.database}
password: ${config.data-source.redis.password}
username: ${config.data-source.redis.username}
connect-timeout: 5s
timeout: 5s
lettuce:
pool:
min-idle: 0
max-idle: 16
max-active: 16
max-wait: 5s
mvc:
static-path-pattern: /static/**
web:
resources:
static-locations: classpath:/html/static
server:
port: ${config.port}
sa-token:
token-name: Authorization
timeout: 2592000
active-timeout: -1
is-concurrent: true
is-share: false
token-style: random-128
is-log: false
jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij
token-prefix: Bearer
is-read-body: false
is-read-cookie: false
mybatis-plus:
type-handlers-package: com.mingliqiye.disk.typeHandlers
configuration:
cache-enabled: true
global-config:
db-config:
id-type: auto
springdoc:
swagger-ui:
path: /apis/swagger
api-docs:
path: /apis/swagger/api.json

View File

@ -0,0 +1,6 @@
__ __ _____ _ _ _____ _ _____ ____ _____ __ __ ______
| \/ | |_ _| | \ | | / ____| | | |_ _| / __ \ |_ _| \ \ / / | ____|
| \ / | | | | \| | | | __ | | | | | | | | | | \ \_/ / | |__
| |\/| | | | | . ` | | | |_ | | | | | | | | | | | \ / | __|
| | | | _| |_ | |\ | | |__| | | |____ _| |_ | |__| | _| |_ | | | |____
|_| |_| |_____| |_| \_| \_____| |______| |_____| \___\_\ |_____| |_| |______|

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link href="/static/svg/BIklAcGo.svg" rel="icon">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Vite App</title>
<script type="module" crossorigin src="/static/js/CcCh4Sgi.js"></script>
<link rel="stylesheet" crossorigin href="/static/css/B-HidlUH.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@ -0,0 +1 @@
:root{--vt-c-white:#fff;--vt-c-white-soft:#f8f8f8;--vt-c-white-mute:#f2f2f2;--vt-c-black:#181818;--vt-c-black-soft:#222;--vt-c-black-mute:#282828;--vt-c-indigo:#2c3e50;--vt-c-divider-light-1:#3c3c3c4a;--vt-c-divider-light-2:#3c3c3c1f;--vt-c-divider-dark-1:#545454a6;--vt-c-divider-dark-2:#5454547a;--vt-c-text-light-1:var(--vt-c-indigo);--vt-c-text-light-2:#3c3c3ca8;--vt-c-text-dark-1:var(--vt-c-white);--vt-c-text-dark-2:#ebebeba3;--color-background:var(--vt-c-white);--color-background-soft:var(--vt-c-white-soft);--color-background-mute:var(--vt-c-white-mute);--color-border:var(--vt-c-divider-light-2);--color-border-hover:var(--vt-c-divider-light-1);--color-heading:var(--vt-c-text-light-1);--color-text:var(--vt-c-text-light-1);--section-gap:160px}@media (prefers-color-scheme:dark){:root{--color-background:var(--vt-c-black);--color-background-soft:var(--vt-c-black-soft);--color-background-mute:var(--vt-c-black-mute);--color-border:var(--vt-c-divider-dark-2);--color-border-hover:var(--vt-c-divider-dark-1);--color-heading:var(--vt-c-text-dark-1);--color-text:var(--vt-c-text-dark-2)}}*,:before,:after{box-sizing:border-box;margin:0;font-weight:400}body{min-height:100vh;color:var(--color-text);background:var(--color-background);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:15px;line-height:1.6;transition:color .5s,background-color .5s}#app{max-width:1280px;margin:0 auto;padding:2rem;font-weight:400}a,.green{color:#00bd7e;padding:3px;text-decoration:none;transition:all .4s}@media (hover:hover){a:hover{background-color:#00bd7e33}}@media (min-width:1024px){body{place-items:center;display:flex}#app{grid-template-columns:1fr 1fr;padding:0 2rem;display:grid}}h1[data-v-bed32675]{font-size:2.6rem;font-weight:500;position:relative;top:-10px}h3[data-v-bed32675]{font-size:1.2rem}.greetings h1[data-v-bed32675],.greetings h3[data-v-bed32675]{text-align:center}@media (min-width:1024px){.greetings h1[data-v-bed32675],.greetings h3[data-v-bed32675]{text-align:left}}header[data-v-608be0ed]{max-height:100vh;line-height:1.5}.logo[data-v-608be0ed]{margin:0 auto 2rem;display:block}nav[data-v-608be0ed]{text-align:center;width:100%;margin-top:2rem;font-size:12px}nav a.router-link-exact-active[data-v-608be0ed]{color:var(--color-text)}nav a.router-link-exact-active[data-v-608be0ed]:hover{background-color:#0000}nav a[data-v-608be0ed]{border-left:1px solid var(--color-border);padding:0 1rem;display:inline-block}nav a[data-v-608be0ed]:first-of-type{border:0}@media (min-width:1024px){header[data-v-608be0ed]{padding-right:calc(var(--section-gap)/2);place-items:center;display:flex}.logo[data-v-608be0ed]{margin:0 2rem 0 0}header .wrapper[data-v-608be0ed]{flex-wrap:wrap;place-items:flex-start;display:flex}nav[data-v-608be0ed]{text-align:left;margin-top:1rem;margin-left:-1rem;padding:1rem 0;font-size:1rem}}.item[data-v-5369c01e]{margin-top:2rem;display:flex;position:relative}.details[data-v-5369c01e]{flex:1;margin-left:1rem}i[data-v-5369c01e]{width:32px;height:32px;color:var(--color-text);place-content:center;place-items:center;display:flex}h3[data-v-5369c01e]{color:var(--color-heading);margin-bottom:.4rem;font-size:1.2rem;font-weight:500}@media (min-width:1024px){.item[data-v-5369c01e]{padding:.4rem 0 1rem calc(var(--section-gap)/2);margin-top:0}i[data-v-5369c01e]{border:1px solid var(--color-border);background:var(--color-background);border-radius:8px;width:50px;height:50px;position:absolute;top:calc(50% - 25px);left:-26px}.item[data-v-5369c01e]:before{content:" ";border-left:1px solid var(--color-border);height:calc(50% - 25px);position:absolute;bottom:calc(50% + 25px);left:0}.item[data-v-5369c01e]:after{content:" ";border-left:1px solid var(--color-border);height:calc(50% - 25px);position:absolute;top:calc(50% + 25px);left:0}.item[data-v-5369c01e]:first-of-type:before,.item[data-v-5369c01e]:last-of-type:after{display:none}}

View File

@ -0,0 +1 @@
@media (min-width:1024px){.about{align-items:center;min-height:100vh;display:flex}}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
import {
__plugin_vue_export_helper_default as e,
createBaseVNode as t,
createElementBlock as n,
openBlock as r,
} from './CcCh4Sgi.js';
const i = {},
a = { class: `about` };
function o(e, i) {
return r(), n(`div`, a, (i[0] ||= [t(`h1`, null, `This is an about page`, -1)]));
}
var s = e(i, [[`render`, o]]);
export { s as default };

View File

@ -0,0 +1,9 @@
<svg viewBox="0 0 1650 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
data-spm-anchor-id="a313x.search_index.0.i10.3b363a81fRIAyG" width="256" height="256">
<path d="M1368.639733 314.94553a283.692506 283.692506 0 0 0-45.17515 3.532214 436.693118 436.693118 0 0 0-282.577071-295.032771 448.219288 448.219288 0 0 0-407.691787 63.022123 38.854347 38.854347 0 0 0-7.993956 55.771791 40.713407 40.713407 0 0 0 55.77179 7.80805 366.792474 366.792474 0 0 1 350.618655-46.290586 356.009928 356.009928 0 0 1 222.343537 269.563653 39.412065 39.412065 0 0 0 18.590597 27.142272 40.713407 40.713407 0 0 0 33.277168 3.718119 201.707975 201.707975 0 0 1 254.877081 122.697938 196.130796 196.130796 0 0 1-123.813374 249.671715 39.226159 39.226159 0 0 0-24.539588 50.194611 40.341595 40.341595 0 0 0 51.124141 23.98187 275.140832 275.140832 0 0 0 183.303284-306.558941 278.858951 278.858951 0 0 0-276.999891-229.036152z"
fill="#9aed7f"/>
<path d="M1368.639733 787.704406a238.703262 238.703262 0 0 1-240.748228 236.286485H240.934134a236.379438 236.379438 0 1 1 0-472.758876 151.69927 151.69927 0 0 1 22.494622 1.115436 402.858232 402.858232 0 0 1 788.984926 10.782546 244.652253 244.652253 0 0 1 217.13817 33.277168 234.985143 234.985143 0 0 1 99.831505 191.483147z"
fill="#9aed7f"/>
<path d="M1100.377422 632.472923a156.532825 156.532825 0 0 0-23.05234 5.763085l-43.873809 13.942948a40.713407 40.713407 0 0 1-33.277168-3.71812 39.412065 39.412065 0 0 1-18.590596-27.142271L973.589552 576.329321a322.175042 322.175042 0 0 0-632.08029-8.737581l-7.064426 34.764416a39.969783 39.969783 0 0 1-42.386561 31.232203l-35.879852-3.346308h-12.827512a157.64826 157.64826 0 1 0-2.974495 315.110615h3.346307l81.61272-1.301341v1.301341h799.395661a161.552286 161.552286 0 0 0 163.411345-151.141551 156.161013 156.161013 0 0 0-54.470448-125.114716 162.667722 162.667722 0 0 0-133.480485-37.181194z"
fill="#cdf8bf"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,29 @@
Appenders:
Console: #输出到控制台
name: CONSOLE #Appender命名
target: SYSTEM_OUT
PatternLayout:
pattern: "%style{%d{yyyy-MM-dd HH:mm:ss,SSS}}{bright,magenta} [%highlight{%p}{FATAL=white, ERROR=bright_red, WARN=bright_yellow, INFO=bright_green, DEBUG=bright_cyan, TRACE=bright_blue}] [%style{%t}{bright,yellow}/%style{%c{1}}{bright,cyan}] -- %style{%m}{#EEDFCC}%n" #输出日志的格式
disableAnsi: "${env:DISABLECOLOR:-false}"
RollingFile:
- name: FILE
fileName: 'log/info.log'
filePattern: "log/$${date:yyyy-MM-dd}/%d{yyyy-MM-dd}-%i.log"
PatternLayout:
pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS} [%p] [%t/%F:%L/%M/%c] -- %m%n" #输出日志的格式
Policies:
SizeBasedTriggeringPolicy:
size: "10 KB"
TimeBasedTriggeringPolicy:
modulate: true
interval: 1
DefaultRolloverStrategy: # 单目录下文件最多20个超过会删除最早之前的
max: 1000
Loggers:
Root:
level: INFO
additivity: true
AppenderRef:
- ref: CONSOLE
- ref: FILE

View File

@ -0,0 +1,3 @@
create database meven_repository collate utf8mb4_unicode_ci;
create user 'meven_repository_admin'@'%' IDENTIFIED BY 'meven_repository_admin_password';
GRANT ALL ON meven_repository.* TO 'meven_repository_admin'@'%'

View File

@ -0,0 +1,18 @@
DROP TABLE IF EXISTS `users`;
create table `users`
(
id binary(16) default (uuid_to_bin(
uuid(),
1)) primary key,
username varchar(256) unique not null,
password varchar(128) not null,
nickname varchar(256) not null,
prermissions json,
roles json,
icon mediumblob,
admin bool default false,
creation_time timestamp(6) default current_timestamp(6) not null,
update_time timestamp(6) null
);
INSERT INTO users (id, username, password, nickname, prermissions, roles, icon, admin)
VALUES (0x689EFC204D1F11F08134DB0063E177A7, 'admin', 'admin', '管理员', '[]', '[]', null, 1);

View File

@ -1,11 +1,26 @@
package com.mingliqiye.disk;
import com.mingliqiye.disk.mappers.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@Slf4j
@SpringBootTest
class DiskApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {}
void contextLoads() {
log.info("{}", userMapper.selectList(null));
}
@Test
void getBCryptPdw() {
log.info("{}", BCrypt.hashpw("admin", BCrypt.gensalt()));
}
}

View File

@ -13,13 +13,13 @@ export default defineConfig({
},
},
build: {
outDir: path.resolve(__dirname, 'src/main/resources/static'),
outDir: path.resolve(__dirname, 'src/main/resources/html'),
emptyOutDir: true,
rollupOptions: {
output: {
chunkFileNames: 'js/[hash].js',
entryFileNames: 'js/[hash].js',
assetFileNames: '[ext]/[hash].[ext]',
chunkFileNames: 'static/js/[hash].js',
entryFileNames: 'static/js/[hash].js',
assetFileNames: 'static/[ext]/[hash].[ext]',
},
},
},

View File

@ -1,11 +1,11 @@
<script setup lang="ts">
<script lang="ts" setup>
import { RouterLink, RouterView } from 'vue-router';
import HelloWorld from './components/HelloWorld.vue';
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<img alt="Vue logo" class="logo" height="125" src="@/assets/icon.svg" width="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />

9
web-src/assets/icon.svg Normal file
View File

@ -0,0 +1,9 @@
<svg viewBox="0 0 1650 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
data-spm-anchor-id="a313x.search_index.0.i10.3b363a81fRIAyG" width="256" height="256">
<path d="M1368.639733 314.94553a283.692506 283.692506 0 0 0-45.17515 3.532214 436.693118 436.693118 0 0 0-282.577071-295.032771 448.219288 448.219288 0 0 0-407.691787 63.022123 38.854347 38.854347 0 0 0-7.993956 55.771791 40.713407 40.713407 0 0 0 55.77179 7.80805 366.792474 366.792474 0 0 1 350.618655-46.290586 356.009928 356.009928 0 0 1 222.343537 269.563653 39.412065 39.412065 0 0 0 18.590597 27.142272 40.713407 40.713407 0 0 0 33.277168 3.718119 201.707975 201.707975 0 0 1 254.877081 122.697938 196.130796 196.130796 0 0 1-123.813374 249.671715 39.226159 39.226159 0 0 0-24.539588 50.194611 40.341595 40.341595 0 0 0 51.124141 23.98187 275.140832 275.140832 0 0 0 183.303284-306.558941 278.858951 278.858951 0 0 0-276.999891-229.036152z"
fill="#9aed7f"/>
<path d="M1368.639733 787.704406a238.703262 238.703262 0 0 1-240.748228 236.286485H240.934134a236.379438 236.379438 0 1 1 0-472.758876 151.69927 151.69927 0 0 1 22.494622 1.115436 402.858232 402.858232 0 0 1 788.984926 10.782546 244.652253 244.652253 0 0 1 217.13817 33.277168 234.985143 234.985143 0 0 1 99.831505 191.483147z"
fill="#9aed7f"/>
<path d="M1100.377422 632.472923a156.532825 156.532825 0 0 0-23.05234 5.763085l-43.873809 13.942948a40.713407 40.713407 0 0 1-33.277168-3.71812 39.412065 39.412065 0 0 1-18.590596-27.142271L973.589552 576.329321a322.175042 322.175042 0 0 0-632.08029-8.737581l-7.064426 34.764416a39.969783 39.969783 0 0 1-42.386561 31.232203l-35.879852-3.346308h-12.827512a157.64826 157.64826 0 1 0-2.974495 315.110615h3.346307l81.61272-1.301341v1.301341h799.395661a161.552286 161.552286 0 0 0 163.411345-151.141551 156.161013 156.161013 0 0 0-54.470448-125.114716 162.667722 162.667722 0 0 0-133.480485-37.181194z"
fill="#cdf8bf"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 276 B

View File

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.ts"></script>
</body>
<head>
<meta charset="UTF-8">
<link href="./assets/icon.svg" rel="icon">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script src="/main.ts" type="module"></script>
</body>
</html>