diff --git a/.gitignore b/.gitignore index 1ede29b..dc756d7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build/ !**/src/main/**/build/ !**/src/test/**/build/ /src/main/resources/static/ +/temp-filestore/ ### STS ### .apt_generated diff --git a/build.gradle b/build.gradle index a5824ff..72f2736 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'com.rethinkdb:rethinkdb-driver:2.4.4' } tasks.named('test') { diff --git a/src/main/java/de/w665/sharepulse/config/MvcConfig.java b/src/main/java/de/w665/sharepulse/config/MvcConfig.java index 2d4e69a..c1ef35c 100644 --- a/src/main/java/de/w665/sharepulse/config/MvcConfig.java +++ b/src/main/java/de/w665/sharepulse/config/MvcConfig.java @@ -13,5 +13,6 @@ public class MvcConfig implements WebMvcConfigurer { registry.addViewController("/download").setViewName("forward:/index.html"); registry.addViewController("/upload").setViewName("forward:/index.html"); registry.addViewController("/credits").setViewName("forward:/index.html"); + } } diff --git a/src/main/java/de/w665/sharepulse/db/RethinkDBConfig.java b/src/main/java/de/w665/sharepulse/db/RethinkDBConfig.java new file mode 100644 index 0000000..0ad5016 --- /dev/null +++ b/src/main/java/de/w665/sharepulse/db/RethinkDBConfig.java @@ -0,0 +1,18 @@ +package de.w665.sharepulse.db; + +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Getter +@Configuration +public class RethinkDBConfig { + @Value("${rethinkdb.host}") + private String host; + + @Value("${rethinkdb.port}") + private int port; + + @Value("${rethinkdb.database}") + private String database; +} diff --git a/src/main/java/de/w665/sharepulse/db/RethinkDBService.java b/src/main/java/de/w665/sharepulse/db/RethinkDBService.java new file mode 100644 index 0000000..5bd1c03 --- /dev/null +++ b/src/main/java/de/w665/sharepulse/db/RethinkDBService.java @@ -0,0 +1,72 @@ +package de.w665.sharepulse.db; + +import com.rethinkdb.RethinkDB; +import com.rethinkdb.gen.exc.ReqlOpFailedError; +import com.rethinkdb.net.Connection; +import com.rethinkdb.net.Result; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +@Slf4j +@Service +public class RethinkDBService { + private static final RethinkDB r = RethinkDB.r; + private Connection connection; + private final RethinkDBConfig config; + + public RethinkDBService(RethinkDBConfig config) { + this.config = config; + } + + @PostConstruct + public void initialize() { + connection = r.connection().hostname(config.getHost()).port(config.getPort()).connect(); + log.info("Connected to RethinkDB at " + config.getHost() + ":" + config.getPort() + " on database " + config.getDatabase()); + + //rethinkdb check if database exists + + /*Result result = r.dbList().run(connection); + List databases = new ArrayList<>(); + + Object db = result.first(); + ArrayList dbList = (ArrayList) db; + databases.addAll(dbList); + + if (!databases.contains("sharepulse")) { + r.dbCreate("sharepulse").run(connection); + log.info("Database 'sharepulse' created successfully."); + } else { + log.info("Database 'sharepulse' already exists. No action needed."); + }*/ + + + try { + r.dbCreate(config.getDatabase()).run(connection).stream(); + log.info("Database " + config.getDatabase() + " created"); + } catch (ReqlOpFailedError e) { + log.info("Database " + config.getDatabase() + " already exists. Error: " + e.getClass().getSimpleName()); + } + + + + // Additional initialization, like ensuring the database and tables exist + } + + @PreDestroy + public void close() { + if (connection != null) { + connection.close(); + } + } + + // Example method to insert data into a specific table + public void insertData(String tableName, Object data) { + r.db(config.getDatabase()).table(tableName).insert(data).run(connection); + } +} diff --git a/src/main/java/de/w665/sharepulse/model/FileUpload.java b/src/main/java/de/w665/sharepulse/model/FileUpload.java new file mode 100644 index 0000000..3c176d0 --- /dev/null +++ b/src/main/java/de/w665/sharepulse/model/FileUpload.java @@ -0,0 +1,20 @@ +package de.w665.sharepulse.model; + +import lombok.*; + +import java.util.Date; + +@Getter +@Setter +@ToString +@NoArgsConstructor +public class FileUpload { + private String fileId; + private String fileName; + private long fileSize; + private boolean singleDownload; + private Date uploadDate; + private String uploadedByIpAddress; + private long downloadCount; + private String fileDescription; +} diff --git a/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java b/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java index 0ec4577..3a6ba09 100644 --- a/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java +++ b/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java @@ -1,15 +1,39 @@ package de.w665.sharepulse.rest.mappings; import de.w665.sharepulse.rest.ApiRestController; +import de.w665.sharepulse.service.FileService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +@Slf4j @RestController public class Upload extends ApiRestController { + private final FileService fileService; + + @Autowired + public Upload(FileService fileService) { + this.fileService = fileService; + } + // Currently testing - @GetMapping("/upload") - public String getUpload() { - return "Upload works"; + @PostMapping("/upload") + public ResponseEntity getUpload(@RequestParam("file") MultipartFile file, HttpServletRequest request) { + if (file.isEmpty()) { + log.debug("User tried to upload an empty file. IP: " + request.getRemoteAddr()); + return new ResponseEntity<>("Please select a file to upload.", HttpStatus.NOT_ACCEPTABLE); + } + + fileService.processUploadedFile(file, request.getRemoteAddr()); + log.debug("User uploaded file " + file.getOriginalFilename() + " from IP " + request.getRemoteAddr() + " successfully."); + return new ResponseEntity<>("File " + file.getOriginalFilename() + " uploaded successfully", HttpStatus.OK); } } diff --git a/src/main/java/de/w665/sharepulse/service/FileIdService.java b/src/main/java/de/w665/sharepulse/service/FileIdService.java new file mode 100644 index 0000000..457de4b --- /dev/null +++ b/src/main/java/de/w665/sharepulse/service/FileIdService.java @@ -0,0 +1,34 @@ +package de.w665.sharepulse.service; + +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@Service +public class FileIdService { + + private static int dailyCounter = 0; + private static String lastDate = ""; + + // Not safe to use + public static synchronized String generateId() { + String today = new SimpleDateFormat("yyMMdd").format(new Date()); + + if (!today.equals(lastDate)) { + dailyCounter = 0; + lastDate = today; + } + + String counterEncoded = Integer.toString(++dailyCounter, 36).toUpperCase(); + + return today + counterEncoded; + } + + public String generateNewUniqueId() { + return generateId(); + } + + + +} diff --git a/src/main/java/de/w665/sharepulse/service/FileService.java b/src/main/java/de/w665/sharepulse/service/FileService.java new file mode 100644 index 0000000..a526490 --- /dev/null +++ b/src/main/java/de/w665/sharepulse/service/FileService.java @@ -0,0 +1,80 @@ +package de.w665.sharepulse.service; + +import de.w665.sharepulse.model.FileUpload; +import jakarta.annotation.PostConstruct; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Date; + +@Slf4j +@Service +public class FileService { + + private final FileIdService fileIdService; + + @Value("${sharepulse.temp-filestore-path}") + private String tempDirPath; + + @Autowired + public FileService(FileIdService fileIdService) { + this.fileIdService = fileIdService; + } + + public void processUploadedFile(MultipartFile file, String uploaderIp) { + + String fileId = fileIdService.generateNewUniqueId(); + + FileUpload fileUpload = new FileUpload(); + fileUpload.setFileId(fileId); + fileUpload.setFileName(file.getOriginalFilename()); + fileUpload.setFileSize(file.getSize()); + fileUpload.setSingleDownload(false); + fileUpload.setUploadDate(new Date()); + fileUpload.setUploadedByIpAddress(uploaderIp); + fileUpload.setDownloadCount(0); + fileUpload.setFileDescription("fileDescription"); + + System.out.println(fileUpload.toString()); + + + // TODO: rename file to fileID + Path path = Paths.get(getTempDirPath() + File.separator + fileId); + try { + Files.write(path, file.getBytes()); + log.debug("File " + file.getOriginalFilename() + " written to " + path.getFileName()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // create a folder + @PostConstruct + public void createFolder() { + Path path = Paths.get(getTempDirPath()); + log.debug("Creating directory " + path.getFileName()); + try { + if (!Files.exists(path)) { + Files.createDirectory(path); + log.debug("Directory created"); + } else { + log.debug("Directory already exists"); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private String getTempDirPath() { + return System.getProperty("user.dir") + tempDirPath; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 42ab6e6..05b0892 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,24 @@ +# Application config +sharepulse.temp-filestore-path=/temp-filestore + # Static path spring.web.resources.static-locations=classpath:/static/browser/ # Rest API path prefix -spring.data.rest.base-path=/api/v1 \ No newline at end of file +spring.data.rest.base-path=/api/v1 + +# Upload size overwrite +spring.servlet.multipart.max-file-size=1GB +spring.servlet.multipart.max-request-size=1GB + +# Logging +logging.level.de.w665.sharepulse=DEBUG + +# Database +rethinkdb.host=localhost +rethinkdb.port=28015 +rethinkdb.database=sharepulse + +# Miscellaneous +server.port=80 +spring.application.name=sharepulse