mirror of
https://github.com/RoastSlav/quickdrop.git
synced 2025-12-30 11:09:59 -06:00
a working max file size setting in the admin panel
This commit is contained in:
@@ -88,8 +88,9 @@ java -jar target/quickdrop-0.0.1-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
Using an external application.properties file:
|
||||
- Create an **application.properties** file in the same directory as the JAR file or specify its location in the
|
||||
start command.
|
||||
|
||||
- Create an **application.properties** file in the same directory as the JAR file or specify its location in the
|
||||
start command.
|
||||
|
||||
- Add your custom settings, for example (Listed below are the default values):
|
||||
|
||||
|
||||
10
pom.xml
10
pom.xml
@@ -185,6 +185,16 @@
|
||||
test
|
||||
</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter</artifactId>
|
||||
<version>4.1.3</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package org.rostislav.quickdrop;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -11,6 +8,9 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class QuickdropApplication {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.rostislav.quickdrop.config;
|
||||
|
||||
import jakarta.servlet.MultipartConfigElement;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.MultipartConfigFactory;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
@@ -10,16 +11,18 @@ import org.springframework.util.unit.DataSize;
|
||||
@Configuration
|
||||
public class MultipartConfig {
|
||||
private final long ADDITIONAL_REQUEST_SIZE = 1024L * 1024L * 10L; // 10 MB
|
||||
@Value("${max-upload-file-size}")
|
||||
private String maxUploadFileSize;
|
||||
|
||||
@Autowired
|
||||
private MultipartProperties multipartProperties;
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public MultipartConfigElement multipartConfigElement() {
|
||||
MultipartConfigFactory factory = new MultipartConfigFactory();
|
||||
|
||||
factory.setMaxFileSize(DataSize.parse(maxUploadFileSize));
|
||||
factory.setMaxFileSize(DataSize.parse(multipartProperties.getMaxFileSize()));
|
||||
|
||||
DataSize maxRequestSize = DataSize.parse(maxUploadFileSize);
|
||||
DataSize maxRequestSize = DataSize.parse(multipartProperties.getMaxFileSize());
|
||||
maxRequestSize = DataSize.ofBytes(maxRequestSize.toBytes() + ADDITIONAL_REQUEST_SIZE);
|
||||
factory.setMaxRequestSize(maxRequestSize);
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.rostislav.quickdrop.config;
|
||||
|
||||
import org.rostislav.quickdrop.repository.ApplicationSettingsRepository;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@RefreshScope
|
||||
@Component
|
||||
public class MultipartProperties {
|
||||
private final ApplicationSettingsRepository applicationSettingsRepository;
|
||||
|
||||
public MultipartProperties(ApplicationSettingsRepository applicationSettingsRepository) {
|
||||
this.applicationSettingsRepository = applicationSettingsRepository;
|
||||
}
|
||||
|
||||
public String getMaxFileSize() {
|
||||
return "" + applicationSettingsRepository.findById(1L).orElseThrow().getMaxFileSize();
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,11 @@ public class AdminViewController {
|
||||
@GetMapping("/settings")
|
||||
public String getSettingsPage(Model model) {
|
||||
ApplicationSettingsEntity settings = applicationSettingsService.getApplicationSettings();
|
||||
settings.setMaxFileSize(bytesToMegabytes(settings.getMaxFileSize()));
|
||||
|
||||
model.addAttribute("settings", settings);
|
||||
ApplicationSettingsEntity applicationSettingsEntity = new ApplicationSettingsEntity(settings);
|
||||
applicationSettingsEntity.setMaxFileSize(bytesToMegabytes(settings.getMaxFileSize()));
|
||||
|
||||
model.addAttribute("settings", applicationSettingsEntity);
|
||||
return "admin/settings";
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.rostislav.quickdrop.controller;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.rostislav.quickdrop.model.FileEntity;
|
||||
import org.rostislav.quickdrop.service.ApplicationSettingsService;
|
||||
import org.rostislav.quickdrop.service.FileService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -22,18 +23,18 @@ import static org.rostislav.quickdrop.util.FileUtils.populateModelAttributes;
|
||||
@RequestMapping("/file")
|
||||
public class FileViewController {
|
||||
private final FileService fileService;
|
||||
@Value("${max-upload-file-size}")
|
||||
private String maxFileSize;
|
||||
private final ApplicationSettingsService applicationSettingsService;
|
||||
@Value("${file.max.age}")
|
||||
private String maxFileLifeTime;
|
||||
|
||||
public FileViewController(FileService fileService) {
|
||||
public FileViewController(FileService fileService, ApplicationSettingsService applicationSettingsService) {
|
||||
this.fileService = fileService;
|
||||
this.applicationSettingsService = applicationSettingsService;
|
||||
}
|
||||
|
||||
@GetMapping("/upload")
|
||||
public String showUploadFile(Model model) {
|
||||
model.addAttribute("maxFileSize", maxFileSize);
|
||||
model.addAttribute("maxFileSize", applicationSettingsService.getFormattedMaxFileSize());
|
||||
model.addAttribute("maxFileLifeTime", maxFileLifeTime);
|
||||
return "upload";
|
||||
}
|
||||
|
||||
@@ -20,6 +20,21 @@ public class ApplicationSettingsEntity {
|
||||
private String appPasswordHash;
|
||||
private String adminPasswordHash;
|
||||
|
||||
public ApplicationSettingsEntity() {
|
||||
}
|
||||
|
||||
public ApplicationSettingsEntity(ApplicationSettingsEntity settings) {
|
||||
this.id = settings.id;
|
||||
this.maxFileSize = settings.maxFileSize;
|
||||
this.maxFileLifeTime = settings.maxFileLifeTime;
|
||||
this.fileStoragePath = settings.fileStoragePath;
|
||||
this.logStoragePath = settings.logStoragePath;
|
||||
this.fileDeletionCron = settings.fileDeletionCron;
|
||||
this.appPasswordEnabled = settings.appPasswordEnabled;
|
||||
this.appPasswordHash = settings.appPasswordHash;
|
||||
this.adminPasswordHash = settings.adminPasswordHash;
|
||||
}
|
||||
|
||||
public long getMaxFileSize() {
|
||||
return maxFileSize;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package org.rostislav.quickdrop.model;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
public class FileEntity {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.rostislav.quickdrop.repository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.rostislav.quickdrop.model.FileEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FileRepository extends JpaRepository<FileEntity, Long> {
|
||||
@Query("SELECT f FROM FileEntity f WHERE f.uuid = :uuid")
|
||||
Optional<FileEntity> findByUUID(@Param("uuid") String uuid);
|
||||
|
||||
@@ -2,17 +2,23 @@ package org.rostislav.quickdrop.service;
|
||||
|
||||
import org.rostislav.quickdrop.model.ApplicationSettingsEntity;
|
||||
import org.rostislav.quickdrop.repository.ApplicationSettingsRepository;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.cloud.context.refresh.ContextRefresher;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.rostislav.quickdrop.util.FileUtils.formatFileSize;
|
||||
|
||||
@Service
|
||||
public class ApplicationSettingsService {
|
||||
private final ConfigurableApplicationContext applicationContext;
|
||||
private final ApplicationSettingsRepository applicationSettingsRepository;
|
||||
private final ContextRefresher contextRefresher;
|
||||
private ApplicationSettingsEntity applicationSettings;
|
||||
|
||||
public ApplicationSettingsService(ApplicationSettingsRepository applicationSettingsRepository, ApplicationContext applicationContext) {
|
||||
public ApplicationSettingsService(ApplicationSettingsRepository applicationSettingsRepository, ApplicationContext applicationContext, @Qualifier("configDataContextRefresher") ContextRefresher contextRefresher) {
|
||||
this.contextRefresher = contextRefresher;
|
||||
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
|
||||
this.applicationSettingsRepository = applicationSettingsRepository;
|
||||
|
||||
@@ -27,7 +33,6 @@ public class ApplicationSettingsService {
|
||||
settings.setAppPasswordHash("");
|
||||
settings.setAdminPasswordHash("");
|
||||
settings = applicationSettingsRepository.save(settings);
|
||||
this.applicationContext.refresh();
|
||||
return settings;
|
||||
});
|
||||
}
|
||||
@@ -47,12 +52,18 @@ public class ApplicationSettingsService {
|
||||
|
||||
|
||||
applicationSettingsRepository.save(applicationSettingsEntity);
|
||||
this.applicationSettings = applicationSettingsEntity;
|
||||
contextRefresher.refresh();
|
||||
}
|
||||
|
||||
public long getMaxFileSize() {
|
||||
return applicationSettings.getMaxFileSize();
|
||||
}
|
||||
|
||||
public String getFormattedMaxFileSize() {
|
||||
return formatFileSize(applicationSettings.getMaxFileSize());
|
||||
}
|
||||
|
||||
public long getMaxFileLifeTime() {
|
||||
return applicationSettings.getMaxFileLifeTime();
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ import static org.rostislav.quickdrop.util.FileEncryptionUtils.encryptFile;
|
||||
|
||||
@Service
|
||||
public class FileService {
|
||||
@Value("${file.save.path}")
|
||||
private String fileSavePath;
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileService.class);
|
||||
private final FileRepository fileRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
@Value("${file.save.path}")
|
||||
private String fileSavePath;
|
||||
|
||||
public FileService(FileRepository fileRepository, PasswordEncoder passwordEncoder) {
|
||||
this.fileRepository = fileRepository;
|
||||
@@ -184,10 +184,10 @@ public class FileService {
|
||||
Resource resource = new UrlResource(outputFile.toUri());
|
||||
logger.info("Sending file: {}", fileEntity);
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileEntity.name, StandardCharsets.UTF_8) + "\"")
|
||||
.header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
|
||||
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
|
||||
.body(responseBody);
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileEntity.name, StandardCharsets.UTF_8) + "\"")
|
||||
.header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
|
||||
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
|
||||
.body(responseBody);
|
||||
} catch (
|
||||
Exception e) {
|
||||
logger.error("Error reading file: {}", e.getMessage());
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package org.rostislav.quickdrop.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import org.rostislav.quickdrop.model.FileEntity;
|
||||
import org.rostislav.quickdrop.repository.FileRepository;
|
||||
import org.slf4j.Logger;
|
||||
@@ -11,6 +8,9 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ScheduleService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ScheduleService.class);
|
||||
|
||||
@@ -15,6 +15,5 @@ logging.file.name=log/quickdrop.log
|
||||
file.deletion.cron=0 0 2 * * *
|
||||
app.basic.password=test
|
||||
app.enable.password=false
|
||||
max-upload-file-size=1GB
|
||||
#logging.level.org.springframework=DEBUG
|
||||
#logging.level.org.hibernate=DEBUG
|
||||
@@ -1,9 +1,5 @@
|
||||
package org.rostislav.quickdrop;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -20,46 +16,45 @@ import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.rostislav.quickdrop.TestDataContainer.getEmptyFileUploadRequest;
|
||||
import static org.rostislav.quickdrop.TestDataContainer.getFileEntity;
|
||||
import static org.rostislav.quickdrop.TestDataContainer.getFileUploadRequest;
|
||||
import static org.rostislav.quickdrop.TestDataContainer.*;
|
||||
|
||||
@SpringBootTest
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class FileServiceTests {
|
||||
@Nested
|
||||
class SaveFileTests {
|
||||
@Value("${file.save.path}")
|
||||
private String fileSavePath;
|
||||
@Autowired
|
||||
FileService fileService;
|
||||
@MockBean
|
||||
FileRepository fileRepository;
|
||||
@MockBean
|
||||
PasswordEncoder passwordEncoder;
|
||||
@Value("${file.save.path}")
|
||||
private String fileSavePath;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
//Delete the all files in the fileSavePath
|
||||
try {
|
||||
Files.walk(Path.of(fileSavePath))
|
||||
.filter(Files::isRegularFile)
|
||||
.forEach(file -> {
|
||||
try {
|
||||
Files.delete(file);
|
||||
} catch (
|
||||
IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
.filter(Files::isRegularFile)
|
||||
.forEach(file -> {
|
||||
try {
|
||||
Files.delete(file);
|
||||
} catch (
|
||||
IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (
|
||||
IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.rostislav.quickdrop;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.rostislav.quickdrop.model.FileEntity;
|
||||
import org.rostislav.quickdrop.model.FileUploadRequest;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class TestDataContainer {
|
||||
|
||||
public static FileEntity getFileEntity() {
|
||||
|
||||
@@ -9,6 +9,8 @@ spring.thymeleaf.prefix=classpath:/templates/
|
||||
spring.thymeleaf.suffix=.html
|
||||
spring.thymeleaf.cache=false
|
||||
server.tomcat.connection-timeout=60000
|
||||
management.endpoint.refresh.enabled=true
|
||||
management.endpoints.web.exposure.include=refresh
|
||||
file.save.path=files
|
||||
file.max.age=30
|
||||
logging.file.name=log/quickdrop.log
|
||||
|
||||
Reference in New Issue
Block a user