reghao vor 2 Jahren
Ursprung
Commit
a8e7a1a82d
33 geänderte Dateien mit 1256 neuen und 0 gelöschten Zeilen
  1. 212 0
      dfs-web/pom.xml
  2. 11 0
      dfs-web/src/main/java/cn/reghao/dfs/web/DfsWebApplication.java
  3. 59 0
      dfs-web/src/main/java/cn/reghao/dfs/web/config/FileMessageConverter.java
  4. 43 0
      dfs-web/src/main/java/cn/reghao/dfs/web/config/SwaggerConfig.java
  5. 79 0
      dfs-web/src/main/java/cn/reghao/dfs/web/config/WebConfig.java
  6. 73 0
      dfs-web/src/main/java/cn/reghao/dfs/web/controller/BucketBasicJsonController.java
  7. 88 0
      dfs-web/src/main/java/cn/reghao/dfs/web/controller/ObjectBasicJsonController.java
  8. 33 0
      dfs-web/src/main/java/cn/reghao/dfs/web/controller/RegionBasicJsonController.java
  9. 21 0
      dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/BucketMapper.java
  10. 18 0
      dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/DataBlockMapper.java
  11. 26 0
      dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/FileMetaMapper.java
  12. 17 0
      dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/FileTypeMapper.java
  13. 15 0
      dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/RegionMapper.java
  14. 50 0
      dfs-web/src/main/java/cn/reghao/dfs/web/inerceptor/FileAccessInterceptor.java
  15. 74 0
      dfs-web/src/main/java/cn/reghao/dfs/web/inerceptor/JwtTokenFilter.java
  16. 40 0
      dfs-web/src/main/java/cn/reghao/dfs/web/inerceptor/MutableHttpServletRequest.java
  17. 18 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/po/Bucket.java
  18. 15 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/po/ContentRange.java
  19. 30 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/po/DataBlock.java
  20. 36 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/po/FileMeta.java
  21. 20 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/po/FileType.java
  22. 18 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/po/Region.java
  23. 14 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/ApplyServerSideEncryptionByDefault.java
  24. 22 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/BucketDetail.java
  25. 22 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/BucketGetResult.java
  26. 16 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/RegionNames.java
  27. 17 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/Regions.java
  28. 14 0
      dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/ServerSideEncryptionConfiguration.java
  29. 8 0
      dfs-web/src/main/resources/application-dev.yml
  30. 8 0
      dfs-web/src/main/resources/application-test.yml
  31. 26 0
      dfs-web/src/main/resources/application.yml
  32. 68 0
      dfs-web/src/main/resources/logback-spring.xml
  33. 45 0
      dfs-web/src/main/resources/test.json

+ 212 - 0
dfs-web/pom.xml

@@ -0,0 +1,212 @@
+<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>cn.reghao.dfs</groupId>
+    <artifactId>dfs-web</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>2.3.9.RELEASE</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.reghao.jutil</groupId>
+            <artifactId>jdk</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.reghao.jutil</groupId>
+            <artifactId>tool</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.reghao.jutil</groupId>
+            <artifactId>web</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.reghao.dfs</groupId>
+            <artifactId>dfs-api</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.6</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <version>1.2.3</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.2.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </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</artifactId>
+            <version>1.3.2</version>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.17</version>
+        </dependency>
+        <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+            <version>3.3.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>31.1-jre</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-spring-boot-starter</artifactId>
+            <version>2.7.8</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-recipes</artifactId>
+            <version>2.12.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-core</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.activation</groupId>
+            <artifactId>activation</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.drewnoakes</groupId>
+            <artifactId>metadata-extractor</artifactId>
+            <version>2.18.0</version>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>dev</id>
+            <properties>
+                <profile.active>dev</profile.active>
+            </properties>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>test</id>
+            <properties>
+                <profile.active>test</profile.active>
+            </properties>
+        </profile>
+        <profile>
+            <id>prod</id>
+            <properties>
+                <profile.active>prod</profile.active>
+            </properties>
+        </profile>
+    </profiles>
+
+    <build>
+        <finalName>dfs-web</finalName>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>application.yml</include>
+                    <include>application-${profile.active}.yml</include>
+                    <include>mapper/**</include>
+                    <include>*.xml</include>
+                </includes>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.3.9.RELEASE</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 11 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/DfsWebApplication.java

@@ -0,0 +1,11 @@
+package cn.reghao.dfs.web;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DfsWebApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(DfsWebApplication.class, args);
+    }
+}

+ 59 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/config/FileMessageConverter.java

@@ -0,0 +1,59 @@
+package cn.reghao.dfs.web.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.AbstractHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 实现 PUT 请求上传文件
+ * @author reghao
+ * @date 2022-12-28 10:03:22
+ */
+@Slf4j
+public class FileMessageConverter extends AbstractHttpMessageConverter<File> {
+    public FileMessageConverter() {
+        super(MediaType.APPLICATION_OCTET_STREAM);
+    }
+
+    @Override
+    protected boolean supports(Class<?> clazz) {
+        return File.class.isAssignableFrom(clazz);
+    }
+
+    @Override
+    protected File readInternal(Class<? extends File> clazz, HttpInputMessage inputMessage)
+            throws IOException, HttpMessageNotReadableException {
+
+        InputStream inputStream = inputMessage.getBody();
+        File tmpFile = File.createTempFile("upload","tmp");
+        if (inputStream != null) {
+            FileOutputStream outputStream = new FileOutputStream(tmpFile);
+
+            byte[] buffer = new byte[1024];
+            int bytesRead;
+
+            while ((bytesRead = inputStream.read(buffer)) > 0) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+
+            outputStream.flush();
+
+            outputStream.close();
+        }
+        return tmpFile;
+    }
+
+    protected void writeInternal(File file, HttpOutputMessage outputMessage)
+            throws IOException, HttpMessageNotWritableException {
+        log.info("writeInternal");
+    }
+}

+ 43 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/config/SwaggerConfig.java

@@ -0,0 +1,43 @@
+package cn.reghao.dfs.web.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * 配置 Swagger
+ * Swagger 仅在 dev 和 test 环境生效
+ *
+ * @author reghao
+ * @date 2019-05-14 17:01:07
+ */
+@Profile({"dev", "test"})
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("cn.reghao.dfs.web.controller"))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("FileService APIs")
+                .description("")
+                .termsOfServiceUrl("")
+                .version("1.0.0")
+                .build();
+    }
+}

+ 79 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/config/WebConfig.java

@@ -0,0 +1,79 @@
+package cn.reghao.dfs.web.config;
+
+import cn.reghao.dfs.web.inerceptor.FileAccessInterceptor;
+import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2021-12-30 12:34:26
+ */
+@Configuration
+public class WebConfig extends WebMvcConfigurationSupport {
+    private final FileAccessInterceptor fileAccessInterceptor;
+    private final MultipartProperties multipartProperties;
+
+    public WebConfig(FileAccessInterceptor fileAccessInterceptor, MultipartProperties multipartProperties) {
+        this.fileAccessInterceptor = fileAccessInterceptor;
+        this.multipartProperties = multipartProperties;
+    }
+
+    @Override
+    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("swagger-ui.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+
+    /*@Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(fileAccessInterceptor);
+    }*/
+
+    // TODO 若 dfs-gw 中处理了跨域, 那么这里需要注释
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+                .allowedOrigins("*")
+                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
+                .allowCredentials(true)
+                .maxAge(3600)
+                .allowedHeaders("*");
+    }
+
+    /*@Bean
+    public FilterRegistrationBean<Filter> jwtTokenFilter() {
+        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
+        registrationBean.setFilter(new JwtTokenFilter());
+        registrationBean.addUrlPatterns("*");
+        return registrationBean;
+    }*/
+
+    /**
+     * HTTP req/resp 消息转换器
+     *
+     * @param
+     * @return
+     * @date 2022-12-29 上午9:43
+     */
+    @Override
+    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+        // 处理 application/octet-stream 数据
+        converters.add(new FileMessageConverter());
+        // 处理 application/json 数据
+        converters.add(new StringHttpMessageConverter());
+        converters.add(new MappingJackson2HttpMessageConverter());
+        // 处理 application/xml 数据
+        converters.add(new Jaxb2RootElementHttpMessageConverter());
+    }
+}

+ 73 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/controller/BucketBasicJsonController.java

@@ -0,0 +1,73 @@
+package cn.reghao.dfs.web.controller;
+
+import cn.reghao.dfs.api.dto.BucketRegion;
+import cn.reghao.dfs.api.iface.BucketService;
+import cn.reghao.dfs.api.dto.CreateBucket;
+import cn.reghao.dfs.web.model.vo.BucketGetResult;
+import cn.reghao.dfs.api.dto.BucketListResult;
+import cn.reghao.jutil.jdk.result.WebResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2023-01-04 16:46:45
+ */
+@Api(tags = "存储桶基础操作接口")
+@RestController
+@RequestMapping("/ajax")
+public class BucketBasicJsonController {
+    @DubboReference(check = false)
+    private BucketService bucketService;
+
+    @ApiOperation("查找存储桶名字是否存在")
+    @GetMapping(value = "/bucket/judge_exist.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String judgeExist(@RequestParam("region") String region, @RequestParam("bucketName") String bucketName) {
+        boolean exist = bucketService.exist(region, bucketName);
+        if (exist) {
+            return WebResult.success();
+        } else {
+            return WebResult.failWithMsg("not exist");
+        }
+    }
+
+    @ApiOperation("创建存储桶")
+    @PostMapping(value = "/bucket/new_create_bucket.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String createBucket(@Validated CreateBucket createBucket) {
+        bucketService.create(createBucket);
+        return WebResult.success();
+    }
+
+    @ApiOperation("获取存储桶列表")
+    @GetMapping(value = "/bucket/list.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getBucketList() {
+        BucketListResult bucketListResult = bucketService.list();
+        return WebResult.success(bucketListResult);
+    }
+
+    @ApiOperation("获取存储桶地区 KV 列表")
+    @GetMapping(value = "/bucket/list_kv.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getBucketKvList() {
+        List<BucketRegion> list = bucketService.getBucketRegions();
+        return WebResult.success(list);
+    }
+
+    @ApiOperation("获取存储桶标签")
+    @GetMapping(value = "/bucket/tag.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getTag(@RequestParam("region") String region, @RequestParam("bucket") String bucket) {
+        return WebResult.success();
+    }
+
+    @ApiOperation("获取存储桶详情")
+    @GetMapping(value = "/bucket/get.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getBucket(@RequestParam("region") String region, @RequestParam("bucket") String bucket) {
+        BucketGetResult bucketGetResult = new BucketGetResult();
+        return WebResult.success(bucketGetResult);
+    }
+}

+ 88 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/controller/ObjectBasicJsonController.java

@@ -0,0 +1,88 @@
+package cn.reghao.dfs.web.controller;
+
+import cn.reghao.dfs.api.dto.*;
+import cn.reghao.dfs.api.dto.object.GenerateSignedUrl;
+import cn.reghao.dfs.api.dto.object.GenerateSignedUrlResult;
+import cn.reghao.dfs.api.iface.ObjectService;
+import cn.reghao.jutil.jdk.result.WebResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+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 java.util.List;
+
+/**
+ * @author reghao
+ * @date 2023-01-02 17:47:08
+ */
+@Api(tags = "存储桶中对象的基础操作接口")
+@RestController
+@RequestMapping("/ajax")
+public class ObjectBasicJsonController {
+    @DubboReference(check = false)
+    private ObjectService objectService;
+
+    @ApiOperation("获取文件图标")
+    @GetMapping(value = "/bucket/file/file_icon.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getFileIcon() {
+        List<FileIcon> list = objectService.getFileIcons();
+        return WebResult.success(list);
+    }
+
+    @ApiOperation("获取存储桶中的部分对象")
+    @GetMapping(value = "/bucket/file/list_objects.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getObjects(@Validated GetObjects getObjects) {
+        GetObjectsResult getObjectsResult = objectService.list(getObjects);
+        return WebResult.success(getObjectsResult);
+    }
+
+    @ApiOperation("创建目录")
+    @PostMapping(value = "/bucket/file/create_folder.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String createFolder(@Validated CreateFolder createFolder) {
+        objectService.createFolder(createFolder);
+        return WebResult.success();
+    }
+
+    @ApiOperation("获取域名列表")
+    @GetMapping(value = "/bucket/domain/list.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String domainList() {
+        return WebResult.success();
+    }
+
+    @ApiOperation("获取 bucket ACL")
+    @GetMapping(value = "/bucket/acl.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String bucketAcl() {
+        return WebResult.success();
+    }
+
+    @ApiOperation("获取对象 ACL")
+    @GetMapping(value = "/bucket/file/get_object_acl.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String obejctAcl() {
+        return WebResult.success();
+    }
+
+    @ApiOperation("获取对象信息")
+    @GetMapping(value = "/bucket/file/head_object.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String headObject(@Validated HeadObject headObject) {
+        String objectName = headObject.getObjectName();
+        HeadObjectResult headObjectResult = objectService.head(headObject);
+        if (headObjectResult != null) {
+            return WebResult.success(headObjectResult);
+        } else {
+            return WebResult.failWithMsg(objectName + " 不存在");
+        }
+    }
+
+    @ApiOperation("生成带签名的 URL")
+    @PostMapping(value = "/bucket/file/generate_url_with_signed.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String generateSignedUrl(@Validated GenerateSignedUrl generateSignedUrl) {
+        GenerateSignedUrlResult generateSignedUrlResult = objectService.signedUrl(generateSignedUrl);
+        return WebResult.success(generateSignedUrlResult);
+    }
+}

+ 33 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/controller/RegionBasicJsonController.java

@@ -0,0 +1,33 @@
+package cn.reghao.dfs.web.controller;
+
+import cn.reghao.dfs.api.dto.KeyValue;
+import cn.reghao.dfs.api.iface.RegionService;
+import cn.reghao.jutil.jdk.result.WebResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2023-01-10 10:16:48
+ */
+@Api(tags = "地区基础操作接口")
+@RestController
+@RequestMapping("/ajax/region")
+public class RegionBasicJsonController {
+    @DubboReference(check = false)
+    private RegionService regionService;
+
+    @ApiOperation("获取地区列表")
+    @GetMapping(value = "/list.json", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getRegionList() {
+        List<KeyValue> list = regionService.list();
+        return WebResult.success(list);
+    }
+}

+ 21 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/BucketMapper.java

@@ -0,0 +1,21 @@
+package cn.reghao.dfs.web.db.mapper;
+
+import cn.reghao.dfs.web.model.po.Bucket;
+import cn.reghao.dfs.api.dto.BucketInfo;
+import cn.reghao.dfs.api.dto.BucketRegion;
+import cn.reghao.jutil.jdk.db.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 09:23:38
+ */
+@Mapper
+public interface BucketMapper extends BaseMapper<Bucket> {
+    Bucket findByName(@Param("bucketName") String bucketName);
+    List<BucketRegion> findAll1();
+    List<BucketInfo> findBucketInfo();
+}

+ 18 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/DataBlockMapper.java

@@ -0,0 +1,18 @@
+package cn.reghao.dfs.web.db.mapper;
+
+import cn.reghao.dfs.web.model.po.DataBlock;
+import cn.reghao.jutil.jdk.db.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2022-11-24 11:11:24
+ */
+@Mapper
+public interface DataBlockMapper extends BaseMapper<DataBlock> {
+    void update(@Param("objectId") String objectId, @Param("absolutePath") String absolutePath);
+    List<DataBlock> findByObjectId(String objectId);
+}

+ 26 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/FileMetaMapper.java

@@ -0,0 +1,26 @@
+package cn.reghao.dfs.web.db.mapper;
+
+import cn.reghao.dfs.web.model.po.FileMeta;
+import cn.reghao.dfs.api.dto.HeadObjectResult;
+import cn.reghao.jutil.jdk.db.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2022-11-24 11:11:06
+ */
+@Mapper
+public interface FileMetaMapper extends BaseMapper<FileMeta> {
+    void update(@Param("objectId") String objectId, @Param("objectName") String objectName);
+
+    FileMeta findBySha256sum(String sha256sum);
+    FileMeta findByObjectName(String objectName);
+    HeadObjectResult findByObjectName1(String objectName);
+    List<FileMeta> findAll1(@Param("bucket") String bucket, @Param("prefix") String prefix, @Param("max") Integer max);
+    List<FileMeta> findAll2(@Param("bucket") String bucket, @Param("prefix") String prefix,
+                            @Param("start") String start, @Param("max") Integer max);
+    String findByObjectId(String objectId);
+}

+ 17 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/FileTypeMapper.java

@@ -0,0 +1,17 @@
+package cn.reghao.dfs.web.db.mapper;
+
+import cn.reghao.dfs.api.dto.FileIcon;
+import cn.reghao.dfs.web.model.po.FileType;
+import cn.reghao.jutil.jdk.db.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2022-12-27 16:59:15
+ */
+@Mapper
+public interface FileTypeMapper extends BaseMapper<FileType> {
+    List<FileIcon> findAll1();
+}

+ 15 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/db/mapper/RegionMapper.java

@@ -0,0 +1,15 @@
+package cn.reghao.dfs.web.db.mapper;
+
+import cn.reghao.dfs.web.model.po.Region;
+import cn.reghao.jutil.jdk.db.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 09:23:27
+ */
+@Mapper
+public interface RegionMapper extends BaseMapper<Region> {
+    Integer findIdByLocation(@Param("location") String location);
+}

+ 50 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/inerceptor/FileAccessInterceptor.java

@@ -0,0 +1,50 @@
+package cn.reghao.dfs.web.inerceptor;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 拦截对文件的访问, 判断访问是否具有权限
+ *
+ * @author reghao
+ * @date 2021-12-30 12:19:07
+ */
+@Slf4j
+@Component
+public class FileAccessInterceptor implements HandlerInterceptor {
+    // TODO 添加防盗链处理
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+            throws Exception {
+        String uri = request.getRequestURI();
+        String method = request.getMethod();
+        String userAgent = request.getHeader("user-agent");
+        String ipv4 = request.getRemoteAddr();
+
+        String referer = request.getHeader("referer");
+        String sign = request.getParameter("sign");
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response,
+                           Object handler, @Nullable ModelAndView modelAndView) throws Exception {
+
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+                                Object handler, @Nullable Exception ex) throws Exception {
+        String uri = request.getRequestURI();
+        if (!uri.startsWith("/api")) {
+            int statusCode = response.getStatus();
+            log.info("{} -> {}", uri, statusCode);
+        }
+    }
+}

+ 74 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/inerceptor/JwtTokenFilter.java

@@ -0,0 +1,74 @@
+package cn.reghao.dfs.web.inerceptor;
+
+import cn.reghao.jutil.tool.jwt.Jwt;
+import cn.reghao.jutil.tool.jwt.JwtPayload;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.JwtException;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author reghao
+ * @date 2022-08-25 21:14:23
+ */
+public class JwtTokenFilter implements Filter {
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        MutableHttpServletRequest mutableHttpServletRequest = new MutableHttpServletRequest(httpServletRequest);
+        /*String jwtToken = getJwtToken(httpServletRequest);
+        if (jwtToken != null) {
+            try {
+                JwtPayload jwtPayload = Jwt.parse(jwtToken);
+                String userId = jwtPayload.getUserId();
+                mutableHttpServletRequest.putHeader("x-user-id", userId);
+            } catch (JwtException jwtException) {
+                String msg;
+                if (jwtException instanceof ExpiredJwtException) {
+                    msg = "登录已过期, 请重新登录";
+                } else {
+                    // token 无效
+                    msg = "token is invalid";
+                }
+
+                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+                writeResponse(httpServletResponse, msg);
+                return;
+            }
+        } else {
+            mutableHttpServletRequest.putHeader("x-user-id", "-1");
+        }*/
+
+        mutableHttpServletRequest.putHeader("x-user-id", "-1");
+        chain.doFilter(mutableHttpServletRequest, response);
+    }
+
+    private void writeResponse(HttpServletResponse response, String msg) throws IOException {
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        response.setContentType("text/html; charset=utf-8");
+        PrintWriter printWriter = response.getWriter();
+        printWriter.write(msg);
+    }
+
+    private String getJwtToken(HttpServletRequest request) {
+        String headerValue = request.getHeader(Jwt.AUTH_HEADER);
+        if (headerValue != null && headerValue.startsWith(Jwt.JWT_PREFIX)) {
+            return headerValue.replace(Jwt.JWT_PREFIX, "");
+        }
+
+        return null;
+    }
+
+    @Override
+    public void destroy() {
+    }
+}

+ 40 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/inerceptor/MutableHttpServletRequest.java

@@ -0,0 +1,40 @@
+package cn.reghao.dfs.web.inerceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.util.*;
+
+/**
+ * @author reghao
+ * @date 2022-08-25 21:45:31
+ */
+public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
+    private final Map<String, String> customHeaders;
+
+    public MutableHttpServletRequest(HttpServletRequest request) {
+        super(request);
+        this.customHeaders = new HashMap<>();
+    }
+
+    public void putHeader(String name, String value) {
+        customHeaders.put(name, value);
+    }
+
+    public String getHeader(String name) {
+        String value = customHeaders.get(name);
+        if (value != null) {
+            return value;
+        }
+
+        return ((HttpServletRequest) getRequest()).getHeader(name);
+    }
+
+    public Enumeration<String> getHeaderNames() {
+        Set<String> set = new HashSet<>(customHeaders.keySet());
+        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
+        while (e.hasMoreElements()) {
+            set.add(e.nextElement());
+        }
+        return Collections.enumeration(set);
+    }
+}

+ 18 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/po/Bucket.java

@@ -0,0 +1,18 @@
+package cn.reghao.dfs.web.model.po;
+
+import cn.reghao.jutil.jdk.db.BaseObject;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 09:22:20
+ */
+@Getter
+@Setter
+public class Bucket extends BaseObject<Integer> {
+    private int regionId;
+    private String name;
+    private String storage;
+    private String acl;
+}

+ 15 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/po/ContentRange.java

@@ -0,0 +1,15 @@
+package cn.reghao.dfs.web.model.po;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author reghao
+ * @date 2022-11-28 11:27:27
+ */
+@AllArgsConstructor
+@Getter
+public class ContentRange {
+    private long start;
+    private long end;
+}

+ 30 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/po/DataBlock.java

@@ -0,0 +1,30 @@
+package cn.reghao.dfs.web.model.po;
+
+import cn.reghao.jutil.jdk.db.BaseObject;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author reghao
+ * @date 2022-11-24 10:25:18
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+public class DataBlock extends BaseObject<Integer> {
+    private String objectId;
+    private int index;
+    private String blockId;
+    //private long size;
+    private String absolutePath;
+    private long start;
+    private long end;
+
+    public DataBlock(String objectId, int index, String blockId, String absolutePath) {
+        this.objectId = objectId;
+        this.index = index;
+        this.blockId = blockId;
+        this.absolutePath = absolutePath;
+    }
+}

+ 36 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/po/FileMeta.java

@@ -0,0 +1,36 @@
+package cn.reghao.dfs.web.model.po;
+
+import cn.reghao.jutil.jdk.db.BaseObject;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * 文件元数据
+ *
+ * @author reghao
+ * @date 2022-11-21 10:53:10
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+public class FileMeta extends BaseObject<Integer> {
+    private String objectName;
+    private String objectId;
+    private String filename;
+    private long size;
+    private Integer fileTypeId;
+    private String contentType;
+    private String sha256sum;
+    private String bucket;
+
+    // 目录对象
+    public FileMeta(String objectName, String objectId, String filename, String bucket) {
+        this.objectName = objectName;
+        this.objectId = objectId;
+        this.filename = filename;
+        this.size = 0;
+        this.fileTypeId = 1000;
+        this.bucket = bucket;
+    }
+}

+ 20 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/po/FileType.java

@@ -0,0 +1,20 @@
+package cn.reghao.dfs.web.model.po;
+
+import cn.reghao.jutil.jdk.db.BaseObject;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author reghao
+ * @date 2022-12-27 16:55:26
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+public class FileType extends BaseObject<Integer> {
+    private String code;
+    private String name;
+    private String icon;
+    private String iconLarge;
+}

+ 18 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/po/Region.java

@@ -0,0 +1,18 @@
+package cn.reghao.dfs.web.model.po;
+
+import cn.reghao.jutil.jdk.db.BaseObject;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 09:22:15
+ */
+@Setter
+@Getter
+public class Region extends BaseObject<Integer> {
+    private String location;
+    private String nameZhCn;
+    private String nameEnUs;
+    private String endpoint;
+}

+ 14 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/ApplyServerSideEncryptionByDefault.java

@@ -0,0 +1,14 @@
+package cn.reghao.dfs.web.model.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 16:45:47
+ */
+@Getter
+@Setter
+public class ApplyServerSideEncryptionByDefault {
+    private String sselgorithm;
+}

+ 22 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/BucketDetail.java

@@ -0,0 +1,22 @@
+package cn.reghao.dfs.web.model.vo;
+
+import cn.reghao.dfs.api.dto.Owner;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 16:44:05
+ */
+@Getter
+@Setter
+public class BucketDetail {
+    private String region;
+    private String location;
+    private String name;
+    private String storageClass;
+    private Long creationDate;
+    private String extranetEndpoint;
+    private String intranetEndpoint;
+    private Owner owner;
+}

+ 22 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/BucketGetResult.java

@@ -0,0 +1,22 @@
+package cn.reghao.dfs.web.model.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 16:43:23
+ */
+@Getter
+@Setter
+public class BucketGetResult {
+    private BucketDetail bucket;
+    private String cannedACL;
+    private String comment;
+    private String dataRedundancyType;
+    private List<Object> grants;
+    private ServerSideEncryptionConfiguration serverSideEncryptionConfiguration;
+    private String requestId;
+}

+ 16 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/RegionNames.java

@@ -0,0 +1,16 @@
+package cn.reghao.dfs.web.model.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 09:51:16
+ */
+@Deprecated
+@Getter
+@Setter
+public class RegionNames {
+    private String enUs;
+    private String zhCn;
+}

+ 17 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/Regions.java

@@ -0,0 +1,17 @@
+package cn.reghao.dfs.web.model.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 09:51:39
+ */
+@Deprecated
+@Getter
+@Setter
+public class Regions {
+    private RegionNames regionNames;
+    private String endpoint;
+    private String regionLocation;
+}

+ 14 - 0
dfs-web/src/main/java/cn/reghao/dfs/web/model/vo/ServerSideEncryptionConfiguration.java

@@ -0,0 +1,14 @@
+package cn.reghao.dfs.web.model.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2023-01-05 16:45:38
+ */
+@Getter
+@Setter
+public class ServerSideEncryptionConfiguration {
+    private ApplyServerSideEncryptionByDefault applyServerSideEncryptionByDefault;
+}

+ 8 - 0
dfs-web/src/main/resources/application-dev.yml

@@ -0,0 +1,8 @@
+dubbo:
+  registry:
+    address: zookeeper://localhost:2181
+spring:
+  datasource:
+    url: jdbc:mysql://localhost:3306/reghao_oss_rdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
+    username: dev
+    password: Dev@123456

+ 8 - 0
dfs-web/src/main/resources/application-test.yml

@@ -0,0 +1,8 @@
+dubbo:
+  registry:
+    address: zookeeper://192.168.0.110:2181
+spring:
+  datasource:
+    url: jdbc:mysql://192.168.0.110:3306/reghao_oss_tdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
+    username: test
+    password: Test@123456

+ 26 - 0
dfs-web/src/main/resources/application.yml

@@ -0,0 +1,26 @@
+server:
+  port: 8020
+spring:
+  application:
+    name: dfs-web
+  profiles:
+    active: @profile.active@
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    type: com.zaxxer.hikari.HikariDataSource
+    hikari:
+      minimum-idle: 5
+      maximum-pool-size: 10
+      auto-commit: true
+      idle-timeout: 30000
+      pool-name: EvaluationHikariCP
+      max-lifetime: 1800000
+      connection-timeout: 30000
+      connection-test-query: SELECT 1
+mybatis:
+  configuration:
+    map-underscore-to-camel-case: true
+    # TODO mybatis sql 执行日志
+#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  mapper-locations: classpath*:mapper/**/**.xml
+  type-aliases-package: cn.reghao.dfs.web.model.po

+ 68 - 0
dfs-web/src/main/resources/logback-spring.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration>
+    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
+        <layout class="ch.qos.logback.classic.PatternLayout">
+            <pattern>
+                %d{HH:mm:ss.SSS} [%thread] %-5level %c %M %L - %msg%n
+            </pattern>
+        </layout>
+    </appender>
+
+    <!-- info 日志文件 -->
+    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>DENY</onMatch>
+            <onMismatch>ACCEPT</onMismatch>
+        </filter>
+        <encoder>
+            <pattern>
+                %d{HH:mm:ss.SSS} %-5level %c %M %L - %msg%n
+            </pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <!-- 滚动策略 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>
+                logs/info.%d.log
+            </fileNamePattern>
+        </rollingPolicy>
+    </appender>
+
+    <!-- error 日志文件 -->
+    <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <encoder>
+            <pattern>
+                %d{HH:mm:ss.SSS} %-5level %c %M %L - %msg%n
+            </pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>
+                logs/error.%d.log
+            </fileNamePattern>
+        </rollingPolicy>
+    </appender>
+
+    <springProfile name="dev">
+        <root level="info">
+            <appender-ref ref="consoleLog"></appender-ref>
+        </root>
+    </springProfile>
+    <springProfile name="test">
+        <root level="info">
+            <appender-ref ref="fileInfoLog"></appender-ref>
+            <appender-ref ref="fileErrorLog"></appender-ref>
+        </root>
+    </springProfile>
+    <springProfile name="prod">
+        <root level="info">
+            <appender-ref ref="fileInfoLog"></appender-ref>
+            <appender-ref ref="fileErrorLog"></appender-ref>
+        </root>
+    </springProfile>
+</configuration>

Datei-Diff unterdrückt, da er zu groß ist
+ 45 - 0
dfs-web/src/main/resources/test.json


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.