第一个版本
This commit is contained in:
commit
59fde72a18
136
pom.xml
Normal file
136
pom.xml
Normal file
@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.lion</groupId>
|
||||
<artifactId>LionWebsite</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>LionWebsite</name>
|
||||
<description>LionWebsite</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter-test</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.15.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.11</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.im4java</groupId>
|
||||
<artifactId>im4java</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.21</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.86.Final</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,18 @@
|
||||
package com.lion.lionwebsite.Configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowCredentials(true)
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE")
|
||||
.allowedHeaders("*")
|
||||
.exposedHeaders("*");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.lion.lionwebsite.Configuration;
|
||||
|
||||
import com.lion.lionwebsite.Domain.MaskDomain;
|
||||
import com.lion.lionwebsite.Interceptor.HumanInterceptor;
|
||||
import com.lion.lionwebsite.Interceptor.PersonalInterceptor;
|
||||
import com.lion.lionwebsite.Interceptor.TaskHandlerInterceptor;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class InterceptorConfiguration implements WebMvcConfigurer {
|
||||
@Resource
|
||||
TaskHandlerInterceptor taskHandlerInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(getPersonalInterceptor()).addPathPatterns("/personal/**", "/remote/**");
|
||||
registry.addInterceptor(taskHandlerInterceptor).addPathPatterns("/GalleryManage", "/validate");
|
||||
registry.addInterceptor(getHumanInterceptor()).addPathPatterns("/", "/mobile");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HandlerInterceptor getPersonalInterceptor(){
|
||||
return new PersonalInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HandlerInterceptor getHumanInterceptor(){
|
||||
return new HumanInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CustomUtil getParameterUtil(){
|
||||
CustomUtil customUtil = new CustomUtil();
|
||||
MaskDomain[] maskDomains = new MaskDomain[2];
|
||||
maskDomains[0] = new MaskDomain("exhentai.org", "element-plus.org");
|
||||
maskDomains[1] = new MaskDomain("e-hentai.org", "element.org");
|
||||
customUtil.setMaskDomains(maskDomains);
|
||||
return customUtil;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package com.lion.lionwebsite.Controller;
|
||||
|
||||
import com.lion.lionwebsite.Service.CollectService;
|
||||
import com.lion.lionwebsite.Service.GalleryManageService;
|
||||
import com.lion.lionwebsite.Service.UserServiceImpl;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/GalleryManage")
|
||||
@Slf4j
|
||||
public class GalleryManageController {
|
||||
@Resource
|
||||
GalleryManageService galleryManageService;
|
||||
|
||||
@Resource
|
||||
CollectService collectService;
|
||||
|
||||
@Resource
|
||||
CustomUtil customUtil;
|
||||
|
||||
@Resource
|
||||
UserServiceImpl userService;
|
||||
|
||||
@PostMapping("")
|
||||
public String create_task(String link, String targetResolution, String AuthCode,
|
||||
@RequestParam(value = "tags", required = false) List<Integer> tags,
|
||||
@RequestParam(value = "mode", defaultValue = "3", required = false)String mode){
|
||||
if(link == null || targetResolution == null)
|
||||
return Response._failure("参数不全");
|
||||
link = customUtil.restoreUrl(link);
|
||||
if(link == null)
|
||||
return Response._failure("链接错误");
|
||||
return galleryManageService.createTask(link, targetResolution, AuthCode, tags, Byte.parseByte(mode));
|
||||
}
|
||||
|
||||
@PostMapping("/update")
|
||||
public String updateGallery(String link){
|
||||
return galleryManageService.updateGallery(link);
|
||||
}
|
||||
|
||||
@GetMapping("")
|
||||
public String selectGallery(String param, String type, String AuthCode) {
|
||||
int userId = userService.getUserId(AuthCode); //能调到这里的授权码对应用户不可能为空
|
||||
|
||||
if(type == null)
|
||||
return Response._failure("参数不全");
|
||||
switch (type) {
|
||||
case "link" -> {
|
||||
param = customUtil.restoreUrl(param);
|
||||
if (param == null)
|
||||
return Response._failure("链接错误");
|
||||
return galleryManageService.selectTaskByLink(param);
|
||||
}
|
||||
case "gid" -> {
|
||||
return galleryManageService.selectTaskByGid(Integer.parseInt(param));
|
||||
}
|
||||
case "all" -> {
|
||||
return galleryManageService.selectAllGallery(userId);
|
||||
}
|
||||
case "name" -> {
|
||||
return galleryManageService.selectGalleryByName(param);
|
||||
}
|
||||
case "undone" -> {
|
||||
return galleryManageService.selectUnDoneGallery();
|
||||
}
|
||||
case "downloader" -> {
|
||||
return galleryManageService.selectGalleryByDownloader(AuthCode);
|
||||
}
|
||||
default -> {
|
||||
return Response._default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("")
|
||||
public String deleteTask(Integer gid, String AuthCode, @RequestParam(value = "mode", defaultValue = "3", required = false)String mode){
|
||||
if(gid == null)
|
||||
return Response._failure("参数不全");
|
||||
|
||||
return galleryManageService.deleteGalleryByGid(gid, AuthCode, Byte.parseByte(mode));
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/collect")
|
||||
public String collectGallery(Integer gid, String AuthCode){
|
||||
return collectService.collectGallery(gid, userService.getUserId(AuthCode));
|
||||
}
|
||||
|
||||
@PostMapping("/disCollect")
|
||||
public String disCollectGallery(Integer gid, String AuthCode){
|
||||
return collectService.disCollectGallery(gid, userService.getUserId(AuthCode));
|
||||
}
|
||||
|
||||
@GetMapping("/weekUsedAmount")
|
||||
public String getWeekUsedAmount(){
|
||||
return galleryManageService.getWeekUsedAmount();
|
||||
}
|
||||
|
||||
@GetMapping("/thumbnail/{name}")
|
||||
public void getThumbnail(HttpServletRequest request, HttpServletResponse response, @PathVariable("name") String name){
|
||||
galleryManageService.getThumbnail(request, response, name.replace(".webp", ""));
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/onlineImage/{page}")
|
||||
public void getOnlineImage(Integer gid, @PathVariable("page") Short page, HttpServletRequest request, HttpServletResponse response){
|
||||
galleryManageService.getOnlineImage(gid, page, request, response);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/share")
|
||||
public String shareGallery(Integer gid, Integer userId, Integer expireHour){
|
||||
if(userId.equals(3))
|
||||
return galleryManageService.shareGallery(gid, expireHour);
|
||||
return Response._failure("非法访问");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.lion.lionwebsite.Controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class NavigationController {
|
||||
@GetMapping("/personal/")
|
||||
public String personal_index(){
|
||||
return "self";
|
||||
}
|
||||
|
||||
@GetMapping("/personal/mobile")
|
||||
public String personal_mobile(){
|
||||
return "selfMobile";
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(){
|
||||
return "index";
|
||||
}
|
||||
|
||||
@GetMapping("/mobile")
|
||||
public String mobile(){
|
||||
return "mobile";
|
||||
}
|
||||
|
||||
@GetMapping("/publish/")
|
||||
public String publish_index(){
|
||||
return "publish";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package com.lion.lionwebsite.Controller;
|
||||
|
||||
import com.lion.lionwebsite.Service.LocalServiceImpl;
|
||||
import com.lion.lionwebsite.Service.PersonalServiceImpl;
|
||||
import com.lion.lionwebsite.Util.FileDownload;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequestMapping("/personal")
|
||||
public class PersonalController {
|
||||
|
||||
@Resource
|
||||
PersonalServiceImpl personalService;
|
||||
|
||||
@Resource
|
||||
LocalServiceImpl localService;
|
||||
|
||||
@GetMapping("/sub/self")
|
||||
public void sub(HttpServletResponse response, HttpServletRequest request){
|
||||
FileDownload.export(request, response, "sub/sub.txt");
|
||||
}
|
||||
|
||||
@GetMapping("/files")
|
||||
public String file(String path) throws IOException {
|
||||
return personalService.getFiles(path);
|
||||
}
|
||||
|
||||
@PostMapping("/uploadFile")
|
||||
public String uploadFile(String path, String fileName, MultipartFile file){
|
||||
return personalService.uploadFile(path, fileName, file);
|
||||
}
|
||||
|
||||
@GetMapping("/private/**")
|
||||
public void getFile(HttpServletRequest request, HttpServletResponse response, String path){
|
||||
personalService.download(request, response, path);
|
||||
}
|
||||
|
||||
@PostMapping("/share")
|
||||
public String shareFile(String path, Integer expireHour) {
|
||||
return personalService.shareFile(path, expireHour);
|
||||
}
|
||||
|
||||
@PostMapping("/compress")
|
||||
public String compress(String path){
|
||||
return personalService.compress(path);
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
public String deleteFile(String path){
|
||||
return personalService.deleteFile(path);
|
||||
}
|
||||
|
||||
@PostMapping("/extendShareTime")
|
||||
public String extendShareTime(String path, Integer extendHour) throws JsonProcessingException {
|
||||
return personalService.extendShareTime(path, extendHour);
|
||||
}
|
||||
|
||||
@PostMapping("/cancelShare")
|
||||
public String cancelShare(String path){
|
||||
return personalService.cancelShare(path);
|
||||
}
|
||||
|
||||
@PostMapping("/updateSub")
|
||||
public String updateSub() throws IOException {
|
||||
Response response = Response.generateResponse();
|
||||
if(localService.updateSub(true))
|
||||
response.success();
|
||||
else
|
||||
response.failure();
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
@GetMapping("/lastUpdate")
|
||||
public String lastUpdate(){
|
||||
return personalService.lastUpdate();
|
||||
}
|
||||
|
||||
@GetMapping("/ip")
|
||||
public String ip() throws JsonProcessingException {
|
||||
return personalService.getIp();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package com.lion.lionwebsite.Controller;
|
||||
|
||||
|
||||
import com.lion.lionwebsite.Domain.User;
|
||||
import com.lion.lionwebsite.Service.PublicServiceImpl;
|
||||
import com.lion.lionwebsite.Util.FileDownload;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class PublicController {
|
||||
|
||||
final List<String> black_share_codes = new LinkedList<>();
|
||||
|
||||
@Resource
|
||||
PublicServiceImpl publicService;
|
||||
|
||||
@GetMapping("/ip")
|
||||
public void ip(HttpServletRequest request, String auth, HttpServletResponse response) throws IOException {
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if(ip == null)
|
||||
ip = request.getRemoteAddr();
|
||||
|
||||
if(auth != null && auth.equals("ip"))
|
||||
publicService.logIpAddress(ip);
|
||||
|
||||
log.info("ip地址:" + ip + " auth=" + auth);
|
||||
response.getOutputStream().write(ip.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@GetMapping("/sub/{client}/{AuthCode}")
|
||||
public void publicSub(@PathVariable("AuthCode") String AuthCode,
|
||||
@PathVariable("client") String client,
|
||||
HttpServletResponse response,
|
||||
HttpServletRequest request) throws IOException {
|
||||
|
||||
if(AuthCode == null || client == null || !AuthCode.equals("covid"))
|
||||
return;
|
||||
|
||||
switch (client){
|
||||
case "v2ray" -> FileDownload.export(request, response, "sub/DouNaiV2ray.txt");
|
||||
case "clash" -> FileDownload.export(request, response, "sub/DouNaiClash.txt");
|
||||
default -> response.getOutputStream().write("client error".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/GetFile/{path}")
|
||||
public void getFile(HttpServletRequest request, HttpServletResponse response, String ShareCode, @PathVariable("path") String path) throws IOException {
|
||||
synchronized (black_share_codes) {
|
||||
if (black_share_codes.contains(ShareCode)) {
|
||||
String ip = request.getHeader("X-Forwarded-For") == null ? request.getRemoteAddr() : request.getHeader("X-Forwarded-For");
|
||||
log.info("dispatch request ip:{} file:{}", ip, path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log.info("ShareCode:{}", ShareCode);
|
||||
log.info("Path:{}", path);
|
||||
boolean result = publicService.GetFile(request, response, ShareCode);
|
||||
|
||||
if(!result)
|
||||
black_share_codes.add(ShareCode);
|
||||
|
||||
if(black_share_codes.size() > 100)
|
||||
black_share_codes.remove(0);
|
||||
}
|
||||
|
||||
@PostMapping("/validate")
|
||||
public String validate(String AuthCode){
|
||||
Response response = Response.generateResponse();
|
||||
User user = publicService.getUserId(AuthCode);
|
||||
response.success(String.format("{\"userId\": %d, \"username\": \"%s\"}", user.getId(), user.getUsername()));
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
@PutMapping("/AuthCode")
|
||||
public String alterAuthCode(String AuthCode, String newAuthCode){
|
||||
return publicService.alterAuthCode(AuthCode, newAuthCode);
|
||||
}
|
||||
|
||||
@GetMapping("/GalleryManage/ehThumbnail")
|
||||
public void getEhThumbnail(String path, HttpServletResponse response){
|
||||
publicService.getEhThumbnail(path, response);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.lion.lionwebsite.Controller;
|
||||
|
||||
import com.lion.lionwebsite.Service.QueryService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/query")
|
||||
public class QueryController {
|
||||
|
||||
@Resource
|
||||
QueryService queryService;
|
||||
|
||||
|
||||
@GetMapping("")
|
||||
public String query(String keyword, String prev, String next){
|
||||
return queryService.query(keyword, prev, next);
|
||||
}
|
||||
|
||||
@GetMapping("/image")
|
||||
public void image(HttpServletResponse response, String path){
|
||||
queryService.image(response, path);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.lion.lionwebsite.Controller;
|
||||
|
||||
import com.lion.lionwebsite.Service.TagService;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/GalleryManage")
|
||||
public class TagController {
|
||||
|
||||
@Resource
|
||||
TagService tagService;
|
||||
|
||||
@PostMapping("/tag")
|
||||
public String createTag(String tag){
|
||||
return tagService.createTag(tag);
|
||||
}
|
||||
|
||||
@PostMapping("/tagAndMark")
|
||||
public String createTagAndMark(Integer gid, String tag){
|
||||
return tagService.createTagAndMark(gid, tag);
|
||||
}
|
||||
|
||||
@DeleteMapping("/tag")
|
||||
public String deleteTag(Integer tid){
|
||||
return tid == null ? Response._failure("参数错误") : tagService.deleteTag(tid);
|
||||
}
|
||||
|
||||
@PostMapping("/mark")
|
||||
public String markTag(Integer gid, Integer tid){
|
||||
return tagService.markTag(gid, tid);
|
||||
}
|
||||
|
||||
@PostMapping("/disMark")
|
||||
public String disMarkTag(Integer gid, Integer tid){
|
||||
return tagService.disMarkTag(gid, tid);
|
||||
}
|
||||
|
||||
@GetMapping("/allTag")
|
||||
public String allTag(){
|
||||
return tagService.selectAllTag();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.lion.lionwebsite.Controller;
|
||||
|
||||
import com.lion.lionwebsite.Service.UserServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/personal/user")
|
||||
public class UserController {
|
||||
@Resource
|
||||
UserServiceImpl userService;
|
||||
|
||||
@GetMapping("")
|
||||
public String getAllUser(){
|
||||
return userService.getAllUser();
|
||||
}
|
||||
|
||||
@PostMapping("")
|
||||
public String addAuthCode(String targetAuthCode, String username){
|
||||
return userService.addAuthCode(targetAuthCode, username);
|
||||
}
|
||||
|
||||
@PutMapping("/AuthCode")
|
||||
public String alterAuthCode(String targetAuthCode, String newAuthCode){
|
||||
return userService.alterAuthCode(targetAuthCode, newAuthCode);
|
||||
}
|
||||
|
||||
@PutMapping("/Username")
|
||||
public String alterUsername(String targetAuthCode, String newUsername){
|
||||
return userService.alterUsername(targetAuthCode, newUsername);
|
||||
}
|
||||
|
||||
@DeleteMapping ("")
|
||||
public String deleteAuthCode(String targetAuthCode){
|
||||
return userService.deleteAuthCode(targetAuthCode);
|
||||
}
|
||||
|
||||
@PutMapping("/status")
|
||||
public String alterStatus(String AuthCode, boolean isEnable){
|
||||
return userService.alterStatus(AuthCode, isEnable);
|
||||
}
|
||||
}
|
||||
26
src/main/java/com/lion/lionwebsite/Dao/CRC32CacheMapper.java
Normal file
26
src/main/java/com/lion/lionwebsite/Dao/CRC32CacheMapper.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import com.lion.lionwebsite.Domain.CRC32Cache;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Insert;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface CRC32CacheMapper {
|
||||
@Insert("insert into CRC32Cache (path, lastModifyTime, crc32) values (#{path}, #{lastModifyTime}, #{crc32})")
|
||||
void insertCache(CRC32Cache crc32Cache);
|
||||
|
||||
@Select("select * from CRC32Cache where path=#{path}")
|
||||
CRC32Cache selectCache(String path);
|
||||
|
||||
@Select("select * from CRC32Cache where path like '%' || #{name} || '%'")
|
||||
CRC32Cache selectCacheByName(String name);
|
||||
|
||||
// @Update("update CRC32Cache set lastModifyTime=#{lastModifyTime}, crc32=#{crc32} where path=#{path}")
|
||||
// void updateCache(CRC32Cache crc32Cache);
|
||||
|
||||
@Delete("delete from CRC32Cache where path=#{path}")
|
||||
void deleteCacheByPath(String path);
|
||||
|
||||
}
|
||||
23
src/main/java/com/lion/lionwebsite/Dao/CollectMapper.java
Normal file
23
src/main/java/com/lion/lionwebsite/Dao/CollectMapper.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Mapper
|
||||
public interface CollectMapper {
|
||||
@Insert("insert into collect (gid, collector) values (#{gid}, #{collector})")
|
||||
void collect(@Param("gid") int gid, @Param("collector") int collector);
|
||||
|
||||
@Delete("delete from collect where collector=#{collector} and gid=#{gid}")
|
||||
void disCollect(@Param("gid") int gid, @Param("collector") int collector);
|
||||
|
||||
@Select("select count(id) from collect where collector=#{collector} and gid=#{gid}")
|
||||
int isCollect(@Param("gid") int gid, @Param("collector") int collector);
|
||||
|
||||
@Select("select gid from collect where collector=#{collector}")
|
||||
ArrayList<Integer> selectGidByCollector(int collector);
|
||||
|
||||
@Select("select collector from collect where gid=#{gid}")
|
||||
ArrayList<Integer> selectCollectorByGid(int gid);
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import com.lion.lionwebsite.Domain.CustomConfiguration;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
@Mapper
|
||||
public interface CustomConfigurationMapper {
|
||||
// @Insert("insert into customConfiguration values (#{parameter}, #{value})")
|
||||
// void insertConfiguration(CustomConfiguration configuration);
|
||||
|
||||
@Update("update customConfiguration set value=#{value} where parameter=#{parameter}")
|
||||
void updateConfiguration(@Param("parameter")String parameter, @Param("value")String value);
|
||||
|
||||
// @Delete("delete from customConfiguration where parameter=#{parameter}")
|
||||
// void deleteConfiguration(CustomConfiguration configuration);
|
||||
|
||||
@Select("select * from customConfiguration where parameter=#{parameter}")
|
||||
CustomConfiguration selectConfiguration(String parameter);
|
||||
}
|
||||
50
src/main/java/com/lion/lionwebsite/Dao/GalleryMapper.java
Normal file
50
src/main/java/com/lion/lionwebsite/Dao/GalleryMapper.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import com.lion.lionwebsite.Domain.Gallery;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
|
||||
@Mapper
|
||||
public interface GalleryMapper {
|
||||
|
||||
|
||||
@Insert("insert into gallery" +
|
||||
" (gid, name, link, language, pages, status, fileSize, createTime, proceeding, resolution, displayFileSize, downloader, mode)\n" +
|
||||
" values (#{gid}, #{name}, #{link}, #{language}, #{pages}, #{status}, #{fileSize}, #{createTime}, #{proceeding}, #{resolution}, #{displayFileSize}, #{downloader}, #{mode})")
|
||||
void insertGallery(Gallery gallery);
|
||||
|
||||
@Select("select * from gallery where link=#{link}")
|
||||
Gallery selectGalleryByLink(String link);
|
||||
|
||||
@Select("select * from gallery where gid=#{gid}")
|
||||
Gallery selectGalleryByGid(int gid);
|
||||
|
||||
@Select("select name from gallery where gid=#{gid}")
|
||||
String selectGalleryNameByGid(int gid);
|
||||
|
||||
@Select("select * from gallery where downloader=#{downloader}")
|
||||
Gallery[] selectGalleryByDownloader(int downloader);
|
||||
|
||||
@Select("select * from gallery where status in ('已提交', '下载中')")
|
||||
Gallery[] selectUnDoneGalleries();
|
||||
|
||||
@Select("select * from gallery")
|
||||
Gallery[] selectAllGallery();
|
||||
|
||||
@Select("select * from gallery where name like #{name} limit 1")
|
||||
Gallery selectGalleryByName(String name);
|
||||
|
||||
|
||||
@Update("""
|
||||
update gallery set name=#{name}, link=#{link}, language=#{language},
|
||||
pages=#{pages}, status=#{status}, fileSize=#{fileSize}, createTime=#{createTime},
|
||||
proceeding=#{proceeding}, resolution=#{resolution}, displayFileSize=#{displayFileSize}, mode=#{mode}
|
||||
where gid=#{gid}""")
|
||||
void updateGallery(Gallery gallery);
|
||||
|
||||
@Update("update gallery set downloader=#{downloader} where downloader=#{oldDownloader}")
|
||||
void updateGalleryDownloader(@Param("downloader") int downloader, @Param("oldDownloader") int oldDownloader);
|
||||
|
||||
@Delete("delete from gallery where gid=#{gid}")
|
||||
void deleteGalleryByGid(Integer gid);
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import com.lion.lionwebsite.Domain.PageNameCache;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
@Mapper
|
||||
public interface PageNameCacheMapper {
|
||||
|
||||
@Insert("insert into pageName (gid, page, pageName) values (#{gid}, #{page}, #{pageName})")
|
||||
void insertPageNameCache(PageNameCache pageNameCache);
|
||||
|
||||
|
||||
@Select("select pageName from pageName where gid=#{gid} and page=#{page}")
|
||||
String selectPageName(@Param("gid") int gid, @Param("page") short page);
|
||||
|
||||
|
||||
@Delete("delete from pageName where gid=#{gid}")
|
||||
void deletePageNameByGid(int gid);
|
||||
|
||||
@Delete("delete from pageName where gid=#{gid} and page=#{page}")
|
||||
void deletePageName(PageNameCache pageNameCache);
|
||||
}
|
||||
31
src/main/java/com/lion/lionwebsite/Dao/ShareFileMapper.java
Normal file
31
src/main/java/com/lion/lionwebsite/Dao/ShareFileMapper.java
Normal file
@ -0,0 +1,31 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import com.lion.lionwebsite.Domain.ShareFile;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
@Mapper
|
||||
public interface ShareFileMapper {
|
||||
@Insert("insert into ShareFile (ShareCode, FilePath, ExpireTime) values (#{ShareCode}, #{FilePath}, #{ExpireTime})")
|
||||
void insertShareFile(@Param("ShareCode")String ShareCode, @Param("FilePath")String FilePath, @Param("ExpireTime") Date ExpireTime);
|
||||
|
||||
@Select("select * from ShareFile where ShareCode=#{ShareCode}")
|
||||
ShareFile selectShareFileByShareCode(String ShareCode);
|
||||
|
||||
@Select("select * from ShareFile where FilePath=#{FilePath}")
|
||||
ShareFile selectShareFileByFilePath(String FilePath);
|
||||
|
||||
@Select("select * from ShareFile where FilePath like '%' || #{FilePath} || '%'")
|
||||
ArrayList<ShareFile> selectShareFilesByFilePath(String FilePath);
|
||||
|
||||
@Delete("delete from ShareFile where ShareCode=#{ShareCode}")
|
||||
void deleteShareFile(String ShareCode);
|
||||
|
||||
@Select("select * from ShareFile")
|
||||
ShareFile[] selectAllShareFile();
|
||||
|
||||
@Update("update ShareFile set ExpireTime=#{ExpireTime}, ShareCode=#{ShareCode} where FilePath=#{FilePath}")
|
||||
void updateShareFile(ShareFile ShareFile);
|
||||
}
|
||||
60
src/main/java/com/lion/lionwebsite/Dao/TagMapper.java
Normal file
60
src/main/java/com/lion/lionwebsite/Dao/TagMapper.java
Normal file
@ -0,0 +1,60 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import com.lion.lionwebsite.Domain.Tag;
|
||||
import com.lion.lionwebsite.Domain.TagMark;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Mapper
|
||||
public interface TagMapper {
|
||||
@Insert("insert into tag (tag) values (#{tag})")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
|
||||
void insertTag(Tag tag);
|
||||
|
||||
// @Select("select id, tag, usage from tag where id=#{id}")
|
||||
// Tag selectTagById(int id);
|
||||
//
|
||||
// @Select("select id from tag where tag=#{tag}")
|
||||
// int selectTidByTag(String tag);
|
||||
|
||||
@Select("select count(id) from tag where tag=#{tag}")
|
||||
int selectTagExistByTag(String tag);
|
||||
|
||||
@Select("select count(id) from tag where id=#{id}")
|
||||
int selectTagExistById(int id);
|
||||
|
||||
|
||||
@Select("select id, tag, usage from tag")
|
||||
ArrayList<Tag> selectAllTag();
|
||||
|
||||
@Delete("delete from tag where id=#{id}")
|
||||
void deleteTagById(int id);
|
||||
|
||||
@Select("select count(id) from galleryTag where tid=#{tid}")
|
||||
int selectTagUsage(int tid);
|
||||
|
||||
@Select("select count(id) from galleryTag where gid=#{gid} and tid=#{tid}")
|
||||
int selectIsMark(@Param("gid") int gid, @Param("tid") int tid);
|
||||
|
||||
@Select("select tid from galleryTag where gid=#{gid}")
|
||||
ArrayList<Integer> selectTagByGid(int gid);
|
||||
|
||||
@Insert("insert into galleryTag (gid, tid) values (#{gid}, #{tid})")
|
||||
int markTag(@Param("gid") int gid, @Param("tid") int tid);
|
||||
|
||||
@Delete("delete from galleryTag where gid=#{gid} and tid=#{tid}")
|
||||
int disMarkTag(@Param("gid") int gid, @Param("tid") int tid);
|
||||
|
||||
@Select("select gid, tid from galleryTag")
|
||||
ArrayList<TagMark> selectAllMark();
|
||||
|
||||
@Update("update tag set usage=usage+1 where id=#{tid}")
|
||||
void incrTagUsage(int tid);
|
||||
|
||||
@Update("update tag set usage=usage-1 where id=#{tid}")
|
||||
void decrTagUsage(int tid);
|
||||
|
||||
@Delete("delete from galleryTag where gid=#{gid}")
|
||||
void disMarkTagByGid(int gid);
|
||||
}
|
||||
40
src/main/java/com/lion/lionwebsite/Dao/UserMapper.java
Normal file
40
src/main/java/com/lion/lionwebsite/Dao/UserMapper.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.lion.lionwebsite.Dao;
|
||||
|
||||
import com.lion.lionwebsite.Domain.User;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
@Mapper
|
||||
public interface UserMapper {
|
||||
@Insert("insert into User (AuthCode, username) values (#{AuthCode}, #{username})")
|
||||
void insertUser(User user);
|
||||
|
||||
@Select("select * from User where AuthCode=#{AuthCode}")
|
||||
User selectUserByAuthCode(String AuthCode);
|
||||
|
||||
@Select("select AuthCode from User")
|
||||
String[] selectAllAuthCode();
|
||||
|
||||
@Select("select AuthCode from User")
|
||||
String[] selectEnableAuthCode();
|
||||
|
||||
@Select("select * from User")
|
||||
User[] selectAllUser();
|
||||
|
||||
@Update("update User set AuthCode=#{newAuthCode} where AuthCode=#{AuthCode}")
|
||||
void updateAuthCode(@Param("AuthCode") String AuthCode, @Param("newAuthCode") String newAuthCode);
|
||||
|
||||
@Update("update User set username=#{newUsername} where AuthCode=#{AuthCode}")
|
||||
void updateUsername(@Param("AuthCode") String AuthCode, @Param("newUsername") String newUsername);
|
||||
|
||||
@Update("update User set lastAccessTime=#{lastAccessTime} where AuthCode=#{AuthCode}")
|
||||
void updateLastAccessTime(@Param("lastAccessTime") String lastAccessTime, @Param("AuthCode") String AuthCode);
|
||||
|
||||
@Update("update User set isEnable=#{isEnable} where id=#{id}")
|
||||
void updateIsEnableById(@Param("id") int id, @Param("isEnable") boolean isEnable);
|
||||
|
||||
@Delete("delete from User where AuthCode=#{AuthCode}")
|
||||
void deleteUserByAuthCode(String AuthCode);
|
||||
|
||||
@Select("select count(AuthCode) from User where AuthCode=#{AuthCode}")
|
||||
int isExist(String AuthCode);
|
||||
}
|
||||
14
src/main/java/com/lion/lionwebsite/Domain/CRC32Cache.java
Normal file
14
src/main/java/com/lion/lionwebsite/Domain/CRC32Cache.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class CRC32Cache {
|
||||
String path;
|
||||
long lastModifyTime;
|
||||
String crc32;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
|
||||
public class CustomConfiguration {
|
||||
public static final String AUTH = "auth";
|
||||
public static final String LAST_UPDATE_SUB_TIME = "lastUpdateSubTime";
|
||||
public static final String CURRENT_IP_ADDRESS = "currentIpAddress";
|
||||
public static final String LAST_UPDATE_IP_ADDRESS_TIME = "lastUpdateIpAddressTime";
|
||||
public static final String WEEK_USED_AMOUNT = "weekUsedAmount";
|
||||
public static final String LAST_RESET_AMOUNT_TIME = "lastResetAmountTime";
|
||||
String parameter;
|
||||
String value;
|
||||
}
|
||||
70
src/main/java/com/lion/lionwebsite/Domain/Gallery.java
Normal file
70
src/main/java/com/lion/lionwebsite/Domain/Gallery.java
Normal file
@ -0,0 +1,70 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class Gallery {
|
||||
@JsonProperty("name")
|
||||
private String name; //本子名字
|
||||
|
||||
@JsonProperty("gid")
|
||||
private int gid; //gid
|
||||
|
||||
@JsonProperty("link")
|
||||
private String link; //本子链接
|
||||
|
||||
@JsonProperty("language")
|
||||
private String language; //本子语言
|
||||
|
||||
@JsonProperty("pages")
|
||||
private int pages; //本子页数
|
||||
|
||||
@JsonProperty("status")
|
||||
private String status; //本子当前状态
|
||||
|
||||
@JsonIgnore
|
||||
private long fileSize; //文件大小
|
||||
|
||||
@JsonProperty("fileSize")
|
||||
private String displayFileSize; //展示文件大小
|
||||
|
||||
@JsonProperty("createTime")
|
||||
private Long createTime; //创建时间
|
||||
|
||||
@JsonProperty("resolution")
|
||||
private String resolution; //分辨率
|
||||
|
||||
@JsonProperty("proceeding")
|
||||
private int proceeding; //进度
|
||||
|
||||
@JsonProperty("downloader")
|
||||
private int downloader; //下载人
|
||||
|
||||
@JsonProperty("collector")
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private String collector; //收藏人
|
||||
|
||||
@JsonProperty("isCollect") //是否收藏
|
||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||
private boolean isCollect;
|
||||
|
||||
@JsonProperty("tags")
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private ArrayList<Integer> tags; //标签
|
||||
|
||||
@JsonProperty("availableResolution")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private Map<String, String> availableResolution; //可选分辨率
|
||||
|
||||
@JsonProperty("mode")
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private byte mode; //rd 可读可下载 r可读 d可下载
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GalleryForQuery {
|
||||
|
||||
@JsonProperty("name")
|
||||
String name;
|
||||
|
||||
@JsonProperty("link")
|
||||
String link;
|
||||
|
||||
@JsonProperty("type")
|
||||
String type;
|
||||
|
||||
@JsonProperty("page")
|
||||
int page;
|
||||
|
||||
@JsonProperty("uploadTime")
|
||||
String uploadTime;
|
||||
|
||||
@JsonProperty("thumbnailUrl")
|
||||
String thumbnailUrl;
|
||||
}
|
||||
48
src/main/java/com/lion/lionwebsite/Domain/GalleryTask.java
Normal file
48
src/main/java/com/lion/lionwebsite/Domain/GalleryTask.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GalleryTask {
|
||||
|
||||
public static byte DOWNLOADING = 1;
|
||||
|
||||
public static byte DOWNLOAD_COMPLETE = 2;
|
||||
|
||||
public static byte DOWNLOAD_QUEUED = 3;
|
||||
|
||||
public static byte COMPRESS_COMPLETE = 4;
|
||||
|
||||
|
||||
public static byte DOWNLOAD_ALL = 3;
|
||||
public static byte DOWNLOAD_PREVIEW = 2;
|
||||
public static byte DOWNLOAD_SOURCE = 1;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private String name;
|
||||
|
||||
private int gid;
|
||||
|
||||
private int pages;
|
||||
|
||||
private byte status;
|
||||
|
||||
private int proceeding;
|
||||
|
||||
private byte type;
|
||||
|
||||
@JsonIgnore
|
||||
private String path;
|
||||
|
||||
@JsonIgnore
|
||||
public boolean is_download_complete(){
|
||||
return status == DOWNLOAD_COMPLETE;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean is_compress_complete(){
|
||||
return status == COMPRESS_COMPLETE;
|
||||
}
|
||||
}
|
||||
11
src/main/java/com/lion/lionwebsite/Domain/MaskDomain.java
Normal file
11
src/main/java/com/lion/lionwebsite/Domain/MaskDomain.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class MaskDomain {
|
||||
String raw;
|
||||
String mask;
|
||||
}
|
||||
16
src/main/java/com/lion/lionwebsite/Domain/PageNameCache.java
Normal file
16
src/main/java/com/lion/lionwebsite/Domain/PageNameCache.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PageNameCache {
|
||||
int gid;
|
||||
|
||||
short page;
|
||||
|
||||
String pageName;
|
||||
}
|
||||
12
src/main/java/com/lion/lionwebsite/Domain/ShareFile.java
Normal file
12
src/main/java/com/lion/lionwebsite/Domain/ShareFile.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class ShareFile {
|
||||
String ShareCode;
|
||||
String FilePath;
|
||||
Date ExpireTime;
|
||||
}
|
||||
14
src/main/java/com/lion/lionwebsite/Domain/Tag.java
Normal file
14
src/main/java/com/lion/lionwebsite/Domain/Tag.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Tag {
|
||||
int id;
|
||||
String tag;
|
||||
int usage;
|
||||
}
|
||||
11
src/main/java/com/lion/lionwebsite/Domain/TagMark.java
Normal file
11
src/main/java/com/lion/lionwebsite/Domain/TagMark.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class TagMark {
|
||||
int gid;
|
||||
int tid;
|
||||
}
|
||||
28
src/main/java/com/lion/lionwebsite/Domain/User.java
Normal file
28
src/main/java/com/lion/lionwebsite/Domain/User.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.lion.lionwebsite.Domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class User {
|
||||
@JsonProperty("id")
|
||||
int id;
|
||||
@JsonProperty("AuthCode")
|
||||
String AuthCode;
|
||||
@JsonProperty("username")
|
||||
String username;
|
||||
|
||||
@JsonProperty("lastAccessTime")
|
||||
String lastAccessTime;
|
||||
|
||||
@JsonProperty("isEnable")
|
||||
boolean isEnable;
|
||||
|
||||
public String getUniqueId(){
|
||||
return id < 10 ? "0" + id: "" + id;
|
||||
}
|
||||
}
|
||||
8
src/main/java/com/lion/lionwebsite/Error/ErrorCode.java
Normal file
8
src/main/java/com/lion/lionwebsite/Error/ErrorCode.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.lion.lionwebsite.Error;
|
||||
|
||||
public class ErrorCode {
|
||||
public static final byte IO_ERROR = 1;
|
||||
public static final byte FILE_NOT_FOUND = 2;
|
||||
|
||||
public static final byte COMPRESS_ERROR = 3;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.lion.lionwebsite.Exception;
|
||||
|
||||
public class ResolutionNotMatchException extends Exception{
|
||||
public ResolutionNotMatchException(String targetResolution) {
|
||||
super("TargetResolution " + targetResolution + " not exist");
|
||||
}
|
||||
}
|
||||
28
src/main/java/com/lion/lionwebsite/Filter/AccessFilter.java
Normal file
28
src/main/java/com/lion/lionwebsite/Filter/AccessFilter.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.lion.lionwebsite.Filter;
|
||||
|
||||
import com.lion.lionwebsite.Dao.UserMapper;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.annotation.WebFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@WebFilter(filterName = "AccessFilter", urlPatterns = {"/validate"})
|
||||
public class AccessFilter implements Filter {
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
String authCode = servletRequest.getParameter("AuthCode");
|
||||
|
||||
if(authCode == null)
|
||||
return;
|
||||
|
||||
userMapper.updateLastAccessTime(CustomUtil.now(), authCode);
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
}
|
||||
78
src/main/java/com/lion/lionwebsite/Filter/AdaptorFilter.java
Normal file
78
src/main/java/com/lion/lionwebsite/Filter/AdaptorFilter.java
Normal file
@ -0,0 +1,78 @@
|
||||
package com.lion.lionwebsite.Filter;
|
||||
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.annotation.WebFilter;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
|
||||
@WebFilter(filterName = "AdaptorFilter", urlPatterns = {"/", "/personal/"})
|
||||
public class AdaptorFilter implements Filter {
|
||||
|
||||
FileWriter writer;
|
||||
|
||||
AdaptorFilter(){
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
try {
|
||||
writer = new FileWriter(String.format("log/AccessLog_%s-%s-%s.log",
|
||||
calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH)));
|
||||
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 0 * * *")
|
||||
void changeDate(){
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
try {
|
||||
if(writer != null)
|
||||
writer.close();
|
||||
|
||||
writer = new FileWriter(String.format("log/AccessLog_%s-%s-%s.log",
|
||||
calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH)));
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
String UserAgent = request.getHeader("User-Agent");
|
||||
|
||||
//屏蔽UA为null的请求
|
||||
if(UserAgent == null)
|
||||
return;
|
||||
|
||||
String AuthCode = request.getParameter("AuthCode") == null ? "null" : request.getParameter("AuthCode");
|
||||
String ServletPath = request.getServletPath();
|
||||
String ip = request.getHeader("X-Forwarded-For") == null ? request.getRemoteAddr(): request.getHeader("X-Forwarded-For");
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
String now = String.format("%s:%s:%s", calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
|
||||
|
||||
|
||||
//日志
|
||||
System.out.printf("%s ip:%s \tpath:%s \tAuthCode:%s ua:%s\n", now, ip, ServletPath, AuthCode, UserAgent.length() > 61 ? UserAgent.substring(0, 60): UserAgent);
|
||||
writer.write(String.format("%s ip:%s \tpath:%s \tAuthCode:%s ua:%s\n", now, ip, ServletPath, AuthCode, UserAgent));
|
||||
writer.flush();
|
||||
|
||||
//如果是验证,则直接跳转
|
||||
if(ServletPath.equals("/validate"))
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
|
||||
//如果不是,则根据UA判断是否跳转
|
||||
else if ((UserAgent.contains("Android") || UserAgent.contains("iPhone")))
|
||||
if (ServletPath.equals("/personal/") && AuthCode.equals("alone"))
|
||||
response.sendRedirect("/mobile?AuthCode=alone");
|
||||
else
|
||||
response.sendRedirect("/mobile");
|
||||
else
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.lion.lionwebsite.Interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
|
||||
public class HumanInterceptor implements HandlerInterceptor {
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
|
||||
return request.getHeader("User-Agent") != null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.lion.lionwebsite.Interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
|
||||
public class PersonalInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
|
||||
return request.getParameter("AuthCode") != null && request.getParameter("AuthCode").equals("alone");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package com.lion.lionwebsite.Interceptor;
|
||||
|
||||
import com.lion.lionwebsite.Dao.UserMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
|
||||
@Component
|
||||
public class TaskHandlerInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
String[] AuthCodes = null;
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
|
||||
if(AuthCodes == null)
|
||||
AuthCodes = userMapper.selectAllAuthCode();
|
||||
|
||||
String auth = request.getParameter("AuthCode");
|
||||
|
||||
if(auth != null)
|
||||
for(String AuthCode: AuthCodes)
|
||||
if(auth.equals(AuthCode))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updateAuthCodes(){
|
||||
AuthCodes = userMapper.selectEnableAuthCode();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.lion.lionwebsite;
|
||||
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@ServletComponentScan(value = "com.lion.lionwebsite.Filter")
|
||||
public class LionWebsiteApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(LionWebsiteApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
public class AbstractMessage {
|
||||
|
||||
public static final byte DOWNLOAD_POST_MESSAGE = 1;
|
||||
|
||||
public static final byte DOWNLOAD_STATUS_MESSAGE = 2;
|
||||
|
||||
public static final byte DELETE_GALLERY_MESSAGE = 3;
|
||||
|
||||
public static final byte RESPONSE_MESSAGE = 0;
|
||||
|
||||
public static final byte UPDATE_GALLERY_MESSAGE = 4;
|
||||
|
||||
public static final byte GALLERY_PAGE_QUERY_MESSAGE = 5;
|
||||
|
||||
public static final byte IDENTITY_MESSAGE = 6;
|
||||
public static final byte GALLERY_REQUEST_MESSAGE = 101;
|
||||
public byte messageType;
|
||||
|
||||
public int messageId;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DeleteGalleryMessage extends AbstractMessage{
|
||||
{
|
||||
messageType = DELETE_GALLERY_MESSAGE;
|
||||
}
|
||||
|
||||
public static final byte DELETE_ALL = 3;
|
||||
public static final byte DELETE_PREVIEW = 2;
|
||||
public static final byte DELETE_SOURCE = 1;
|
||||
|
||||
|
||||
|
||||
byte deleteType;
|
||||
|
||||
String galleryName;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
|
||||
import com.lion.lionwebsite.Domain.GalleryTask;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DownloadPostMessage extends AbstractMessage{
|
||||
{
|
||||
messageType = DOWNLOAD_POST_MESSAGE;
|
||||
}
|
||||
|
||||
GalleryTask galleryTask;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
import com.lion.lionwebsite.Domain.GalleryTask;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DownloadStatusMessage extends AbstractMessage{
|
||||
GalleryTask[] galleryTasks;
|
||||
|
||||
{
|
||||
messageType = DOWNLOAD_STATUS_MESSAGE;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GalleryPageQueryMessage extends AbstractMessage{
|
||||
{
|
||||
messageType = GALLERY_PAGE_QUERY_MESSAGE;
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
String name;
|
||||
|
||||
int page;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
String pageName;
|
||||
|
||||
byte result;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
//请求预览/压缩包
|
||||
public class GalleryRequestMessage extends AbstractMessage{
|
||||
|
||||
public static final byte SOURCE = 1;
|
||||
public static final byte PREVIEW = 2;
|
||||
|
||||
public static final byte COMPRESS_SOURCE = 3;
|
||||
|
||||
{
|
||||
messageType = GALLERY_REQUEST_MESSAGE;
|
||||
}
|
||||
|
||||
String galleryName;
|
||||
|
||||
byte type;
|
||||
|
||||
short page;
|
||||
|
||||
short port;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class IdentityMessage extends AbstractMessage{
|
||||
{
|
||||
messageType = IDENTITY_MESSAGE;
|
||||
}
|
||||
}
|
||||
57
src/main/java/com/lion/lionwebsite/Message/MessageCodec.java
Normal file
57
src/main/java/com/lion/lionwebsite/Message/MessageCodec.java
Normal file
@ -0,0 +1,57 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageCodec;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class MessageCodec extends ByteToMessageCodec<AbstractMessage> {
|
||||
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
public MessageCodec(){
|
||||
objectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext channelHandlerContext, AbstractMessage abstractMessage, ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(abstractMessage.messageType);
|
||||
|
||||
byte[] bytes = objectMapper.valueToTree(abstractMessage).toString().getBytes(StandardCharsets.UTF_8);
|
||||
byteBuf.writeInt(bytes.length);
|
||||
byteBuf.writeBytes(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||
byte messageType = byteBuf.readByte();
|
||||
int length = byteBuf.readInt();
|
||||
byte[] bytes = new byte[length];
|
||||
byteBuf.readBytes(bytes);
|
||||
final String metadata = new String(bytes, StandardCharsets.UTF_8);
|
||||
|
||||
AbstractMessage abstractMessage = switch (messageType){
|
||||
case AbstractMessage.DOWNLOAD_POST_MESSAGE -> objectMapper.readValue(metadata, DownloadPostMessage.class);
|
||||
case AbstractMessage.DOWNLOAD_STATUS_MESSAGE -> objectMapper.readValue(metadata, DownloadStatusMessage.class);
|
||||
case AbstractMessage.GALLERY_REQUEST_MESSAGE -> objectMapper.readValue(metadata, GalleryRequestMessage.class);
|
||||
case AbstractMessage.RESPONSE_MESSAGE -> objectMapper.readValue(metadata, ResponseMessage.class);
|
||||
case AbstractMessage.UPDATE_GALLERY_MESSAGE -> objectMapper.readValue(metadata, UpdateGalleryMessage.class);
|
||||
case AbstractMessage.DELETE_GALLERY_MESSAGE -> objectMapper.readValue(metadata, DeleteGalleryMessage.class);
|
||||
case AbstractMessage.GALLERY_PAGE_QUERY_MESSAGE -> objectMapper.readValue(metadata, GalleryPageQueryMessage.class);
|
||||
case AbstractMessage.IDENTITY_MESSAGE -> objectMapper.readValue(metadata, IdentityMessage.class);
|
||||
default -> null;
|
||||
};
|
||||
|
||||
if (abstractMessage == null){
|
||||
log.error("decode error, messageType: " + messageType + "ip:" + channelHandlerContext.channel().remoteAddress().toString());
|
||||
return;
|
||||
}
|
||||
list.add(abstractMessage);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ResponseMessage extends AbstractMessage{
|
||||
{
|
||||
messageType = RESPONSE_MESSAGE;
|
||||
}
|
||||
|
||||
static final byte SUCCESS = 0;
|
||||
|
||||
byte result;
|
||||
|
||||
public ResponseMessage(int messageId, byte result){
|
||||
this.messageId = messageId;
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.lion.lionwebsite.Message;
|
||||
|
||||
import com.lion.lionwebsite.Domain.GalleryTask;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateGalleryMessage extends AbstractMessage{
|
||||
{
|
||||
messageType = UPDATE_GALLERY_MESSAGE;
|
||||
}
|
||||
|
||||
GalleryTask galleryTask;
|
||||
|
||||
|
||||
}
|
||||
2
src/main/java/com/lion/lionwebsite/Message/lombok.config
Normal file
2
src/main/java/com/lion/lionwebsite/Message/lombok.config
Normal file
@ -0,0 +1,2 @@
|
||||
config.stopBubbling=true
|
||||
lombok.equalsAndHashCode.callSuper=call
|
||||
@ -0,0 +1,37 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import com.lion.lionwebsite.Dao.CollectMapper;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Data
|
||||
public class CollectService {
|
||||
@Resource
|
||||
CollectMapper collectMapper;
|
||||
|
||||
public String collectGallery(int gid, int collector){
|
||||
Response response = Response.generateResponse();
|
||||
if(collectMapper.isCollect(gid, collector) == 0) { //没有收藏
|
||||
collectMapper.collect(gid, collector);
|
||||
response.success("收藏成功");
|
||||
}else{
|
||||
response.failure("已经收藏了");
|
||||
}
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String disCollectGallery(int gid, int collector){
|
||||
Response response = Response.generateResponse();
|
||||
if(collectMapper.isCollect(gid, collector) == 0) { //没有收藏
|
||||
response.failure("没有收藏该本子");
|
||||
}else{
|
||||
collectMapper.disCollect(gid, collector);
|
||||
response.success("取消收藏成功");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,606 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.lion.lionwebsite.Dao.*;
|
||||
import com.lion.lionwebsite.Domain.*;
|
||||
import com.lion.lionwebsite.Exception.ResolutionNotMatchException;
|
||||
import com.lion.lionwebsite.Error.ErrorCode;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import com.lion.lionwebsite.Util.FileDownload;
|
||||
import com.lion.lionwebsite.Util.GalleryUtil;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
|
||||
import static com.lion.lionwebsite.Util.CustomUtil.dateTimeFormatter;
|
||||
import static com.lion.lionwebsite.Util.CustomUtil.fourZeroFour;
|
||||
|
||||
@Service
|
||||
@Data
|
||||
@Slf4j
|
||||
@ConfigurationProperties(prefix = "gallery-manage-service")
|
||||
public class GalleryManageService {
|
||||
String TargetPath;
|
||||
|
||||
int cacheSize;
|
||||
|
||||
Map<Integer, String> gid2name_cache = new HashMap<>();
|
||||
|
||||
Map<Integer, Integer> gid2page_cache = new HashMap<>();
|
||||
|
||||
Map<String, String> page2pageNameCache = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
GalleryMapper galleryMapper;
|
||||
|
||||
@Resource
|
||||
CollectMapper collectMapper;
|
||||
|
||||
@Resource
|
||||
CustomConfigurationMapper configurationMapper;
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
@Resource
|
||||
ShareFileMapper shareFileMapper;
|
||||
|
||||
@Resource
|
||||
TagMapper tagMapper;
|
||||
|
||||
@Resource
|
||||
PageNameCacheMapper pageNameCacheMapper;
|
||||
|
||||
@Resource
|
||||
RemoteService remoteService;
|
||||
|
||||
/**
|
||||
* 创建任务
|
||||
* @param link 任务链接
|
||||
* @param targetResolution 目标分辨率
|
||||
* @param AuthCode 授权码(用于记录是谁下载的)
|
||||
* @return 提交结果
|
||||
*/
|
||||
public String createTask(String link, String targetResolution, String AuthCode, List<Integer> tidS, byte mode){
|
||||
Response response = Response.generateResponse();
|
||||
// return Response._failure("调试中,请勿提交任务");
|
||||
int gid;
|
||||
try {
|
||||
gid = Integer.parseInt(link.split("/")[4]);
|
||||
}catch (NumberFormatException e){
|
||||
response.failure("链接错误");
|
||||
return response.toJSONString();
|
||||
}
|
||||
Gallery gallery;
|
||||
|
||||
//判断数据库中是否有这个任务,有则返回状态
|
||||
if((gallery = galleryMapper.selectGalleryByGid(gid)) != null){
|
||||
response.failure("任务队列已有此任务,任务状态: " + gallery.getStatus() + " 请点击查找任务");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
//尝试下载本子,返回结果
|
||||
try {
|
||||
gallery = GalleryUtil.parse(link, true, targetResolution);
|
||||
if(gallery == null){
|
||||
log.error("创建任务: {},解析失败", link);
|
||||
return Response._failure("任务解析失败,未知原因,请检查链接是否正常");
|
||||
}else{
|
||||
log.info("创建任务: {} 目标分辨率:{}", link, targetResolution);
|
||||
if(remoteService.addGalleryToQueue(gallery, mode) != 0){
|
||||
log.error("传送任务{}失败, 未知原因", gallery.getName());
|
||||
return Response._failure("任务传送失败,未知原因");
|
||||
}
|
||||
}
|
||||
}catch (ResolutionNotMatchException e){
|
||||
e.printStackTrace();
|
||||
response.failure("提交失败,分辨率不存在");
|
||||
return response.toJSONString();
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
response.failure("IO错误,可能是网络波动");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
//处理下载结果,将任务插入数据库并且更新每周用量
|
||||
if(gallery.getStatus().equals("已提交")) {
|
||||
response.success(gallery.toString());
|
||||
gallery.setDownloader(userMapper.selectUserByAuthCode(AuthCode).getId());
|
||||
gallery.setMode(mode);
|
||||
galleryMapper.insertGallery(gallery);
|
||||
|
||||
long usedAmount = Long.parseLong(configurationMapper.selectConfiguration(CustomConfiguration.WEEK_USED_AMOUNT).getValue());
|
||||
usedAmount += gallery.getFileSize();
|
||||
configurationMapper.updateConfiguration(CustomConfiguration.WEEK_USED_AMOUNT, String.valueOf(usedAmount));
|
||||
if(tidS != null)
|
||||
for (Integer tid : tidS)
|
||||
if(tagMapper.selectTagExistById(tid) > 0)
|
||||
tagMapper.markTag(gallery.getGid(), tid);
|
||||
}
|
||||
else{
|
||||
response.failure("提交失败,未知原因");
|
||||
galleryMapper.deleteGalleryByGid(gallery.getGid());
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据链接查询本子
|
||||
* @param link 链接
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String selectTaskByLink(String link) {
|
||||
Response response = Response.generateResponse();
|
||||
Integer gid = parseGid(link);
|
||||
Gallery gallery;
|
||||
if(gid != null)
|
||||
gallery = galleryMapper.selectGalleryByGid(gid);
|
||||
else {
|
||||
response.failure("链接错误");
|
||||
return response.toJSONString();
|
||||
}
|
||||
log.info("查询{}", link);
|
||||
|
||||
//判断数据库中是否有这个任务,如果有则返回,如果没有则直接查询
|
||||
if(gallery == null)
|
||||
try{
|
||||
gallery = GalleryUtil.parse(link, false, null);
|
||||
if(gallery != null)
|
||||
response.success(new ObjectMapper().valueToTree(gallery).toString());
|
||||
else
|
||||
response.failure("查询失败");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
response.failure("查询失败");
|
||||
}
|
||||
|
||||
else
|
||||
response.success(new ObjectMapper().valueToTree(gallery).toString());
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过gid查询本子
|
||||
* @param gid gid
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String selectTaskByGid(int gid){
|
||||
Response response = Response.generateResponse();
|
||||
Gallery gallery = galleryMapper.selectGalleryByGid(gid);
|
||||
|
||||
if(gallery == null)
|
||||
response.failure("未找到该本子,请使用链接");
|
||||
|
||||
else
|
||||
response.success(gallery.toString());
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有本子
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String selectAllGallery(int userId) {
|
||||
Response response = Response.generateResponse();
|
||||
Gallery[] galleries = galleryMapper.selectAllGallery();
|
||||
|
||||
if(galleries == null){
|
||||
response.failure("没有找到本子");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
ArrayList<Integer> galleryIds = collectMapper.selectGidByCollector(userId);
|
||||
Iterator<Integer> idIterator;
|
||||
|
||||
if(!galleryIds.isEmpty()) //如果该用户收藏了本子
|
||||
galleryLoop: for (Gallery gallery : galleries) { //遍历本子
|
||||
idIterator = galleryIds.iterator();
|
||||
while (idIterator.hasNext()){ //遍历收藏的gid
|
||||
Integer id = idIterator.next();
|
||||
if(id.equals(gallery.getGid())){ //如果找到对应的gid,修改对应本子的属性,删除当前gid,判断是否需要跳出或者结束循环
|
||||
gallery.setCollect(true);
|
||||
idIterator.remove();
|
||||
|
||||
if(galleryIds.isEmpty())
|
||||
break galleryLoop;
|
||||
else
|
||||
continue galleryLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HashMap<Integer, ArrayList<Integer>> tagsMap = new HashMap<>();
|
||||
ArrayList<TagMark> tags = tagMapper.selectAllMark();
|
||||
|
||||
for (TagMark tagMark : tags) { //从数据库取出所有标记,然后筛选出 gid -> tids ,后面加个缓存
|
||||
tagsMap.computeIfAbsent(tagMark.getGid(), k -> new ArrayList<>());
|
||||
tagsMap.get(tagMark.getGid()).add(tagMark.getTid());
|
||||
}
|
||||
|
||||
ArrayList<Integer> temp;
|
||||
for (Gallery gallery : galleries) {
|
||||
if((temp = tagsMap.get(gallery.getGid())) != null) {
|
||||
gallery.setTags(new ArrayList<>());
|
||||
gallery.getTags().addAll(temp);
|
||||
}
|
||||
}
|
||||
|
||||
response.success(new ObjectMapper().valueToTree(galleries).toString());
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未完成的本子
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String selectUnDoneGallery() {
|
||||
Response response = Response.generateResponse();
|
||||
Gallery[] galleries = galleryMapper.selectUnDoneGalleries();
|
||||
|
||||
if(galleries.length > 0)
|
||||
response.success(new ObjectMapper().valueToTree(galleries).toString());
|
||||
else
|
||||
response.failure();
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过本子名查询本子
|
||||
* @param name 名字
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String selectGalleryByName(String name) {
|
||||
Response response = Response.generateResponse();
|
||||
Gallery gallery = galleryMapper.selectGalleryByName("%" + name + "%");
|
||||
|
||||
if(gallery != null)
|
||||
response.success(new ObjectMapper().valueToTree(gallery).toString());
|
||||
else
|
||||
response.failure("没有找到该名字的本子");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户下载的本子
|
||||
* @param AuthCode 授权码(用于查询用户名)
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String selectGalleryByDownloader(String AuthCode){
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
Gallery[] galleries = galleryMapper.selectGalleryByDownloader(userMapper.selectUserByAuthCode(AuthCode).getId());
|
||||
if(galleries.length > 0)
|
||||
response.success(new ObjectMapper().valueToTree(galleries).toString());
|
||||
else
|
||||
response.failure("您未下载本子");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 获取本子图片的名字
|
||||
// * @param gid gid
|
||||
// * @return 名字数组
|
||||
// */
|
||||
// public String selectOnlineFilename(Integer gid){
|
||||
// Response response = Response.generateResponse();
|
||||
// String path;
|
||||
//
|
||||
// if((path = gid2name_cache.get(gid)) == null) {
|
||||
// Gallery gallery = galleryMapper.selectGalleryByGid(gid);
|
||||
//
|
||||
// if (gallery == null) {
|
||||
// response.failure("本子不存在");
|
||||
// return response.toJSONString();
|
||||
// }
|
||||
//
|
||||
// path = TargetPath + gallery.getName();
|
||||
// gid2name_cache.put(gid, path);
|
||||
// if(gid2name_cache.size() > cacheSize)
|
||||
// gid2name_cache.remove(gid2name_cache.keySet().iterator().next());
|
||||
// }
|
||||
//
|
||||
// File file = new File(path);
|
||||
// if(!file.isDirectory()){
|
||||
// response.failure("本子文件已被删除");
|
||||
// return response.toJSONString();
|
||||
// }
|
||||
//
|
||||
// File[] files = file.listFiles(pathname -> pathname.getName().endsWith(".webp") && !pathname.getName().startsWith("thumbnail"));
|
||||
// if(files == null || files.length == 0){
|
||||
// response.failure("本子文件丢失");
|
||||
// return response.toJSONString();
|
||||
// }
|
||||
//
|
||||
// ArrayList<String> images = new ArrayList<>();
|
||||
// for(File image: files){
|
||||
// images.add(image.getName().replace(".webp", ""));
|
||||
// }
|
||||
// //字符串长度相等时比较字典顺序,不相等时比较长度
|
||||
// images.sort((s1, s2) ->
|
||||
// s1.length() == s2.length() ? s1.compareTo(s2): s1.length() - s2.length()
|
||||
// );
|
||||
//
|
||||
// response.success(new ObjectMapper().valueToTree(images).toString());
|
||||
// return response.toJSONString();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 删除本子以及对应的文件(如果存在的话)
|
||||
* @param gid gid
|
||||
* @return 删除结果
|
||||
*/
|
||||
public String deleteGalleryByGid(Integer gid, String AuthCode, byte mode) {
|
||||
Response response = Response.generateResponse();
|
||||
String deleteType;
|
||||
|
||||
Gallery gallery = galleryMapper.selectGalleryByGid(gid);
|
||||
User user = userMapper.selectUserByAuthCode(AuthCode);
|
||||
if(gallery == null){
|
||||
response.failure("删除失败,该本子不存在");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
ArrayList<Integer> collector = collectMapper.selectCollectorByGid(gallery.getGid());
|
||||
if(!(collector.isEmpty() || collector.size() == 1 && collector.get(0).equals(user.getId()) //判断收藏
|
||||
&& gallery.getDownloader() == user.getId())) //判断下载
|
||||
response.failure("删除失败,该本子已被别人收藏或你不是下载人");
|
||||
|
||||
else{
|
||||
if((gallery.getMode() & mode) == 0) { //不能删除对应的,因为对应的没有
|
||||
if (mode == 1)
|
||||
response.failure("无法删除该本子的源文件,不存在");
|
||||
else if (mode == 2)
|
||||
response.failure("无法删除该本子的预览图,不存在");
|
||||
} else {
|
||||
log.info("删除本子{}, mode:{}, delete_mode:{}", gallery.getName(), gallery.getMode(), mode);
|
||||
gallery.setMode((byte) (gallery.getMode() ^ mode));
|
||||
if(gallery.getMode() == 0) { //删整个本子
|
||||
galleryMapper.deleteGalleryByGid(gallery.getGid()); //删除本子记录
|
||||
ArrayList<Integer> tidS = tagMapper.selectTagByGid(gallery.getGid());
|
||||
for (Integer tid : tidS)
|
||||
tagMapper.decrTagUsage(tid);
|
||||
|
||||
tagMapper.disMarkTagByGid(gallery.getGid()); //删除本子标记
|
||||
gid2name_cache.remove(gallery.getGid()); //删除本子缓存
|
||||
page2pageNameCache.remove(gallery.getName()); //移除页数缓存
|
||||
|
||||
if(new File(TargetPath + "/" + gallery.getName()).isDirectory()) //删除本地缓存
|
||||
FileUtil.del(TargetPath + "/" + gallery.getName());
|
||||
deleteType = "全部";
|
||||
}else{
|
||||
if(mode == 2 && new File(TargetPath + "/" + gallery.getName()).isDirectory()) { //删除预览缓存
|
||||
FileUtil.del(TargetPath + "/" + gallery.getName());
|
||||
gid2name_cache.remove(gallery.getGid()); //删除本子缓存
|
||||
page2pageNameCache.remove(gallery.getName()); //移除页数缓存
|
||||
}
|
||||
|
||||
galleryMapper.updateGallery(gallery);
|
||||
deleteType = (mode == 1? "源文件": "预览图"); //用于提示
|
||||
}
|
||||
switch (remoteService.deleteGallery(gallery, mode)){
|
||||
case ErrorCode.IO_ERROR -> response.failure("本子:" + gallery.getName() + deleteType + "删除失败,IO错误");
|
||||
case ErrorCode.FILE_NOT_FOUND -> response.failure("本子:" + gallery.getName() + deleteType + "删除失败,文件不存在");
|
||||
case 0 -> response.success();
|
||||
}
|
||||
if(response.get("result").equals("failure"))
|
||||
log.info(response.getResult());
|
||||
}
|
||||
}
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新本子
|
||||
* @param link 目标链接
|
||||
* @return 更新结果
|
||||
*/
|
||||
public String updateGallery(String link){
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
Integer gid = parseGid(link);
|
||||
if(gid == null) {
|
||||
response.failure("链接格式出错");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
Gallery oldGallery = galleryMapper.selectGalleryByGid(gid);
|
||||
if(oldGallery == null) {
|
||||
response.failure("数据库不存在该本子,请提交下载");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
try {
|
||||
String newLink = GalleryUtil.queryUpdateLink(oldGallery.getLink());
|
||||
if(newLink == null)
|
||||
response.failure("本子没有更新");
|
||||
|
||||
else{
|
||||
Gallery newGallery = GalleryUtil.parse(newLink, true, oldGallery.getResolution());
|
||||
if(newGallery != null) {
|
||||
galleryMapper.deleteGalleryByGid(oldGallery.getGid());
|
||||
remoteService.updateGallery(newGallery, oldGallery.getMode());
|
||||
newGallery.setDownloader(oldGallery.getDownloader());
|
||||
newGallery.setCollector(oldGallery.getCollector());
|
||||
|
||||
long usedAmount = Long.parseLong(configurationMapper.selectConfiguration(CustomConfiguration.WEEK_USED_AMOUNT).getValue());
|
||||
usedAmount += newGallery.getFileSize();
|
||||
configurationMapper.updateConfiguration(CustomConfiguration.WEEK_USED_AMOUNT, String.valueOf(usedAmount));
|
||||
|
||||
galleryMapper.insertGallery(newGallery);
|
||||
response.success("提交更新成功,更新页数:" + newGallery.getPages());
|
||||
}
|
||||
else {
|
||||
response.failure("存在更新,但更新失败");
|
||||
}
|
||||
}
|
||||
}catch (Exception e){
|
||||
response.failure("获取更新失败");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取每周额度以及上次重置时间
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String getWeekUsedAmount() {
|
||||
Response response = Response.generateResponse();
|
||||
CustomConfiguration weekUsedAmount = configurationMapper.selectConfiguration(CustomConfiguration.WEEK_USED_AMOUNT);
|
||||
CustomConfiguration lastResetAmountTime = configurationMapper.selectConfiguration(CustomConfiguration.LAST_RESET_AMOUNT_TIME);
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
data.put("weekUsedAmount", CustomUtil.fileSizeToString(Long.parseLong(weekUsedAmount.getValue())));
|
||||
data.put("lastResetAmountTime", lastResetAmountTime.getValue());
|
||||
|
||||
response.success(new ObjectMapper().valueToTree(data).toString());
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取缩略图 (直接按照名字路径获取,后期可能会加入缓存)
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
* @param name 本子名字
|
||||
*/
|
||||
public void getThumbnail(HttpServletRequest request, HttpServletResponse response, String name){
|
||||
File thumbnail = new File(TargetPath + name, "thumbnail.webp");
|
||||
|
||||
if(!thumbnail.exists())
|
||||
if(remoteService.cachePreview(name, (short) 0, thumbnail) != 0){
|
||||
fourZeroFour(response);
|
||||
return;
|
||||
}
|
||||
|
||||
FileDownload.export(request, response, thumbnail.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在线图片
|
||||
* @param gid gid
|
||||
* @param page 文件名
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
*/
|
||||
public void getOnlineImage(Integer gid, Short page, HttpServletRequest request, HttpServletResponse response){
|
||||
String name;
|
||||
|
||||
//本子名缓存
|
||||
if((name = gid2name_cache.get(gid)) == null) //内存
|
||||
if((name = galleryMapper.selectGalleryNameByGid(gid)) == null) { //数据库
|
||||
fourZeroFour(response);
|
||||
return;
|
||||
}
|
||||
else
|
||||
gid2name_cache.put(gid, name);
|
||||
|
||||
//页数缓存
|
||||
Integer real_page;
|
||||
if((real_page = gid2page_cache.get(gid)) == null)
|
||||
real_page = galleryMapper.selectGalleryByGid(gid).getPages();
|
||||
|
||||
//判断页数是否超出
|
||||
if(real_page < page || page < 0){
|
||||
fourZeroFour(response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//页名缓存
|
||||
String pageName;
|
||||
if((pageName = page2pageNameCache.get(gid + String.valueOf(page))) == null) //内存
|
||||
if((pageName = pageNameCacheMapper.selectPageName(gid, page)) == null) //数据库
|
||||
if ((pageName = remoteService.queryPageName(name, page)) == null) { //远程查询
|
||||
fourZeroFour(response);
|
||||
return;
|
||||
}
|
||||
//插入数据库
|
||||
else
|
||||
pageNameCacheMapper.insertPageNameCache(new PageNameCache(gid, page, pageName));
|
||||
//插入缓存
|
||||
else
|
||||
page2pageNameCache.put(gid + String.valueOf(page), pageName);
|
||||
|
||||
|
||||
//图片缓存
|
||||
File file = new File(TargetPath, name + "/" + pageName);
|
||||
if(!file.exists()) //硬盘
|
||||
if(remoteService.cachePreview(name, page, file) != 0) { //远程
|
||||
fourZeroFour(response);
|
||||
return;
|
||||
}
|
||||
|
||||
FileDownload.export(request, response, file.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String shareGallery(Integer gid, Integer expireHour){
|
||||
Response response = Response.generateResponse();
|
||||
Gallery gallery = galleryMapper.selectGalleryByGid(gid);
|
||||
Map<String, String> jsonObject = new HashMap<>();
|
||||
if(gallery == null){
|
||||
response.failure("本子不存在");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
File file = new File(TargetPath + gallery.getName() + ".zip");
|
||||
|
||||
if(!file.isFile()){
|
||||
response.failure("本子文件不存在");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
ShareFile shareFile = shareFileMapper.selectShareFileByFilePath(file.getAbsolutePath());
|
||||
if(shareFile != null){
|
||||
jsonObject.put("shareCode", shareFile.getShareCode());
|
||||
jsonObject.put("expireTime", dateTimeFormatter.format(LocalDateTime.ofInstant(shareFile.getExpireTime().toInstant(), ZoneId.systemDefault())));
|
||||
response.success(new ObjectMapper().valueToTree(jsonObject).toString());
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
String ShareCode = RandomUtil.randomString(8);
|
||||
Calendar expireTime = Calendar.getInstance();
|
||||
|
||||
expireTime.add(Calendar.HOUR, expireHour);
|
||||
shareFileMapper.insertShareFile(ShareCode, file.getAbsolutePath(), expireTime.getTime());
|
||||
jsonObject.put("shareCode", ShareCode);
|
||||
jsonObject.put("expireTime", dateTimeFormatter.format(expireTime.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
|
||||
response.success(new ObjectMapper().valueToTree(jsonObject).toString());
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public static Integer parseGid(String link){
|
||||
try {
|
||||
return Integer.parseInt(link.split("/g/")[1].split("/")[0]);
|
||||
}catch (IndexOutOfBoundsException e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
299
src/main/java/com/lion/lionwebsite/Service/LocalServiceImpl.java
Normal file
299
src/main/java/com/lion/lionwebsite/Service/LocalServiceImpl.java
Normal file
@ -0,0 +1,299 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import com.lion.lionwebsite.Dao.CustomConfigurationMapper;
|
||||
import com.lion.lionwebsite.Dao.GalleryMapper;
|
||||
import com.lion.lionwebsite.Dao.ShareFileMapper;
|
||||
import com.lion.lionwebsite.Domain.CustomConfiguration;
|
||||
import com.lion.lionwebsite.Domain.ShareFile;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import com.lion.lionwebsite.Util.GalleryUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static java.nio.file.FileVisitResult.CONTINUE;
|
||||
|
||||
@Service
|
||||
@ConfigurationProperties(prefix = "local-service")
|
||||
@Data
|
||||
public class LocalServiceImpl{
|
||||
String fires;
|
||||
String DouNaiClash;
|
||||
String DouNaiV2ray;
|
||||
|
||||
@Resource
|
||||
CustomConfigurationMapper configurationMapper;
|
||||
|
||||
@Resource
|
||||
ShareFileMapper shareFIleMapper;
|
||||
|
||||
@Resource
|
||||
GalleryMapper galleryMapper;
|
||||
|
||||
/**
|
||||
* 每周周一四点重置额度
|
||||
*/
|
||||
@Scheduled(cron = "0 0 4 * * MON")
|
||||
public void reset() {
|
||||
configurationMapper.updateConfiguration(CustomConfiguration.WEEK_USED_AMOUNT, "0");
|
||||
configurationMapper.updateConfiguration(CustomConfiguration.LAST_RESET_AMOUNT_TIME, CustomUtil.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时更新订阅
|
||||
* @throws IOException 下载以及保存异常
|
||||
*/
|
||||
@Scheduled(fixedRate = 86400000)
|
||||
public void updateSubScheduler() throws IOException {
|
||||
updateSub(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订阅链接的实际方法
|
||||
*/
|
||||
public boolean updateSub(boolean isManual) throws IOException {
|
||||
DateTimeFormatter dateTimeFormatter = CustomUtil.dateTimeFormatter();
|
||||
CustomConfiguration customConfiguration = configurationMapper.selectConfiguration(CustomConfiguration.LAST_UPDATE_SUB_TIME);
|
||||
|
||||
//如果不是手动,则判断更新间隔是否满足,不满足则取消更新
|
||||
if (!isManual) {
|
||||
LocalDateTime lastUpdate = LocalDateTime.parse(customConfiguration.getValue(), CustomUtil.dateTimeFormatter());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
now = now.plusHours(-3);
|
||||
if(now.isBefore(lastUpdate))
|
||||
return false;
|
||||
}
|
||||
|
||||
File mixin = new File("sub/sub.txt");
|
||||
File firesFile = new File("sub/fires.txt");
|
||||
File DouNaiClashFile = new File("sub/DouNaiClash.txt");
|
||||
File DouNaiV2rayFile = new File("sub/DouNaiV2ray.txt");
|
||||
File directory = new File("sub");
|
||||
|
||||
if(!directory.isDirectory())
|
||||
Files.createDirectory(Paths.get("sub"));
|
||||
|
||||
List<String> fires_profile = null;
|
||||
List<String> DouNaiClash_profile = null;
|
||||
OutputStream outputStream;
|
||||
|
||||
//下载薯条订阅
|
||||
try(FileWriter writer = new FileWriter(firesFile)) {
|
||||
fires_profile = Get(fires);
|
||||
for (String i : fires_profile)
|
||||
writer.write(i + "\n");
|
||||
|
||||
System.out.println("load fires complete");
|
||||
}catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("load fires failure");
|
||||
}
|
||||
|
||||
//下载豆奶v2ray订阅
|
||||
try(FileWriter writer = new FileWriter(DouNaiV2rayFile)) {
|
||||
String DouNaiV2rayRaw = Get(DouNaiV2ray).get(0);
|
||||
String[] v2rayPlain = new String(Base64.getDecoder().decode(DouNaiV2rayRaw)).split("\n");
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
//过滤高倍率节点
|
||||
for(String node: v2rayPlain){
|
||||
String name = URLDecoder.decode(node.split("#")[1], StandardCharsets.UTF_8);
|
||||
if(name.startsWith("①") && name.contains("流量")){
|
||||
float ratio = Float.parseFloat(name.substring(name.indexOf("(") + 1, name.indexOf(")")).replace("倍流量", ""));
|
||||
if(ratio <= 1)
|
||||
stringBuilder.append(node).append("\n");
|
||||
}
|
||||
else{
|
||||
stringBuilder.append(node).append("\n");
|
||||
}
|
||||
}
|
||||
writer.write(new String(Base64.getEncoder().encode(stringBuilder.toString().getBytes(StandardCharsets.UTF_8))));
|
||||
|
||||
System.out.println("load DouNai v2ray complete");
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
System.out.println("load DouNai v2ray failure");
|
||||
}
|
||||
|
||||
//下载豆奶clash订阅
|
||||
try(FileWriter writer = new FileWriter(DouNaiClashFile)) {
|
||||
DouNaiClash_profile = Get(DouNaiClash);
|
||||
//过滤高倍率节点
|
||||
ArrayList<String> clashProcessed = new ArrayList<>();
|
||||
boolean isProxies = false;
|
||||
boolean skip = false;
|
||||
for(String line: DouNaiClash_profile){
|
||||
if(line.equals("proxies:"))
|
||||
isProxies = true;
|
||||
else if(line.equals("proxy-groups:") && isProxies)
|
||||
isProxies = false;
|
||||
|
||||
if(isProxies) {
|
||||
if (line.contains("name"))
|
||||
skip = line.contains("流量");
|
||||
if (!skip)
|
||||
clashProcessed.add(line);
|
||||
}
|
||||
else
|
||||
if (!line.contains("流量"))
|
||||
clashProcessed.add(line);
|
||||
}
|
||||
|
||||
for(String line: clashProcessed)
|
||||
writer.write(line + "\n");
|
||||
System.out.println("load DouNai clash complete");
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
System.out.println("load DouNai clash failure");
|
||||
}
|
||||
|
||||
//处理合并
|
||||
assert DouNaiClash_profile != null;
|
||||
int side_start = DouNaiClash_profile.indexOf("proxies:");
|
||||
int side_end = DouNaiClash_profile.indexOf("proxy-groups:");
|
||||
|
||||
List<String> side_proxy = new ArrayList<>();
|
||||
List<String> side_name = new ArrayList<>();
|
||||
for (int i = side_start + 1; i < side_end; i++) {
|
||||
side_proxy.add(DouNaiClash_profile.get(i));
|
||||
if (DouNaiClash_profile.get(i).contains("name"))
|
||||
side_name.add(DouNaiClash_profile.get(i).replace("name:", ""));
|
||||
}
|
||||
|
||||
List<String> final_profile = new ArrayList<>();
|
||||
assert fires_profile != null;
|
||||
for (String i : fires_profile) {
|
||||
if (i.equals("proxy-groups:"))
|
||||
final_profile.addAll(side_proxy);
|
||||
|
||||
final_profile.add(i);
|
||||
if (i.equals("proxy-groups:")) {
|
||||
final_profile.add(" -");
|
||||
final_profile.add(" name: e站流量");
|
||||
final_profile.add(" type: select");
|
||||
final_profile.add(" proxies:");
|
||||
for (String x : side_name)
|
||||
final_profile.add(" " + x);
|
||||
|
||||
}
|
||||
if (i.equals("rules:"))
|
||||
final_profile.add(" - DOMAIN-KEYWORD,hentai,e站流量");
|
||||
}
|
||||
|
||||
outputStream = Files.newOutputStream(mixin.toPath());
|
||||
for(String i: final_profile)
|
||||
outputStream.write((i + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.close();
|
||||
|
||||
configurationMapper.updateConfiguration(CustomConfiguration.LAST_UPDATE_SUB_TIME, dateTimeFormatter.format(LocalDateTime.now()));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装好的get方法
|
||||
* @param url url
|
||||
* @return 请求结果
|
||||
* @throws IOException 网络异常
|
||||
*/
|
||||
public static ArrayList<String> Get(String url) throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse httpResponse;
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
|
||||
httpResponse = httpClient.execute(httpGet);
|
||||
|
||||
HttpEntity responseEntity = httpResponse.getEntity();
|
||||
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
ArrayList<String> temp = new ArrayList<>();
|
||||
|
||||
if (statusCode == 200) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
|
||||
String str;
|
||||
while ((str = reader.readLine()) != null)
|
||||
temp.add(str);
|
||||
}
|
||||
|
||||
httpClient.close();
|
||||
httpResponse.close();
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 每天四点,清理过期分享码
|
||||
*/
|
||||
@Scheduled(cron = "0 0 4 * * *")
|
||||
public void checkShareCode(){
|
||||
ShareFile[] shareFiles = shareFIleMapper.selectAllShareFile();
|
||||
Calendar now;
|
||||
Calendar expireTime;
|
||||
for(ShareFile shareFile: shareFiles){
|
||||
now = Calendar.getInstance();
|
||||
expireTime = Calendar.getInstance();
|
||||
expireTime.setTime(shareFile.getExpireTime());
|
||||
if(now.after(expireTime))
|
||||
shareFIleMapper.deleteShareFile(shareFile.getShareCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每周,查看缩略图数量,按照访问时间排序,删除超出1w的部分
|
||||
*/
|
||||
@Scheduled(cron = "0 0 4 1 * *")
|
||||
public void clearThumbnailCache(){
|
||||
String cachePath = "/storage/hentaiCache/";
|
||||
File directory = new File(cachePath);
|
||||
List<Path> files = new ArrayList<>();
|
||||
try {
|
||||
Files.walkFileTree(directory.toPath(), new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
if (attrs.isRegularFile()) {
|
||||
files.add(file);
|
||||
}
|
||||
return CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
files.sort((f1, f2) -> {
|
||||
try {
|
||||
return Files.readAttributes(f1, BasicFileAttributes.class).lastAccessTime()
|
||||
.compareTo(Files.readAttributes(f2, BasicFileAttributes.class).lastAccessTime());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
if (files.size() > 10000) {
|
||||
List<Path> toDelete = files.subList(0, files.size() - 10000);
|
||||
for (Path file : toDelete) {
|
||||
try {
|
||||
Files.delete(file);
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
System.out.println("Deleted " + toDelete.size() + " files");
|
||||
} else {
|
||||
System.out.println("No files to delete");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,365 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.lion.lionwebsite.Dao.CustomConfigurationMapper;
|
||||
import com.lion.lionwebsite.Dao.ShareFileMapper;
|
||||
import com.lion.lionwebsite.Dao.UserMapper;
|
||||
import com.lion.lionwebsite.Domain.CustomConfiguration;
|
||||
import com.lion.lionwebsite.Domain.ShareFile;
|
||||
import com.lion.lionwebsite.Interceptor.TaskHandlerInterceptor;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import com.lion.lionwebsite.Util.FileDownload;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
||||
@Service
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "personal-service")
|
||||
@Slf4j
|
||||
public class PersonalServiceImpl{
|
||||
@Resource
|
||||
CustomConfigurationMapper configurationMapper;
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
@Resource
|
||||
ShareFileMapper shareFileMapper;
|
||||
|
||||
@Resource
|
||||
TaskHandlerInterceptor taskHandlerInterceptor;
|
||||
|
||||
String StoragePath;
|
||||
|
||||
DateTimeFormatter dateTimeFormatter = CustomUtil.dateTimeFormatter();
|
||||
|
||||
ExecutorService compressThreadPool;
|
||||
|
||||
PersonalServiceImpl(){
|
||||
compressThreadPool = Executors.newFixedThreadPool(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件列表,同时带上分享码以及过期时间
|
||||
* @param path 路径
|
||||
* @return 文件列表
|
||||
*/
|
||||
public String getFiles(String path) {
|
||||
Response response = Response.generateResponse();
|
||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
||||
File root_file = new File(StoragePath + path);
|
||||
Calendar now = Calendar.getInstance();
|
||||
Calendar expireTime = Calendar.getInstance();
|
||||
|
||||
//如果目标路径是文件夹
|
||||
if(root_file.isDirectory()) {
|
||||
ArrayList<Map<String, String>> fileMaps = new ArrayList<>();
|
||||
File[] originalFiles = root_file.listFiles();
|
||||
|
||||
//如果文件夹里的文件不为空
|
||||
if (originalFiles != null) {
|
||||
originalFiles = Arrays.stream(originalFiles).sorted(Comparator.comparing(File::getName)).toArray(File[]::new);
|
||||
ArrayList<ShareFile> shareFiles = shareFileMapper.selectShareFilesByFilePath(root_file.getAbsolutePath());
|
||||
|
||||
//遍历文件,放入文件信息以及查询对应的分享码
|
||||
for (File file : originalFiles) {
|
||||
Map<String, String> fileMap = new LinkedHashMap<>();
|
||||
fileMap.put("name", file.getName());
|
||||
fileMap.put("path", file.getAbsolutePath());
|
||||
|
||||
if (file.isDirectory()) {
|
||||
fileMap.put("type", "FOLDER");
|
||||
} else if (file.isFile()) {
|
||||
fileMap.put("type", "FILE"); //处理文件大小单位
|
||||
String fileSize = CustomUtil.fileSizeToString(file.length());
|
||||
fileMap.put("size", fileSize);
|
||||
|
||||
Iterator<ShareFile> iterator = shareFiles.iterator();
|
||||
while(iterator.hasNext()){
|
||||
ShareFile shareFile = iterator.next();
|
||||
if(shareFile.getFilePath().equals(file.getAbsolutePath())){
|
||||
expireTime.setTime(shareFile.getExpireTime());
|
||||
if(now.after(expireTime)){
|
||||
shareFileMapper.deleteShareFile(shareFile.getShareCode());
|
||||
}
|
||||
else {
|
||||
fileMap.put("shareCode", shareFile.getShareCode());
|
||||
fileMap.put("expireTime", dateTimeFormatter.format(shareFile.getExpireTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
|
||||
}
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fileMaps.add(fileMap);
|
||||
}
|
||||
response.success(new ObjectMapper().valueToTree(fileMaps).toString());
|
||||
}
|
||||
else
|
||||
response.failure("文件夹为空");
|
||||
}
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
* @param path 目标路径
|
||||
*/
|
||||
public void download(HttpServletRequest request, HttpServletResponse response, String path){
|
||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
||||
File file = new File(path);
|
||||
if(file.exists())
|
||||
FileDownload.export(request, response, path);
|
||||
|
||||
else
|
||||
try{
|
||||
response.getWriter().print("404 NOT FOUND");
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param path 目标路径
|
||||
* @param fileName 文件名称
|
||||
* @param file 文件对象
|
||||
* @return 上传结果
|
||||
*/
|
||||
public String uploadFile(String path, String fileName, MultipartFile file) {
|
||||
Response response = Response.generateResponse();
|
||||
log.info("上传文件:{}, 目标路径:{}", fileName, path);
|
||||
|
||||
if(path == null || fileName == null || file == null){
|
||||
response.failure("参数不完整");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
File directory = new File(StoragePath + path);
|
||||
if(directory.isDirectory()){
|
||||
File targetFile = new File(StoragePath + path, fileName);
|
||||
|
||||
if(targetFile.exists())
|
||||
response.failure("目标文件已存在");
|
||||
else
|
||||
try {
|
||||
file.transferTo(Path.of(StoragePath + path, fileName));
|
||||
response.success("上传成功");
|
||||
} catch (IOException e) {
|
||||
response.failure("上传失败");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else
|
||||
response.failure("该路径不存在或者不是文件夹");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建分享码
|
||||
* @param path 目标路径
|
||||
* @param expireHour 过期时间
|
||||
* @return 如果成功则是分享码以及过期时间,失败则是失败原因
|
||||
*/
|
||||
public String shareFile(String path, Integer expireHour) {
|
||||
Response response = Response.generateResponse();
|
||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
||||
Map<String, String> jsonObject = new HashMap<>();
|
||||
File file = new File(path);
|
||||
if(file.isFile()){ //如果是文件,则生成分享码,调用此接口时不需要考虑分享已分享文件以及分享已过期文件
|
||||
String ShareCode;
|
||||
ShareCode = RandomUtil.randomString(8);
|
||||
Calendar expireTime = Calendar.getInstance();
|
||||
|
||||
expireTime.add(Calendar.HOUR, expireHour);
|
||||
shareFileMapper.insertShareFile(ShareCode, path, expireTime.getTime());
|
||||
jsonObject.put("shareCode", ShareCode);
|
||||
jsonObject.put("expireTime", dateTimeFormatter.format(expireTime.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
|
||||
response.success(new ObjectMapper().valueToTree(jsonObject).toString());
|
||||
}
|
||||
else
|
||||
response.failure("此路径为文件夹或不存在");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延长分享时间
|
||||
* @param path 目标文件路径
|
||||
* @param extendHour 延长小时数
|
||||
* @return 延长结果
|
||||
*/
|
||||
public String extendShareTime(String path, Integer extendHour) {
|
||||
Response response = Response.generateResponse();
|
||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
||||
Map<String, String> data = new LinkedHashMap<>();
|
||||
ShareFile shareFile = shareFileMapper.selectShareFileByFilePath(path);
|
||||
|
||||
if(shareFile != null){
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(shareFile.getExpireTime());
|
||||
|
||||
calendar.add(Calendar.HOUR, extendHour);
|
||||
shareFile.setExpireTime(calendar.getTime());
|
||||
shareFileMapper.updateShareFile(shareFile);
|
||||
data.put("expireTime", dateTimeFormatter.format(shareFile.getExpireTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
|
||||
data.put("path", shareFile.getFilePath());
|
||||
|
||||
response.success(new ObjectMapper().valueToTree(data).toString());
|
||||
}
|
||||
else{
|
||||
response.failure("该文件未被分享");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消分享
|
||||
* @param path 目标路径
|
||||
* @return 取消结果
|
||||
*/
|
||||
public String cancelShare(String path) {
|
||||
Response response = Response.generateResponse();
|
||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
||||
ShareFile shareFile = shareFileMapper.selectShareFileByFilePath(path);
|
||||
|
||||
if(shareFile != null){
|
||||
shareFileMapper.deleteShareFile(shareFile.getShareCode());
|
||||
response.success("取消分享成功");
|
||||
}
|
||||
else{
|
||||
response.failure("该文件未被分享");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订阅文件上次更新时间
|
||||
* @return 订阅文件上次更新时间
|
||||
*/
|
||||
public String lastUpdate() {
|
||||
Response response = Response.generateResponse();
|
||||
CustomConfiguration configuration = configurationMapper.selectConfiguration(CustomConfiguration.LAST_UPDATE_SUB_TIME);
|
||||
response.success(configuration.getValue());
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取家里的ip
|
||||
* @return 家里的ip
|
||||
*/
|
||||
public String getIp(){
|
||||
Response response = Response.generateResponse();
|
||||
Map<String, String> jsonObject = new HashMap<>();
|
||||
String ip = configurationMapper.selectConfiguration(CustomConfiguration.CURRENT_IP_ADDRESS).getValue();
|
||||
String updateTime = configurationMapper.selectConfiguration(CustomConfiguration.LAST_UPDATE_IP_ADDRESS_TIME).getValue();
|
||||
|
||||
jsonObject.put("ip", ip);
|
||||
jsonObject.put("lastUpdateTime", updateTime);
|
||||
response.success(new ObjectMapper().valueToTree(jsonObject).toString().replace("\"", " ").replace("\\", " "));
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包文件
|
||||
* @param path 目标路径
|
||||
* @return 响应提交成功,因为该方法为异步执行,未完成时后辍为undone
|
||||
*/
|
||||
public String compress(String path) {
|
||||
Response response = Response.generateResponse();
|
||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
||||
File file = new File(path);
|
||||
String finalPath = path;
|
||||
|
||||
if(!file.isDirectory() || !file.exists()){
|
||||
response.failure("选中的路径不是文件夹");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
compressThreadPool.submit(() -> {
|
||||
try(OutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(finalPath + ".tar***undone")));
|
||||
TarArchiveOutputStream aos = new TarArchiveOutputStream(bos)) {
|
||||
aos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); //解除文件名长度限制
|
||||
Path dirPath = Paths.get(finalPath);
|
||||
Files.walkFileTree(dirPath, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
ArchiveEntry entry = new TarArchiveEntry(dir.toFile(), dirPath.relativize(dir).toString());
|
||||
aos.putArchiveEntry(entry);
|
||||
aos.closeArchiveEntry();
|
||||
return super.preVisitDirectory(dir, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
ArchiveEntry entry = new TarArchiveEntry(file.toFile(), dirPath.relativize(file).toString());
|
||||
aos.putArchiveEntry(entry);
|
||||
IOUtils.copy(Files.newInputStream(file.toFile().toPath()), aos);
|
||||
aos.closeArchiveEntry();
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
});
|
||||
File targetFile = new File(finalPath + ".tar***undone");
|
||||
log.info("打包成功,重命名:" + targetFile.renameTo(new File(finalPath + ".tar")));
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
log.info("打包失败,删除文件结果:" + new File(finalPath + ".tar***undone").delete());
|
||||
}
|
||||
});
|
||||
response.success("加入队列成功");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param path 目标路径
|
||||
* @return 删除结果
|
||||
*/
|
||||
public String deleteFile(String path) {
|
||||
Response response = Response.generateResponse();
|
||||
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
|
||||
File file = new File(path);
|
||||
|
||||
if(FileUtil.del(file))
|
||||
response.success("删除成功");
|
||||
else
|
||||
response.failure("删除失败");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,161 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import com.lion.lionwebsite.Dao.CustomConfigurationMapper;
|
||||
|
||||
import com.lion.lionwebsite.Dao.ShareFileMapper;
|
||||
import com.lion.lionwebsite.Dao.UserMapper;
|
||||
import com.lion.lionwebsite.Domain.CustomConfiguration;
|
||||
import com.lion.lionwebsite.Domain.ShareFile;
|
||||
import com.lion.lionwebsite.Domain.User;
|
||||
import com.lion.lionwebsite.Interceptor.TaskHandlerInterceptor;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import com.lion.lionwebsite.Util.FileDownload;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Calendar;
|
||||
|
||||
import static com.lion.lionwebsite.Util.CustomUtil.fourZeroFour;
|
||||
|
||||
|
||||
@Service
|
||||
public class PublicServiceImpl {
|
||||
|
||||
@Resource
|
||||
CustomConfigurationMapper configurationMapper;
|
||||
|
||||
@Resource
|
||||
ShareFileMapper shareFIleMapper;
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
@Resource
|
||||
TaskHandlerInterceptor taskHandlerInterceptor;
|
||||
|
||||
/**
|
||||
* 记录家里ip地址
|
||||
* @param ip ip地址
|
||||
*/
|
||||
public void logIpAddress(String ip) {
|
||||
configurationMapper.updateConfiguration(CustomConfiguration.CURRENT_IP_ADDRESS, ip);
|
||||
configurationMapper.updateConfiguration(CustomConfiguration.LAST_UPDATE_IP_ADDRESS_TIME, CustomUtil.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过分享码获取文件
|
||||
* @param httpRequest 请求对象
|
||||
* @param httpResponse 响应对象
|
||||
* @param ShareCode 分享码
|
||||
* @throws IOException 响应时的异常
|
||||
*/
|
||||
public boolean GetFile(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String ShareCode) throws IOException {
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
//参数为空的情况
|
||||
if(ShareCode == null) {
|
||||
response.failure("ShareCode invalid");
|
||||
} else {
|
||||
ShareFile shareFile = shareFIleMapper.selectShareFileByShareCode(ShareCode);
|
||||
Calendar ExpireTime = Calendar.getInstance();
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
||||
if (shareFile != null) {
|
||||
ExpireTime.setTime(shareFile.getExpireTime());
|
||||
if (ExpireTime.after(now) && new File(shareFile.getFilePath()).isFile()) {
|
||||
FileDownload.export(httpRequest, httpResponse, shareFile.getFilePath());
|
||||
return true;
|
||||
} else {
|
||||
shareFIleMapper.deleteShareFile(shareFile.getShareCode());
|
||||
response.failure("ShareCode is expired or File is not exist");
|
||||
}
|
||||
} else
|
||||
response.failure("ShareCode is not exist or expired");
|
||||
}
|
||||
|
||||
httpResponse.getOutputStream().write(response.toJSONString().getBytes(StandardCharsets.UTF_8));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改授权码,如果能够执行此方法,则授权码一定存在
|
||||
* @param AuthCode 原来的授权码
|
||||
* @param newAuthCode 新的授权码
|
||||
* @return 修改结果
|
||||
*/
|
||||
public String alterAuthCode(String AuthCode, String newAuthCode) {
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
userMapper.updateAuthCode(AuthCode, newAuthCode);
|
||||
taskHandlerInterceptor.updateAuthCodes();
|
||||
response.success("修改成功");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public User getUserId(String AuthCode){
|
||||
return userMapper.selectUserByAuthCode(AuthCode);
|
||||
}
|
||||
|
||||
public void getEhThumbnail(String path, HttpServletResponse response){
|
||||
String url;
|
||||
if(!path.contains("/")){
|
||||
fourZeroFour(response);
|
||||
return;
|
||||
}
|
||||
|
||||
url = "https://ehgt.org/" + path;
|
||||
try{
|
||||
byte[] imageBytes = getImageBytesFromUrl(url);
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
outputStream.write(imageBytes);
|
||||
outputStream.close();
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
try {
|
||||
response.sendError(503);
|
||||
}catch (IOException ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getImageBytesFromUrl(String imageUrl) throws IOException {
|
||||
URL url = new URL(imageUrl);
|
||||
InputStream inputStream = null;
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
// 打开URL连接
|
||||
inputStream = url.openStream();
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
// 从输入流读取数据并写入输出流
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} finally {
|
||||
// 关闭流
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
// 返回图片的字节数组
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
174
src/main/java/com/lion/lionwebsite/Service/QueryService.java
Normal file
174
src/main/java/com/lion/lionwebsite/Service/QueryService.java
Normal file
@ -0,0 +1,174 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import com.lion.lionwebsite.Domain.GalleryForQuery;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class QueryService {
|
||||
|
||||
String CachePath = "/storage/hentaiCache/";
|
||||
|
||||
public String query(String keyword, String prev, String next) {
|
||||
Response response = Response.generateResponse();
|
||||
String get;
|
||||
String param = "?f_search=" + keyword.replace(" ", "+");
|
||||
|
||||
if(prev != null)
|
||||
param += "&prev=" + prev;
|
||||
else if(next != null)
|
||||
param += "&next=" + next;
|
||||
|
||||
try{
|
||||
get = requests("https://exhentai.org/" + param, false, null, null);
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
response.failure("query failure");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
Document parse = Jsoup.parse(get);
|
||||
Elements elements = parse.select("body > div.ido > div:nth-child(2) > table > tbody > tr");
|
||||
|
||||
|
||||
ArrayList<GalleryForQuery> galleries = new ArrayList<>();
|
||||
ListIterator<Element> elementListIterator = elements.listIterator();
|
||||
try {
|
||||
elementListIterator.next(); //skip firstlink
|
||||
}catch (NullPointerException | NoSuchElementException e){
|
||||
response.failure("没有搜索到结果");
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
while (elementListIterator.hasNext()){
|
||||
Element element = elementListIterator.next();
|
||||
GalleryForQuery gallery = new GalleryForQuery();
|
||||
gallery.setType(element.child(0).text()); //type
|
||||
String src = element.child(1).select("img").attr("src");
|
||||
if (src.startsWith("data"))
|
||||
src = element.child(1).select("img").attr("data-src");
|
||||
src = src.replace("https://s.exhentai.org", "");
|
||||
gallery.setThumbnailUrl(src); //thumbnailSrc
|
||||
gallery.setUploadTime(element.child(1).select("div > div [onclick]").get(1).text()); //uploadTime
|
||||
gallery.setLink(element.child(2).child(0).attr("href")); //link
|
||||
gallery.setName(element.child(2).child(0).child(0).text()); //name
|
||||
gallery.setPage(Integer.parseInt(element.child(3).child(1).text().split(" ")[0])); //page
|
||||
|
||||
galleries.add(gallery);
|
||||
}
|
||||
|
||||
|
||||
response.success(new ObjectMapper().valueToTree(galleries).toString());
|
||||
Elements nextLink = parse.select("#unext");
|
||||
if(nextLink.hasAttr("href"))
|
||||
response.set("next", nextLink.attr("href"));
|
||||
|
||||
Elements previousLink = parse.select("#uprev");
|
||||
if(previousLink.hasAttr("href"))
|
||||
response.set("previous", previousLink.attr("href"));
|
||||
|
||||
Elements firstLink = parse.select("#ufirst");
|
||||
if(firstLink.hasAttr("href"))
|
||||
response.set("first", firstLink.attr("href"));
|
||||
|
||||
Elements lastLink = parse.select("#ulast");
|
||||
if(lastLink.hasAttr("href"))
|
||||
response.set("last", lastLink.attr("href"));
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public void image(HttpServletResponse response, String path){
|
||||
String imageName = path.substring(path.lastIndexOf("/") + 1);
|
||||
File image = new File(CachePath, imageName);
|
||||
if(image.isFile()){ //hit cache
|
||||
log.info("hit cache:{}", imageName);
|
||||
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(image));
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(response.getOutputStream())){
|
||||
bufferedInputStream.transferTo(bufferedOutputStream);
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else{ // transfer image and save it as cache
|
||||
log.info("miss cache:{}", imageName);
|
||||
try {
|
||||
requests("https://s.exhentai.org" + path, true, response, new FileOutputStream(image));
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String requests(String url, boolean isDirect, HttpServletResponse response, OutputStream local) throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse httpResponse;
|
||||
HashMap<String, String> headers = new HashMap<>();
|
||||
|
||||
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36");
|
||||
headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
|
||||
headers.put("Upgrade-Insecure-Requests", "1");
|
||||
headers.put("Cookie", "ipb_member_id=5774855; ipb_pass_hash=4b061c3abe25289568b5a8e0123fb3b9; igneous=cea2e08fb; sk=oye107wk02gtomb56x65dmv4qzbn; nw=1");
|
||||
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
for (Map.Entry<String, String> header: headers.entrySet()){
|
||||
httpGet.addHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
httpResponse = httpClient.execute(httpGet);
|
||||
|
||||
HttpEntity responseEntity = httpResponse.getEntity();
|
||||
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
|
||||
try {
|
||||
if (statusCode == 200) {
|
||||
if (isDirect) {
|
||||
InputStream inputStream = new BufferedInputStream(responseEntity.getContent());
|
||||
byte[] bytes = inputStream.readAllBytes();
|
||||
if(response != null) {
|
||||
OutputStream outputStream = response.getOutputStream();
|
||||
outputStream.write(bytes);
|
||||
outputStream.close();
|
||||
}
|
||||
if(local != null){
|
||||
local.write(bytes);
|
||||
local.close();
|
||||
}
|
||||
inputStream.close();
|
||||
return null;
|
||||
} else {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
String str;
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
|
||||
while ((str = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(str).append("\n");
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
} else {
|
||||
System.out.println(statusCode);
|
||||
return null;
|
||||
}
|
||||
}finally {
|
||||
httpClient.close();
|
||||
httpResponse.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
287
src/main/java/com/lion/lionwebsite/Service/RemoteService.java
Normal file
287
src/main/java/com/lion/lionwebsite/Service/RemoteService.java
Normal file
@ -0,0 +1,287 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import com.lion.lionwebsite.Dao.GalleryMapper;
|
||||
import com.lion.lionwebsite.Domain.Gallery;
|
||||
import com.lion.lionwebsite.Domain.GalleryTask;
|
||||
import com.lion.lionwebsite.Message.*;
|
||||
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.util.concurrent.DefaultPromise;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Service
|
||||
@Data
|
||||
@Slf4j
|
||||
@ConfigurationProperties(prefix = "remote-service")
|
||||
public class RemoteService {
|
||||
|
||||
ChannelFuture channelFuture;
|
||||
|
||||
Channel channel;
|
||||
|
||||
String ip = "5.255.110.45";
|
||||
|
||||
short port = 26321;
|
||||
|
||||
String storagePath;
|
||||
|
||||
@Resource
|
||||
GalleryMapper galleryMapper;
|
||||
|
||||
HashMap<Integer, Promise<AbstractMessage>> promiseHashMap;
|
||||
|
||||
EventLoop eventLoopGroup;
|
||||
|
||||
ExecutorService downloadThread;
|
||||
|
||||
AtomicInteger atomicInteger;
|
||||
|
||||
public RemoteService(){
|
||||
atomicInteger = new AtomicInteger(0);
|
||||
eventLoopGroup = new DefaultEventLoop();
|
||||
downloadThread = Executors.newCachedThreadPool();
|
||||
promiseHashMap = new HashMap<>();
|
||||
try {
|
||||
channelFuture = new Bootstrap()
|
||||
.channel(NioSocketChannel.class)
|
||||
.group(new NioEventLoopGroup())
|
||||
.handler(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel channel){
|
||||
channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 1, 4));
|
||||
channel.pipeline().addLast(new MessageCodec());
|
||||
channel.pipeline().addLast(new LoggingHandler());
|
||||
channel.pipeline().addLast(new MyChannelInboundHandlerAdapter());
|
||||
}
|
||||
}).connect(new InetSocketAddress(ip, port)).sync();
|
||||
log.info("connect success");
|
||||
channel = channelFuture.channel();
|
||||
channel.writeAndFlush(new IdentityMessage());
|
||||
}catch (InterruptedException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAlive(){
|
||||
return !(channelFuture.channel() == null) || channelFuture.channel().isActive();
|
||||
}
|
||||
|
||||
public byte addGalleryToQueue(Gallery gallery, byte type){
|
||||
GalleryTask galleryTask = new GalleryTask();
|
||||
galleryTask.setGid(gallery.getGid());
|
||||
galleryTask.setType(type);
|
||||
|
||||
DownloadPostMessage dpm = new DownloadPostMessage();
|
||||
dpm.messageId = atomicInteger.getAndIncrement();
|
||||
dpm.setGalleryTask(galleryTask);
|
||||
channel.writeAndFlush(dpm);
|
||||
|
||||
DefaultPromise<AbstractMessage> promise = new DefaultPromise<>(eventLoopGroup);
|
||||
promiseHashMap.put(dpm.messageId, promise);
|
||||
try {
|
||||
boolean result = promise.await(10, TimeUnit.SECONDS);
|
||||
if(result){
|
||||
ResponseMessage rsm = (ResponseMessage)promise.getNow();
|
||||
return rsm.getResult();
|
||||
}
|
||||
else return -1;
|
||||
}catch (InterruptedException e){
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public byte deleteGallery(Gallery gallery, byte type){
|
||||
DeleteGalleryMessage dgm = new DeleteGalleryMessage();
|
||||
dgm.setGalleryName(gallery.getName());
|
||||
dgm.setDeleteType(type);
|
||||
dgm.messageId = atomicInteger.getAndIncrement();
|
||||
|
||||
channel.writeAndFlush(dgm);
|
||||
DefaultPromise<AbstractMessage> promise = new DefaultPromise<>(eventLoopGroup);
|
||||
promiseHashMap.put(dgm.messageId, promise);
|
||||
try{
|
||||
boolean result = promise.await(10, TimeUnit.SECONDS);
|
||||
if(result){
|
||||
ResponseMessage rsm = (ResponseMessage) promise.getNow();
|
||||
return rsm.getResult();
|
||||
}else return -1;
|
||||
}catch (InterruptedException e){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public byte cachePreview(String galleryName, short page, File file){
|
||||
|
||||
File parentFile = file.getParentFile();
|
||||
if(!parentFile.isDirectory())
|
||||
if(parentFile.mkdirs())
|
||||
log.info("创建文件夹{}成功", parentFile.getAbsolutePath());
|
||||
else
|
||||
log.error("创建文件夹{}失败", parentFile.getAbsolutePath());
|
||||
|
||||
GalleryRequestMessage grm = new GalleryRequestMessage();
|
||||
grm.setGalleryName(galleryName);
|
||||
grm.setPage(page);
|
||||
|
||||
grm.setType(GalleryRequestMessage.PREVIEW);
|
||||
grm.messageId = atomicInteger.getAndIncrement();
|
||||
DefaultPromise<Object> downloadPromise = new DefaultPromise<>(eventLoopGroup);
|
||||
DefaultPromise<Object> readyPromise = new DefaultPromise<>(eventLoopGroup);
|
||||
|
||||
downloadThread.submit(() -> {
|
||||
try {
|
||||
short port = CustomUtil._findIdlePort();
|
||||
ServerSocketChannel ssChannel = ServerSocketChannel.open();
|
||||
ssChannel.bind(new InetSocketAddress("0.0.0.0", port));
|
||||
grm.setPort(port);
|
||||
readyPromise.setSuccess("");
|
||||
SocketChannel socketChannel = ssChannel.accept();
|
||||
FileChannel fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
|
||||
while (socketChannel.read(byteBuffer) != -1){
|
||||
byteBuffer.flip();
|
||||
fileChannel.write(byteBuffer);
|
||||
byteBuffer.clear();
|
||||
}
|
||||
fileChannel.close();
|
||||
socketChannel.close();
|
||||
ssChannel.close();
|
||||
log.info("缓存预览图:" + galleryName + " " + file.getName());
|
||||
downloadPromise.setSuccess("");
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
try{
|
||||
readyPromise.await();
|
||||
channel.writeAndFlush(grm);
|
||||
DefaultPromise<AbstractMessage> promise = new DefaultPromise<>(eventLoopGroup);
|
||||
promiseHashMap.put(grm.messageId, promise);
|
||||
boolean result = promise.await(10, TimeUnit.SECONDS);
|
||||
if(result){
|
||||
ResponseMessage rsm = (ResponseMessage) promise.getNow();
|
||||
if(rsm.getResult() == 0){
|
||||
downloadPromise.await(10, TimeUnit.SECONDS);
|
||||
return (byte) (downloadPromise.isSuccess()?0:1);
|
||||
}else{
|
||||
return rsm.getResult();
|
||||
}
|
||||
}else {
|
||||
return -1;
|
||||
}
|
||||
}catch (InterruptedException e){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public String queryPageName(String name, int page){
|
||||
GalleryPageQueryMessage gpqm = new GalleryPageQueryMessage();
|
||||
gpqm.setName(name);
|
||||
gpqm.setPage(page);
|
||||
gpqm.messageId = atomicInteger.getAndIncrement();
|
||||
|
||||
channel.writeAndFlush(gpqm);
|
||||
DefaultPromise<AbstractMessage> promise = new DefaultPromise<>(eventLoopGroup);
|
||||
promiseHashMap.put(gpqm.messageId, promise);
|
||||
try{
|
||||
boolean result = promise.await(10, TimeUnit.SECONDS);
|
||||
if(result){
|
||||
gpqm = (GalleryPageQueryMessage) promise.getNow();
|
||||
if(gpqm.getResult() == 0)
|
||||
return gpqm.getPageName();
|
||||
else {
|
||||
log.error("galleryPageQuery error:" + gpqm.getResult());
|
||||
return null;
|
||||
}
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}catch (InterruptedException e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateGallery(Gallery gallery, byte type){
|
||||
GalleryTask galleryTask = new GalleryTask();
|
||||
galleryTask.setGid(gallery.getGid());
|
||||
galleryTask.setType(type);
|
||||
galleryTask.setPages(gallery.getPages());
|
||||
|
||||
UpdateGalleryMessage ugm = new UpdateGalleryMessage();
|
||||
ugm.messageId = atomicInteger.getAndIncrement();
|
||||
ugm.setGalleryTask(galleryTask);
|
||||
channel.writeAndFlush(ugm);
|
||||
|
||||
DefaultPromise<AbstractMessage> promise = new DefaultPromise<>(eventLoopGroup);
|
||||
promiseHashMap.put(ugm.messageId, promise);
|
||||
try {
|
||||
boolean result = promise.await(10, TimeUnit.SECONDS);
|
||||
if(result){
|
||||
ResponseMessage rsm = (ResponseMessage)promise.getNow();
|
||||
}
|
||||
}catch (InterruptedException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
class MyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter{
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
//如果不是响应信息或者响应信息为失败,则打印
|
||||
if(!(msg instanceof ResponseMessage rm) || rm.getResult() != 0)
|
||||
System.out.println(msg);
|
||||
|
||||
//下载状态
|
||||
if(msg instanceof DownloadStatusMessage dsm){
|
||||
GalleryTask[] galleryTasks = dsm.getGalleryTasks();
|
||||
for (GalleryTask galleryTask : galleryTasks) {
|
||||
Gallery gallery = galleryMapper.selectGalleryByGid(galleryTask.getGid());
|
||||
if(galleryTask.getProceeding() == gallery.getPages()){
|
||||
gallery.setStatus("下载完成");
|
||||
gallery.setProceeding(galleryTask.getProceeding());
|
||||
galleryMapper.updateGallery(gallery);
|
||||
}else if(galleryTask.getProceeding() != 0){
|
||||
gallery.setStatus("下载中");
|
||||
gallery.setProceeding(galleryTask.getProceeding());
|
||||
if(!gallery.getName().equals(galleryTask.getName()))
|
||||
gallery.setName(galleryTask.getName());
|
||||
galleryMapper.updateGallery(gallery);
|
||||
log.info(gallery.getName() + "下载进度:" + gallery.getProceeding() + "/" + gallery.getPages());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(msg instanceof ResponseMessage rsm)
|
||||
promiseHashMap.get(rsm.messageId).setSuccess(rsm);
|
||||
else if(msg instanceof GalleryPageQueryMessage gpqm)
|
||||
promiseHashMap.get(gpqm.messageId).setSuccess(gpqm);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
124
src/main/java/com/lion/lionwebsite/Service/TagService.java
Normal file
124
src/main/java/com/lion/lionwebsite/Service/TagService.java
Normal file
@ -0,0 +1,124 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import com.lion.lionwebsite.Dao.GalleryMapper;
|
||||
import com.lion.lionwebsite.Dao.TagMapper;
|
||||
import com.lion.lionwebsite.Domain.Gallery;
|
||||
import com.lion.lionwebsite.Domain.Tag;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
@Service
|
||||
public class TagService {
|
||||
@Resource
|
||||
TagMapper tagMapper;
|
||||
|
||||
@Resource
|
||||
GalleryMapper galleryMapper;
|
||||
|
||||
|
||||
public String createTag(String tagStr){
|
||||
Response response = Response.generateResponse();
|
||||
//查看tag是否存在
|
||||
if(tagMapper.selectTagExistByTag(tagStr) != 0){
|
||||
response.failure("该tag:" + tagStr + "已存在");
|
||||
}else{
|
||||
Tag tag = new Tag(0, tagStr, 0);
|
||||
tagMapper.insertTag(tag);
|
||||
response.success("创建tag:" + tagStr + "成功");
|
||||
response.set("tid", String.valueOf(tag.getId()));
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String selectAllTag(){
|
||||
Response response = Response.generateResponse();
|
||||
ArrayList<Tag> tags = tagMapper.selectAllTag();
|
||||
HashMap<Integer, Tag> tagHashMap = new HashMap<>();
|
||||
for (Tag tag : tags) {
|
||||
tagHashMap.put(tag.getId(), tag);
|
||||
}
|
||||
|
||||
if(tags.isEmpty())
|
||||
response.failure("当前还没有标签");
|
||||
else
|
||||
response.success(new ObjectMapper().valueToTree(tagHashMap).toString());
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String deleteTag(int tagId){
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
if(tagMapper.selectTagUsage(tagId) == 0){
|
||||
tagMapper.deleteTagById(tagId);
|
||||
response.success("标签删除成功");
|
||||
}else{
|
||||
response.failure("标签删除失败,还有本子引用该标签(如果还显示可以删除,请刷新)");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String markTag(int gid, int tid){
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
Gallery gallery = galleryMapper.selectGalleryByGid(gid);
|
||||
if(gallery == null)//查看本子是否存在
|
||||
response.failure("本子不存在,无法标记标签");
|
||||
else if (tagMapper.selectTagExistById(tid) == 0) //查看标签是否存在
|
||||
response.failure("标签不存在,无法标记标签");
|
||||
else
|
||||
if(tagMapper.selectIsMark(gid, tid) != 0) //查看是否已有该条记录
|
||||
response.failure("当前本子已有该标签");
|
||||
else {
|
||||
if (tagMapper.markTag(gid, tid) == 1) {
|
||||
response.success("标记本子成功");
|
||||
tagMapper.incrTagUsage(tid);
|
||||
}
|
||||
else
|
||||
response.failure("未知错误");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String createTagAndMark(int gid, String tagString){
|
||||
Response response = Response.generateResponse();
|
||||
Gallery gallery = galleryMapper.selectGalleryByGid(gid);
|
||||
//查看tag是否存在
|
||||
if(tagMapper.selectTagExistByTag(tagString) != 0){
|
||||
response.failure("该tag:" + tagString + "已存在,请刷新再尝试");
|
||||
}else if(gallery == null) {//查看本子是否存在
|
||||
response.failure("本子不存在,取消创建标签,请刷新再尝试");
|
||||
}else{
|
||||
Tag tag = new Tag(0, tagString, 0);
|
||||
tagMapper.insertTag(tag);
|
||||
if (tagMapper.markTag(gid, tag.getId()) == 1) {
|
||||
response.success("创建标签并标记成功");
|
||||
response.set("tid", String.valueOf(tag.getId()));
|
||||
tagMapper.incrTagUsage(tag.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String disMarkTag(int gid, int tid){
|
||||
Response response = Response.generateResponse();
|
||||
if(tagMapper.disMarkTag(gid, tid) == 0)
|
||||
response.failure("取消标记失败,可能并没有标记");
|
||||
else {
|
||||
response.success("取消标记成功");
|
||||
tagMapper.decrTagUsage(tid);
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
}
|
||||
139
src/main/java/com/lion/lionwebsite/Service/UserServiceImpl.java
Normal file
139
src/main/java/com/lion/lionwebsite/Service/UserServiceImpl.java
Normal file
@ -0,0 +1,139 @@
|
||||
package com.lion.lionwebsite.Service;
|
||||
|
||||
import com.lion.lionwebsite.Dao.CollectMapper;
|
||||
import com.lion.lionwebsite.Dao.GalleryMapper;
|
||||
import com.lion.lionwebsite.Dao.UserMapper;
|
||||
import com.lion.lionwebsite.Domain.User;
|
||||
import com.lion.lionwebsite.Interceptor.TaskHandlerInterceptor;
|
||||
import com.lion.lionwebsite.Util.CustomUtil;
|
||||
import com.lion.lionwebsite.Util.Response;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl{
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
@Resource
|
||||
GalleryMapper galleryMapper;
|
||||
|
||||
@Resource
|
||||
CollectMapper collectMapper;
|
||||
|
||||
@Resource
|
||||
TaskHandlerInterceptor taskHandlerInterceptor;
|
||||
|
||||
public String addAuthCode(String targetAuthCode, String people) {
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
try {
|
||||
userMapper.insertUser(new User(-1, targetAuthCode, people, CustomUtil.now(), true));
|
||||
taskHandlerInterceptor.updateAuthCodes();
|
||||
response.success("插入成功");
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
response.failure("插入失败");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String alterAuthCode(String targetAuthCode, String newAuthCode) {
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
try{
|
||||
if(userMapper.isExist(targetAuthCode) != 0) {
|
||||
userMapper.updateAuthCode(targetAuthCode, newAuthCode);
|
||||
taskHandlerInterceptor.updateAuthCodes();
|
||||
response.success("修改成功");
|
||||
} else {
|
||||
response.failure("授权码不存在");
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
response.failure("修改失败");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String alterUsername(String targetAuthCode, String newUsername){
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
try{
|
||||
if(userMapper.isExist(targetAuthCode) != 0){
|
||||
userMapper.updateUsername(targetAuthCode, newUsername);
|
||||
response.success("修改成功");
|
||||
}else {
|
||||
response.failure("授权码不存在");
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
response.failure("修改失败");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String deleteAuthCode(String targetAuthCode) {
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
if(userMapper.isExist(targetAuthCode) != 0)
|
||||
try{
|
||||
User user = userMapper.selectUserByAuthCode(targetAuthCode);
|
||||
|
||||
//修改下载人以及取消收藏
|
||||
ArrayList<Integer> galleries = collectMapper.selectGidByCollector(user.getId());
|
||||
for(Integer gallery: galleries)
|
||||
collectMapper.disCollect(gallery, user.getId());
|
||||
galleryMapper.updateGalleryDownloader(3, user.getId());
|
||||
|
||||
userMapper.deleteUserByAuthCode(targetAuthCode);
|
||||
response.success("删除成功");
|
||||
taskHandlerInterceptor.updateAuthCodes();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
response.failure("删除失败");
|
||||
}
|
||||
|
||||
else
|
||||
response.failure("授权码不存在");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String alterStatus(String AuthCode, boolean isEnable){
|
||||
Response response = Response.generateResponse();
|
||||
|
||||
if(userMapper.isExist(AuthCode) == 1){
|
||||
int id = userMapper.selectUserByAuthCode(AuthCode).getId();
|
||||
userMapper.updateIsEnableById(id, isEnable);
|
||||
galleryMapper.updateGalleryDownloader(3, id);
|
||||
response.success("修改用户状态成功");
|
||||
taskHandlerInterceptor.updateAuthCodes();
|
||||
}
|
||||
else{
|
||||
response.failure("该用户不存在");
|
||||
}
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String getAllUser() {
|
||||
Response response = Response.generateResponse();
|
||||
User[] users = userMapper.selectAllUser();
|
||||
|
||||
response.success(new ObjectMapper().valueToTree(users).toString());
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public int getUserId(String AuthCode){
|
||||
return userMapper.selectUserByAuthCode(AuthCode).getId();
|
||||
}
|
||||
}
|
||||
122
src/main/java/com/lion/lionwebsite/Util/CustomUtil.java
Normal file
122
src/main/java/com/lion/lionwebsite/Util/CustomUtil.java
Normal file
@ -0,0 +1,122 @@
|
||||
package com.lion.lionwebsite.Util;
|
||||
|
||||
import com.lion.lionwebsite.Domain.MaskDomain;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URL;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@Data
|
||||
public class CustomUtil {
|
||||
|
||||
public static final double ONE_KB = 1024;
|
||||
|
||||
public static final double ONE_MB = ONE_KB * ONE_KB;
|
||||
|
||||
public static final double ONE_GB = ONE_KB * ONE_MB;
|
||||
|
||||
public static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
|
||||
private MaskDomain[] maskDomains;
|
||||
public String restoreUrl(String link){
|
||||
URL url;
|
||||
|
||||
try {
|
||||
url = new URL(link);
|
||||
}catch (MalformedURLException e){
|
||||
return null;
|
||||
}
|
||||
|
||||
for(MaskDomain maskDomain: maskDomains){
|
||||
if(url.getHost().equals(maskDomain.getMask())){
|
||||
return link.replace(maskDomain.getMask(), maskDomain.getRaw());
|
||||
}
|
||||
else if(url.getHost().equals(maskDomain.getRaw())){
|
||||
return link;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String fileSizeToString(long fileSize){
|
||||
if (fileSize < ONE_KB) {
|
||||
return fileSize + "B";
|
||||
} else if (fileSize >= ONE_KB && fileSize < ONE_MB) {
|
||||
return String.format("%.2f", (fileSize / ONE_KB)) + "KB";
|
||||
} else if (fileSize >= ONE_MB && fileSize < ONE_GB) {
|
||||
return String.format("%.2f", (fileSize / ONE_MB)) + "MB";
|
||||
} else{
|
||||
return String.format("%.2f", (fileSize / ONE_GB)) + "GB";
|
||||
}
|
||||
}
|
||||
|
||||
public static long stringToFileSize(String fileSizeString){
|
||||
String preNum = "";
|
||||
String unit = "";
|
||||
|
||||
Pattern patternWithDot = Pattern.compile("(\\d+.\\d+)([A-Z]+)");
|
||||
Matcher matcherWithDot = patternWithDot.matcher(fileSizeString);
|
||||
if(matcherWithDot.find()){
|
||||
preNum = matcherWithDot.group(1);
|
||||
unit = matcherWithDot.group(2);
|
||||
}
|
||||
else {
|
||||
Pattern patternWithoutDot = Pattern.compile("(\\d+)([A-Z]+)");
|
||||
Matcher matcherWithoutDot = patternWithoutDot.matcher(fileSizeString);
|
||||
if (matcherWithoutDot.find()) {
|
||||
preNum = matcherWithoutDot.group(1);
|
||||
unit = matcherWithoutDot.group(2);
|
||||
}
|
||||
}
|
||||
|
||||
double num = Double.parseDouble(preNum);
|
||||
|
||||
return switch (unit) {
|
||||
case "B" -> (long) num;
|
||||
case "KB" -> (long) (ONE_KB * num);
|
||||
case "MB" -> (long) (ONE_MB * num);
|
||||
case "GB" -> (long) (ONE_GB * num);
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static DateTimeFormatter dateTimeFormatter(){
|
||||
return dateTimeFormatter;
|
||||
}
|
||||
|
||||
public static String now(){
|
||||
return dateTimeFormatter.format(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 寻找一定数量的可用端口
|
||||
*
|
||||
* @return 可用端口的起始位置 -1为没有(几乎没有可能)
|
||||
*/
|
||||
public static short _findIdlePort(){
|
||||
for(int i=20000; i<65535; i++){
|
||||
try(ServerSocket ignored = new ServerSocket(i)){
|
||||
return (short) i;
|
||||
}catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void fourZeroFour(HttpServletResponse response){
|
||||
try{
|
||||
response.sendError(404);
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
124
src/main/java/com/lion/lionwebsite/Util/FileDownload.java
Normal file
124
src/main/java/com/lion/lionwebsite/Util/FileDownload.java
Normal file
@ -0,0 +1,124 @@
|
||||
package com.lion.lionwebsite.Util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
|
||||
public class FileDownload {
|
||||
public static void export(HttpServletRequest request, HttpServletResponse response, String path) {
|
||||
File file = new File(path);
|
||||
|
||||
String fileName = file.getName();
|
||||
|
||||
String range = request.getHeader(HttpHeaders.RANGE);
|
||||
|
||||
String rangeSeparator = "-";
|
||||
// 开始下载位置
|
||||
long startByte = 0;
|
||||
// 结束下载位置
|
||||
long endByte = file.length() - 1;
|
||||
|
||||
// 如果是断点续传
|
||||
if (range != null && range.contains("bytes=") && range.contains(rangeSeparator)) {
|
||||
// 设置响应状态码为 206
|
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||
|
||||
range = range.substring(range.lastIndexOf("=") + 1).trim();
|
||||
String[] ranges = range.split(rangeSeparator);
|
||||
try {
|
||||
// 判断 range 的类型
|
||||
if (ranges.length == 1) {
|
||||
// 类型一:bytes=-2343
|
||||
if (range.startsWith(rangeSeparator)) {
|
||||
endByte = Long.parseLong(ranges[0]);
|
||||
}
|
||||
// 类型二:bytes=2343-
|
||||
else if (range.endsWith(rangeSeparator)) {
|
||||
startByte = Long.parseLong(ranges[0]);
|
||||
}
|
||||
}
|
||||
// 类型三:bytes=22-2343
|
||||
else if (ranges.length == 2) {
|
||||
startByte = Long.parseLong(ranges[0]);
|
||||
endByte = Long.parseLong(ranges[1]);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 传参不规范,则直接返回所有内容
|
||||
startByte = 0;
|
||||
endByte = file.length() - 1;
|
||||
}
|
||||
} else {
|
||||
// 没有 ranges 即全部一次性传输,需要用 200 状态码,这一行应该可以省掉,因为默认返回是 200 状态码
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
//要下载的长度(endByte 为总长度 -1,这时候要加回去)
|
||||
long contentLength = endByte - startByte + 1;
|
||||
//文件类型
|
||||
String contentType = request.getServletContext().getMimeType(fileName);
|
||||
|
||||
if (StrUtil.isEmpty(contentType)) {
|
||||
contentType = "attachment";
|
||||
}
|
||||
|
||||
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
|
||||
response.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
|
||||
// 这里文件名换你想要的,inline 表示浏览器可以直接使用
|
||||
// 参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentType + ";filename=\"" + URLUtil.encode(fileName) + "\"");
|
||||
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
|
||||
// [要下载的开始位置]-[结束位置]/[文件总大小]
|
||||
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + startByte + rangeSeparator + endByte + "/" + file.length());
|
||||
|
||||
BufferedOutputStream outputStream;
|
||||
RandomAccessFile randomAccessFile = null;
|
||||
//已传送数据大小
|
||||
long transmitted = 0;
|
||||
try {
|
||||
randomAccessFile = new RandomAccessFile(file, "r");
|
||||
outputStream = new BufferedOutputStream(response.getOutputStream());
|
||||
byte[] buff = new byte[4096];
|
||||
int len = 0;
|
||||
randomAccessFile.seek(startByte);
|
||||
while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buff)) != -1) {
|
||||
outputStream.write(buff, 0, len);
|
||||
transmitted += len;
|
||||
// 本地测试, 防止下载速度过快
|
||||
// Thread.sleep(1);
|
||||
}
|
||||
// 处理不足 buff.length 部分
|
||||
if (transmitted < contentLength) {
|
||||
len = randomAccessFile.read(buff, 0, (int) (contentLength - transmitted));
|
||||
outputStream.write(buff, 0, len);
|
||||
transmitted += len;
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
response.flushBuffer();
|
||||
randomAccessFile.close();
|
||||
// log.trace("下载完毕: {}-{}, 已传输 {}", startByte, endByte, transmitted);
|
||||
} catch (ClientAbortException e) {
|
||||
// ignore 用户停止下载
|
||||
// log.trace("用户停止下载: {}-{}, 已传输 {}", startByte, endByte, transmitted);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (randomAccessFile != null) {
|
||||
randomAccessFile.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
237
src/main/java/com/lion/lionwebsite/Util/GalleryUtil.java
Normal file
237
src/main/java/com/lion/lionwebsite/Util/GalleryUtil.java
Normal file
@ -0,0 +1,237 @@
|
||||
package com.lion.lionwebsite.Util;
|
||||
|
||||
import com.lion.lionwebsite.Domain.Gallery;
|
||||
import com.lion.lionwebsite.Exception.ResolutionNotMatchException;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
|
||||
public class GalleryUtil {
|
||||
|
||||
static String EX_HENTAI = "https://exhentai.org";
|
||||
static String E_HENTAI = "https://e-hentai.org";
|
||||
|
||||
static String POST = "post";
|
||||
static String GET = "get";
|
||||
|
||||
public static ArrayList<Gallery> galleriesForDownload;
|
||||
|
||||
static {
|
||||
galleriesForDownload = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析/下载本子
|
||||
* @param url 本子链接
|
||||
* @param isDownload 是否下载 否则仅解析
|
||||
* @param targetResolution 目标下载分辨率 不下载时改值为空
|
||||
* @return 解析/下载的本子对象
|
||||
* @throws IOException io问题
|
||||
* @throws ResolutionNotMatchException 没有目标分辨率
|
||||
*/
|
||||
public static Gallery parse(String url, boolean isDownload, String targetResolution) throws IOException, ResolutionNotMatchException {
|
||||
String origin = verifyLink(url);
|
||||
|
||||
if(origin == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
//初始化本子
|
||||
Gallery gallery = new Gallery();
|
||||
gallery.setLink(url);
|
||||
gallery.setCreateTime(System.currentTimeMillis()/1000);
|
||||
gallery.setGid(Integer.parseInt(url.split("/")[4]));
|
||||
gallery.setProceeding(0);
|
||||
|
||||
//访问本子页面
|
||||
String galleryPage = requests(url, null, GET, null);
|
||||
Document galleryDoc = Jsoup.parse(galleryPage);
|
||||
|
||||
//收集本子基本信息
|
||||
gallery.setName(galleryDoc.select("#gn").text() + " [" + gallery.getGid() + ']');
|
||||
gallery.setLanguage(galleryDoc.select("#gdd > table > tbody > tr:nth-child(4) > td.gdt2").text().replace("%nbps", ""));
|
||||
gallery.setPages(Integer.parseInt(galleryDoc.select("#gdd > table > tbody > tr:nth-child(6) > td.gdt2").text().split(" ")[0]));
|
||||
gallery.setFileSize(CustomUtil.stringToFileSize(galleryDoc.select("#gdd > table > tbody > tr:nth-child(5) > td.gdt2").text().replace(" ", "")));
|
||||
gallery.setDisplayFileSize(CustomUtil.fileSizeToString(gallery.getFileSize()));
|
||||
|
||||
//查找下载页面链接并初始化参数
|
||||
String download_link = galleryDoc.select("#gd5 > p:nth-child(2) > a").attr("onclick").split("'")[1];
|
||||
HashMap<String, String> extraProperties = new HashMap<>();
|
||||
extraProperties.put("origin", origin);
|
||||
extraProperties.put("referer", download_link);
|
||||
|
||||
|
||||
//访问下载页面,获取分辨率
|
||||
String downloadPage = requests(download_link, extraProperties, GET, null);
|
||||
Document downloadDoc = Jsoup.parse(downloadPage);
|
||||
Elements resolutions = downloadDoc.select("#db > div > table > tbody > tr > td");
|
||||
download_link = downloadDoc.select("#hathdl_form").attr("action");
|
||||
|
||||
Map<String, String> availableResolution = new LinkedHashMap<>();
|
||||
Map<String, String> tempResolutions = new LinkedHashMap<>();
|
||||
ArrayList<Integer> tempResolutionKeys = new ArrayList<>();
|
||||
|
||||
for(Element element: resolutions){
|
||||
String[] parameter = element.select("td > p").text().split(" ");
|
||||
if(parameter[1].contains("N/A"))
|
||||
continue;
|
||||
tempResolutions.put(parameter[0], parameter[1] + parameter[2]);
|
||||
if(parameter[0].contains("x")){
|
||||
tempResolutionKeys.add(Integer.valueOf(parameter[0].replace("x", "")));
|
||||
}
|
||||
}
|
||||
|
||||
tempResolutionKeys.sort(Comparator.comparingInt(o -> o));
|
||||
|
||||
for(Integer resolution: tempResolutionKeys){
|
||||
availableResolution.put(resolution.toString()+"x", tempResolutions.get(resolution+"x"));
|
||||
}
|
||||
|
||||
availableResolution.put("Original", tempResolutions.get("Original"));
|
||||
|
||||
gallery.setAvailableResolution(availableResolution);
|
||||
|
||||
|
||||
//如果不是下载,则直接返回
|
||||
if(!isDownload){
|
||||
gallery.setStatus("等待确认下载");
|
||||
return gallery;
|
||||
}
|
||||
|
||||
//如果目标分辨率不存在,抛出错误
|
||||
if(!gallery.getAvailableResolution().containsKey(targetResolution)){
|
||||
System.out.println(gallery.getAvailableResolution());
|
||||
throw new ResolutionNotMatchException(targetResolution);
|
||||
}
|
||||
|
||||
gallery.setResolution(targetResolution);
|
||||
gallery.setFileSize(CustomUtil.stringToFileSize(availableResolution.get(targetResolution).replace(" ", "")));
|
||||
gallery.setDisplayFileSize(CustomUtil.fileSizeToString(gallery.getFileSize()));
|
||||
|
||||
//提交下载请求
|
||||
downloadPage = requests(download_link, extraProperties, POST, targetResolution);
|
||||
downloadDoc = Jsoup.parse(downloadPage);
|
||||
|
||||
//判断下载请求是否提交成功
|
||||
if(downloadDoc.select("#db > p:nth-child(2) > strong").text().startsWith("#"))
|
||||
gallery.setStatus("已提交");
|
||||
else {
|
||||
System.out.println(downloadDoc.select("#db"));
|
||||
gallery.setStatus("提交失败");
|
||||
}
|
||||
|
||||
return gallery;
|
||||
}
|
||||
|
||||
|
||||
public static String queryUpdateLink(String link) throws IOException{
|
||||
String page = requests(link, null, GET, null);
|
||||
Document document = Jsoup.parse(page);
|
||||
|
||||
Elements select = document.select("#gnd");
|
||||
if(select.size() > 0)
|
||||
return select.select("a").get(0).attributes().get("href");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证链接
|
||||
* @param url 链接
|
||||
* @return origin
|
||||
*/
|
||||
public static String verifyLink(String url){
|
||||
if(url == null)
|
||||
return null;
|
||||
if(url.length() < 40)
|
||||
return null;
|
||||
|
||||
if(!url.contains("/g/") || !url.contains("hentai"))
|
||||
return null;
|
||||
else if (url.contains(EX_HENTAI))
|
||||
return EX_HENTAI;
|
||||
else if (url.contains(E_HENTAI))
|
||||
return E_HENTAI;
|
||||
else{
|
||||
System.out.println(url);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自用request
|
||||
* @param url 链接
|
||||
* @param extraProperties 额外参数
|
||||
* @param method 请求方法
|
||||
* @param targetResolution 目标分辨率
|
||||
* @return 请求页面
|
||||
* @throws IOException 可能会抛出IO错误
|
||||
*/
|
||||
public static String requests(String url, HashMap<String, String> extraProperties, String method, String targetResolution) throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse httpResponse;
|
||||
HashMap<String, String> headers = new HashMap<>();
|
||||
|
||||
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36");
|
||||
headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
|
||||
headers.put("Upgrade-Insecure-Requests", "1");
|
||||
headers.put("Cookie", "ipb_member_id=5774855; ipb_pass_hash=4b061c3abe25289568b5a8e0123fb3b9; igneous=cea2e08fb; sk=oye107wk02gtomb56x65dmv4qzbn; nw=1");
|
||||
|
||||
if(extraProperties != null)
|
||||
headers.putAll(extraProperties);
|
||||
|
||||
if(method.equals(GET)){
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
for (Map.Entry<String, String> header: headers.entrySet()){
|
||||
httpGet.addHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
httpResponse = httpClient.execute(httpGet);
|
||||
} else {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
for (Map.Entry<String, String> header: headers.entrySet()){
|
||||
httpPost.addHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
|
||||
multipartEntityBuilder.addTextBody("hathdl_xres",
|
||||
targetResolution.replace("x", "").replace("Original", "org"));
|
||||
|
||||
httpPost.setEntity(multipartEntityBuilder.build());
|
||||
httpResponse = httpClient.execute(httpPost);
|
||||
}
|
||||
HttpEntity responseEntity = httpResponse.getEntity();
|
||||
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
if(statusCode == 200){
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
|
||||
String str;
|
||||
while((str = reader.readLine()) != null)
|
||||
stringBuilder.append(str).append("\n");
|
||||
}
|
||||
else{
|
||||
System.out.println(statusCode);
|
||||
}
|
||||
|
||||
httpClient.close();
|
||||
httpResponse.close();
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
74
src/main/java/com/lion/lionwebsite/Util/Response.java
Normal file
74
src/main/java/com/lion/lionwebsite/Util/Response.java
Normal file
@ -0,0 +1,74 @@
|
||||
package com.lion.lionwebsite.Util;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Response {
|
||||
HashMap<String, String> result;
|
||||
public Response(){
|
||||
result = new HashMap<>();
|
||||
}
|
||||
|
||||
public static Response generateResponse(){
|
||||
return new Response();
|
||||
}
|
||||
|
||||
public void set(String key, String value){
|
||||
result.put(key, value);
|
||||
}
|
||||
|
||||
public String get(String key){
|
||||
return result.get(key);
|
||||
}
|
||||
|
||||
public void setData(String data){
|
||||
result.put("data", data);
|
||||
}
|
||||
|
||||
public void setResult(String result){
|
||||
this.result.put("result", result);
|
||||
}
|
||||
|
||||
public void success(){
|
||||
setResult("success");
|
||||
}
|
||||
|
||||
public void success(String result){
|
||||
success();
|
||||
setData(result);
|
||||
}
|
||||
|
||||
public void failure(){
|
||||
setResult("failure");
|
||||
}
|
||||
|
||||
public void failure(String result){
|
||||
failure();
|
||||
setData(result);
|
||||
}
|
||||
|
||||
public String getResult(){
|
||||
return result.get("data");
|
||||
}
|
||||
|
||||
|
||||
public static String _failure(String result){
|
||||
Response response = generateResponse();
|
||||
response.failure(result);
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public static String _default(){
|
||||
Response response = Response.generateResponse();
|
||||
response.failure("参数错误");
|
||||
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
public String toJSONString(){
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.valueToTree(result).toString();
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/LionWebsite.db
Normal file
BIN
src/main/resources/LionWebsite.db
Normal file
Binary file not shown.
41
src/main/resources/application.yaml
Normal file
41
src/main/resources/application.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
server:
|
||||
port: 8888
|
||||
tomcat:
|
||||
max-swallow-size: 10000MB
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: org.sqlite.JDBC
|
||||
url: jdbc:sqlite:/root/LionWebsite/LionWebsite.db
|
||||
mvc:
|
||||
view:
|
||||
prefix: /resources/templates/
|
||||
suffix: .html
|
||||
async:
|
||||
request-timeout: 180000
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10000MB
|
||||
max-request-size: 10000MB
|
||||
enabled: true
|
||||
|
||||
mybatis:
|
||||
type-aliases-package: com.lion.lionwebsite.Dao
|
||||
|
||||
personal-service:
|
||||
StoragePath: /root/
|
||||
|
||||
gallery-manage-service:
|
||||
target-path: /root/gallery/
|
||||
cache-size: 100
|
||||
|
||||
remote-service:
|
||||
ip: 5.255.110.45
|
||||
port: 8080
|
||||
|
||||
|
||||
|
||||
local-service:
|
||||
fires: https://api.dler.io/sub?target=clash&new_name=true&url=https%3A%2F%2Ffast.losadhwselfff2332dasd.xyz%2Flink%2Fz0pfwyTvC5naXkbb%3Fclash%3D1&insert=false&config=https%3A%2F%2Fraw.githubusercontent.com%2FACL4SSR%2FACL4SSR%2Fmaster%2FClash%2Fconfig%2FACL4SSR_Online.ini
|
||||
DouNaiClash: https://aaaa.gay/link/A5CXg2cJATerEEoe?client=clashv2
|
||||
DouNaiV2ray: https://aaaa.gay/link/A5CXg2cJATerEEoe?client=v2
|
||||
BIN
src/main/resources/static/favicon.ico
Normal file
BIN
src/main/resources/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
1
src/main/resources/static/index/index.css
Normal file
1
src/main/resources/static/index/index.css
Normal file
File diff suppressed because one or more lines are too long
59
src/main/resources/static/index/index.js
Normal file
59
src/main/resources/static/index/index.js
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/static/mobile/index.css
Normal file
1
src/main/resources/static/mobile/index.css
Normal file
File diff suppressed because one or more lines are too long
59
src/main/resources/static/mobile/index.js
Normal file
59
src/main/resources/static/mobile/index.js
Normal file
File diff suppressed because one or more lines are too long
42
src/main/resources/static/reset.css
Normal file
42
src/main/resources/static/reset.css
Normal file
@ -0,0 +1,42 @@
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
16
src/main/resources/templates/index.html
Normal file
16
src/main/resources/templates/index.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link href="/reset.css" type="text/css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lion</title>
|
||||
<script type="module" src="/index/index.js"></script>
|
||||
<link rel="stylesheet" href="/index/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
16
src/main/resources/templates/mobile.html
Normal file
16
src/main/resources/templates/mobile.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
|
||||
<title>Vite + Vue</title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<script type="module" crossorigin src="/mobile/index.js"></script>
|
||||
<link rel="stylesheet" href="/mobile/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user