From e05040bf3a46bc4b4885680576e507b85974d99e Mon Sep 17 00:00:00 2001 From: vovastelmashchuk Date: Thu, 8 Aug 2024 15:37:20 +0300 Subject: [PATCH] Remove minio as data store --- .../src/main/java/com/nestapp/AppComponent.kt | 12 ++- .../main/java/com/nestapp/RestController.kt | 16 ++- .../nestapp/minio/MinioProjectRepository.kt | 38 +++++++ .../com/nestapp/minio/ProjectRepository.kt | 99 ------------------- .../com/nestapp/mongo/ProjectRepository.kt | 42 ++++++++ .../main/java/com/nestapp/nest/NestRestApi.kt | 6 +- .../com/nestapp/project/ProjectDetails.kt | 33 ++++--- .../java/com/nestapp/project/ProjectMaker.kt | 24 ++++- .../nestapp/project/RestProjectController.kt | 9 +- 9 files changed, 147 insertions(+), 132 deletions(-) create mode 100644 backend/src/main/java/com/nestapp/minio/MinioProjectRepository.kt delete mode 100644 backend/src/main/java/com/nestapp/minio/ProjectRepository.kt create mode 100644 backend/src/main/java/com/nestapp/mongo/ProjectRepository.kt diff --git a/backend/src/main/java/com/nestapp/AppComponent.kt b/backend/src/main/java/com/nestapp/AppComponent.kt index 47ae4c0..b9ebaab 100644 --- a/backend/src/main/java/com/nestapp/AppComponent.kt +++ b/backend/src/main/java/com/nestapp/AppComponent.kt @@ -2,9 +2,15 @@ package com.nestapp import com.mongodb.kotlin.client.coroutine.MongoClient import com.nestapp.minio.MinioFileUpload -import com.nestapp.minio.ProjectRepository +import com.nestapp.minio.MinioProjectRepository import com.nestapp.mongo.NestHistoryRepository +import com.nestapp.mongo.ProjectDatabase +import com.nestapp.mongo.ProjectRepository import io.minio.MinioClient +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.bson.types.ObjectId class AppComponent( val configuration: Configuration, @@ -17,10 +23,12 @@ class AppComponent( val minioFileUpload = MinioFileUpload(minioClient) - val projectRepository: ProjectRepository = ProjectRepository(minioClient, minioFileUpload) + val minioProjectRepository: MinioProjectRepository = MinioProjectRepository(minioClient, minioFileUpload) private val mongoClient = MongoClient.create(connectionString = configuration.mongoUrl) val nestHistoryRepository = NestHistoryRepository(mongoClient) + val projectRepository = ProjectRepository(mongoClient) + } diff --git a/backend/src/main/java/com/nestapp/RestController.kt b/backend/src/main/java/com/nestapp/RestController.kt index 182ed83..5d3cfb8 100644 --- a/backend/src/main/java/com/nestapp/RestController.kt +++ b/backend/src/main/java/com/nestapp/RestController.kt @@ -1,7 +1,6 @@ package com.nestapp import com.nestapp.files.svg.SvgWriter -import com.nestapp.minio.MinioFileUpload import com.nestapp.nest.PolygonGenerator import com.nestapp.nest.jaguar.JaguarRequest import com.nestapp.nest.nestRestApi @@ -9,6 +8,7 @@ import com.nestapp.project.ProjectMaker import com.nestapp.project.projectsRestController import io.ktor.client.HttpClient import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application import io.ktor.server.application.call @@ -16,6 +16,8 @@ import io.ktor.server.application.install import io.ktor.server.plugins.autohead.AutoHeadResponse import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.plugins.cors.routing.CORS +import io.ktor.server.plugins.statuspages.StatusPages +import io.ktor.server.response.respond import io.ktor.server.response.respondText import io.ktor.server.routing.Route import io.ktor.server.routing.get @@ -39,6 +41,13 @@ fun createHttpClient(): HttpClient { fun Application.restConfig( appComponent: AppComponent, ) { + install(StatusPages) { + exception { cause, throwable -> + println(throwable.printStackTrace()) + cause.respond(HttpStatusCode.InternalServerError, "Error: $throwable") + } + } + install(AutoHeadResponse) install(CORS) { @@ -69,18 +78,19 @@ fun Route.setupRouter( ) { projectsRestController( configuration = appComponent.configuration, - projectRepository = appComponent.projectRepository, projectMaker = ProjectMaker( + minioProjectRepository = appComponent.minioProjectRepository, projectRepository = appComponent.projectRepository, svgWriter = SvgWriter(), polygonGenerator = PolygonGenerator(), ), + projectRepository = appComponent.projectRepository, ) nestRestApi( jaguarRequest = JaguarRequest(client), polygonGenerator = PolygonGenerator(), - projectRepository = appComponent.projectRepository, + minioProjectRepository = appComponent.minioProjectRepository, nestHistoryRepository = appComponent.nestHistoryRepository, configuration = appComponent.configuration, minioFileUpload = appComponent.minioFileUpload, diff --git a/backend/src/main/java/com/nestapp/minio/MinioProjectRepository.kt b/backend/src/main/java/com/nestapp/minio/MinioProjectRepository.kt new file mode 100644 index 0000000..ba2c749 --- /dev/null +++ b/backend/src/main/java/com/nestapp/minio/MinioProjectRepository.kt @@ -0,0 +1,38 @@ +package com.nestapp.minio + +import io.minio.GetObjectArgs +import io.minio.MinioClient +import io.minio.errors.MinioException +import java.io.InputStream + +class MinioProjectRepository( + private val minioClient: MinioClient, + private val minioFileUpload: MinioFileUpload, +) { + + companion object { + private const val BUCKET_NAME = "nest2d" + } + + + fun uploadFileToMinioByteArray(bytes: ByteArray, contentType: String, objectName: String) { + minioFileUpload.uploadFileToMinioByteArray(bytes, contentType, objectName) + } + + fun getDxfFileAsStream(projectSlug: String, fileName: String): InputStream? { + return try { + minioClient.getObject( + GetObjectArgs.builder() + .bucket(BUCKET_NAME) + .`object`("projects/$projectSlug/files/$fileName.dxf") + .build() + ) + } catch (e: MinioException) { + println("Error occurred: ${e.message}") + return null + } catch (e: Exception) { + e.printStackTrace() + return null + } + } +} diff --git a/backend/src/main/java/com/nestapp/minio/ProjectRepository.kt b/backend/src/main/java/com/nestapp/minio/ProjectRepository.kt deleted file mode 100644 index 7ec04d6..0000000 --- a/backend/src/main/java/com/nestapp/minio/ProjectRepository.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.nestapp.minio - -import io.minio.GetObjectArgs -import io.minio.ListObjectsArgs -import io.minio.MinioClient -import io.minio.PutObjectArgs -import io.minio.errors.MinioException -import java.io.InputStream - -class ProjectRepository( - private val minioClient: MinioClient, - private val minioFileUpload: MinioFileUpload, -) { - - companion object { - private const val BUCKET_NAME = "nest2d" - } - - - fun getProjectList(): List { - val folderList = mutableListOf() - try { - minioClient.listObjects( - ListObjectsArgs.builder() - .bucket(BUCKET_NAME) - .prefix("projects/") - .recursive(false) - .build() - ).forEach { result -> - val objectName = result.get().objectName() - val projectName = objectName.split("/")[1] - val preview = "files/$objectName/media/preview.png" - - folderList.add( - Project( - name = projectName, - preview = preview - ) - ) - } - } catch (e: MinioException) { - println("Error occurred: ${e.message}") - } catch (e: Exception) { - e.printStackTrace() - } - return folderList - } - - fun getProjectSvgFiles(project: String): List { - val projectFiles = mutableListOf() - try { - minioClient.listObjects( - ListObjectsArgs.builder() - .bucket(BUCKET_NAME) - .prefix("projects/$project/files") - .recursive(true) - .build() - ).forEach { result -> - val objectName = result.get().objectName() - val fileName = objectName.split("/").last() - - val fileNameWithoutExtension = fileName.split(".").first() - projectFiles.add(fileNameWithoutExtension) - } - } catch (e: MinioException) { - println("Error occurred: ${e.message}") - } catch (e: Exception) { - e.printStackTrace() - } - - return projectFiles.toList() - } - - fun uploadFileToMinioByteArray(bytes: ByteArray, contentType: String, objectName: String) { - minioFileUpload.uploadFileToMinioByteArray(bytes, contentType, objectName) - } - - fun getDxfFileAsStream(projectSlug: String, fileName: String): InputStream? { - return try { - minioClient.getObject( - GetObjectArgs.builder() - .bucket(BUCKET_NAME) - .`object`("projects/$projectSlug/files/$fileName.dxf") - .build() - ) - } catch (e: MinioException) { - println("Error occurred: ${e.message}") - return null - } catch (e: Exception) { - e.printStackTrace() - return null - } - } - - data class Project( - val name: String, - val preview: String, - ) -} diff --git a/backend/src/main/java/com/nestapp/mongo/ProjectRepository.kt b/backend/src/main/java/com/nestapp/mongo/ProjectRepository.kt new file mode 100644 index 0000000..3980604 --- /dev/null +++ b/backend/src/main/java/com/nestapp/mongo/ProjectRepository.kt @@ -0,0 +1,42 @@ +package com.nestapp.mongo + +import com.mongodb.client.model.Filters +import com.mongodb.kotlin.client.coroutine.MongoClient +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.toList +import org.bson.codecs.pojo.annotations.BsonId +import org.bson.types.ObjectId + +class ProjectRepository( + private val client: MongoClient +) { + + private val database by lazy { client.getDatabase("nest2d") } + + suspend fun insertProject(projectDatabase: ProjectDatabase) { + val collection = database.getCollection(collectionName = "projects") + collection.insertOne(projectDatabase) + } + + suspend fun getProjects(): List { + val collection = database.getCollection(collectionName = "projects") + return collection.find().toList() + } + + suspend fun getProject(slug: String): ProjectDatabase { + val collection = database.getCollection(collectionName = "projects") + val query = Filters.eq("projectSlug", slug) + return collection.find(query).firstOrNull() + ?: throw IllegalArgumentException("Project not found $slug") + } +} + +data class ProjectDatabase( + @BsonId + val id: ObjectId, + val projectSlug: String, + val name: String, + val files: List, + val preview: String?, +) + diff --git a/backend/src/main/java/com/nestapp/nest/NestRestApi.kt b/backend/src/main/java/com/nestapp/nest/NestRestApi.kt index cdf35be..ce4e2bd 100644 --- a/backend/src/main/java/com/nestapp/nest/NestRestApi.kt +++ b/backend/src/main/java/com/nestapp/nest/NestRestApi.kt @@ -5,7 +5,7 @@ import com.nestapp.files.dxf.DxfWriter import com.nestapp.files.dxf.reader.DXFReader import com.nestapp.files.svg.SvgWriter import com.nestapp.minio.MinioFileUpload -import com.nestapp.minio.ProjectRepository +import com.nestapp.minio.MinioProjectRepository import com.nestapp.mongo.NestHistoryRepository import com.nestapp.nest.jaguar.JaguarNestInput import com.nestapp.nest.jaguar.JaguarRequest @@ -23,7 +23,7 @@ import org.bson.types.ObjectId fun Route.nestRestApi( jaguarRequest: JaguarRequest, polygonGenerator: PolygonGenerator, - projectRepository: ProjectRepository, + minioProjectRepository: MinioProjectRepository, nestHistoryRepository: NestHistoryRepository, configuration: Configuration, minioFileUpload: MinioFileUpload, @@ -38,7 +38,7 @@ fun Route.nestRestApi( } .flatMap { (file, count) -> val dxfReader = DXFReader() - dxfReader.parseFile(projectRepository.getDxfFileAsStream(nestInput.projectSlug, file)) + dxfReader.parseFile(minioProjectRepository.getDxfFileAsStream(nestInput.projectSlug, file)) val entities = dxfReader.entities polygonGenerator.getMergedAndCombinedPolygons(entities, nestInput.tolerance).map { JaguarNestInput.NestInputPolygons( diff --git a/backend/src/main/java/com/nestapp/project/ProjectDetails.kt b/backend/src/main/java/com/nestapp/project/ProjectDetails.kt index 804fc1f..15e4c1a 100644 --- a/backend/src/main/java/com/nestapp/project/ProjectDetails.kt +++ b/backend/src/main/java/com/nestapp/project/ProjectDetails.kt @@ -1,7 +1,7 @@ package com.nestapp.project import com.nestapp.Configuration -import com.nestapp.minio.ProjectRepository +import com.nestapp.mongo.ProjectRepository import io.ktor.http.HttpStatusCode import io.ktor.server.application.call import io.ktor.server.plugins.NotFoundException @@ -18,22 +18,25 @@ fun Route.projectDetails( get("/project/{project_slug}") { val slug = call.parameters["project_slug"] ?: throw NotFoundException("Project not found") - val projectFiles = projectRepository.getProjectSvgFiles(slug) - .distinct() + try { + val projectFiles = projectRepository.getProject(slug) - val response = ProjectDetailsResponse( - slug = slug, - name = slug, - files = projectFiles - .map { fileName -> - ProjectDetailsResponse.ProjectFile( - name = fileName, - preview = "${configuration.baseUrl}/files/projects/${slug}/files/$fileName.svg", - ) - } - ) + val response = ProjectDetailsResponse( + slug = slug, + name = slug, + files = projectFiles.files + .map { fileName -> + ProjectDetailsResponse.ProjectFile( + name = fileName, + preview = "${configuration.baseUrl}/files/projects/${slug}/files/$fileName.svg", + ) + } + ) - call.respond(HttpStatusCode.OK, response) + call.respond(HttpStatusCode.OK, response) + } catch (e: Exception) { + e.printStackTrace() + } } } diff --git a/backend/src/main/java/com/nestapp/project/ProjectMaker.kt b/backend/src/main/java/com/nestapp/project/ProjectMaker.kt index f94fe1e..38e9130 100644 --- a/backend/src/main/java/com/nestapp/project/ProjectMaker.kt +++ b/backend/src/main/java/com/nestapp/project/ProjectMaker.kt @@ -2,17 +2,21 @@ package com.nestapp.project import com.nestapp.files.dxf.reader.DXFReader import com.nestapp.files.svg.SvgWriter -import com.nestapp.minio.ProjectRepository +import com.nestapp.minio.MinioProjectRepository +import com.nestapp.mongo.ProjectDatabase +import com.nestapp.mongo.ProjectRepository import com.nestapp.nest.PolygonGenerator +import org.bson.types.ObjectId import java.util.Locale class ProjectMaker( + private val minioProjectRepository: MinioProjectRepository, private val projectRepository: ProjectRepository, private val svgWriter: SvgWriter, private val polygonGenerator: PolygonGenerator, ) { - fun makeProject( + suspend fun makeProject( projectName: String, previewFile: ByteArray?, previewFileNameExtension: String, @@ -21,7 +25,7 @@ class ProjectMaker( val slug = createProjectSlug(projectName) previewFile?.let { - projectRepository.uploadFileToMinioByteArray( + minioProjectRepository.uploadFileToMinioByteArray( it, "image/png", "projects/$slug/media/preview.$previewFileNameExtension" @@ -29,7 +33,7 @@ class ProjectMaker( } dxfFileBytes.forEach { (fileName, fileStream) -> - projectRepository.uploadFileToMinioByteArray( + minioProjectRepository.uploadFileToMinioByteArray( fileStream, "application/dxf", "projects/$slug/files/$fileName" @@ -43,13 +47,23 @@ class ProjectMaker( val fileNameWithoutExtension = fileName.substringBeforeLast(".") val svgString = svgWriter.buildSvgString(polygons) - projectRepository.uploadFileToMinioByteArray( + minioProjectRepository.uploadFileToMinioByteArray( bytes = svgString.toByteArray(), contentType = "image/svg+xml", objectName = "projects/$slug/files/$fileNameWithoutExtension.svg" ) } + projectRepository.insertProject( + ProjectDatabase( + id = ObjectId(), + name = projectName, + projectSlug = slug, + preview = previewFile?.let { "files/projects/$slug/media/preview.png" }, + files = dxfFileBytes.map { (fileName, _) -> fileName }, + ) + ) + return slug } diff --git a/backend/src/main/java/com/nestapp/project/RestProjectController.kt b/backend/src/main/java/com/nestapp/project/RestProjectController.kt index 92f3229..f276709 100644 --- a/backend/src/main/java/com/nestapp/project/RestProjectController.kt +++ b/backend/src/main/java/com/nestapp/project/RestProjectController.kt @@ -1,7 +1,7 @@ package com.nestapp.project import com.nestapp.Configuration -import com.nestapp.minio.ProjectRepository +import com.nestapp.mongo.ProjectRepository import io.ktor.http.HttpStatusCode import io.ktor.http.content.PartData import io.ktor.http.content.forEachPart @@ -14,8 +14,6 @@ import io.ktor.server.routing.get import io.ktor.server.routing.post import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.io.InputStream -import java.util.Locale fun Route.projectsRestController( configuration: Configuration, @@ -79,14 +77,15 @@ fun Route.projectsRestController( } get("/all_projects") { - val result = projectRepository.getProjectList() + val result = projectRepository.getProjects() .map { project -> AllProjectsResponse.Project( - slug = project.name, + slug = project.projectSlug, name = project.name, preview = configuration.baseUrl + project.preview, ) } + call.respond(HttpStatusCode.OK, result) }