diff --git a/build.gradle.kts b/build.gradle.kts index 6351c4c..3dba3fd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,6 +29,7 @@ repositories { dependencies { implementation("io.ktor:ktor-server-core-jvm") implementation("io.ktor:ktor-server-auth-jvm") + implementation("io.ktor:ktor-server-auth-jwt:$ktor_version") implementation("io.ktor:ktor-server-content-negotiation-jvm") implementation("io.ktor:ktor-serialization-gson-jvm") implementation("io.ktor:ktor-server-freemarker-jvm") diff --git a/src/main/kotlin/echo/org/application/route/DocumentRoute.kt b/src/main/kotlin/echo/org/application/route/DocumentRoute.kt index 4d4fa04..4f3f058 100644 --- a/src/main/kotlin/echo/org/application/route/DocumentRoute.kt +++ b/src/main/kotlin/echo/org/application/route/DocumentRoute.kt @@ -1,8 +1,6 @@ package echo.org.application.route import echo.org.domain.* -import echo.org.instructure.AliyunOssFileServiceImpl -import echo.org.instructure.DocumentRepositoryImpl import io.ktor.http.* import io.ktor.http.content.* import io.ktor.server.application.* @@ -10,7 +8,6 @@ import io.ktor.server.freemarker.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -import org.koin.dsl.module import org.koin.ktor.ext.inject import java.io.File import java.net.URLEncoder @@ -20,11 +17,18 @@ import java.time.LocalDateTime fun Route.handleDocuments() { val documentRepository by inject() + val folderRepository by inject() val fileOSS by inject() route("/documents") { get("") { - val res = documentRepository.pageQuery(DocumentQueryParam()) - call.respond(FreeMarkerContent("index.ftl", res.results, "e")) + call.respond(FreeMarkerContent("home.ftl", null, "e")) + } + get("/files") { + val topFolders = folderRepository.searchTopFolders() + val dataModel = mapOf( + "topFolders" to topFolders + ) + call.respond(FreeMarkerContent("files.ftl", dataModel, "e")) } post("/add") { // 添加一个文件 @@ -90,5 +94,9 @@ fun Route.handleDocuments() { val pageQuery = documentRepository.pageQuery(documentQueryParam) call.respond(Result.success(pageQuery)) } + post("/folder/top") { + val topFolders = folderRepository.searchTopFolders() + call.respond(Result.success(topFolders)) + } } } \ No newline at end of file diff --git a/src/main/kotlin/echo/org/application/route/UserRoute.kt b/src/main/kotlin/echo/org/application/route/UserRoute.kt new file mode 100644 index 0000000..1679ca5 --- /dev/null +++ b/src/main/kotlin/echo/org/application/route/UserRoute.kt @@ -0,0 +1,12 @@ +package echo.org.application.route + +import echo.org.plugins.JWT_AUTH +import io.ktor.server.auth.* +import io.ktor.server.routing.* + +fun Route.handleUsers() { + + authenticate(JWT_AUTH) { + post("/login") {} + } +} \ No newline at end of file diff --git a/src/main/kotlin/echo/org/domain/Documents.kt b/src/main/kotlin/echo/org/domain/Documents.kt index 7a40ae3..f829bab 100644 --- a/src/main/kotlin/echo/org/domain/Documents.kt +++ b/src/main/kotlin/echo/org/domain/Documents.kt @@ -1,14 +1,15 @@ package echo.org.domain -import echo.org.instructure.Documents -import org.jetbrains.exposed.dao.LongEntity -import org.jetbrains.exposed.dao.LongEntityClass -import org.jetbrains.exposed.dao.id.EntityID import java.time.LocalDateTime +open class BaseDoc( + var id: Long, + var name: String +) -data class Document( - val id: Long, +class Document( + id: Long, + name: String, val title: String, val uploadName: String, val fileType: String, @@ -16,8 +17,9 @@ data class Document( val createdAt: LocalDateTime, val updatedAt: LocalDateTime, val fileSize: String, - val filePath: String -) + val filePath: String, + +) : BaseDoc(id, name) data class DocumentVO( val title: String, val authorId: String, diff --git a/src/main/kotlin/echo/org/domain/Folder.kt b/src/main/kotlin/echo/org/domain/Folder.kt index 1f6b30a..0ada27d 100644 --- a/src/main/kotlin/echo/org/domain/Folder.kt +++ b/src/main/kotlin/echo/org/domain/Folder.kt @@ -57,4 +57,6 @@ interface FolderRepository { // Search folders by name or tags suspend fun searchFolders(query: String): List + + suspend fun searchTopFolders():List } diff --git a/src/main/kotlin/echo/org/domain/User.kt b/src/main/kotlin/echo/org/domain/User.kt new file mode 100644 index 0000000..203f271 --- /dev/null +++ b/src/main/kotlin/echo/org/domain/User.kt @@ -0,0 +1,3 @@ +package echo.org.domain + +data class User(val id: Int?, val username: String?, val password: String?) \ No newline at end of file diff --git a/src/main/kotlin/echo/org/domain/ValueObject.kt b/src/main/kotlin/echo/org/domain/ValueObject.kt index 8c92cd4..e94d414 100644 --- a/src/main/kotlin/echo/org/domain/ValueObject.kt +++ b/src/main/kotlin/echo/org/domain/ValueObject.kt @@ -28,12 +28,6 @@ data class Result( data class PageResult( val results: List, // 查询结果列表 val totalCount: Long // 总记录数 -) { -// companion object { -// fun of(results: List,totalCount: Long ): PageResult { -// return PageResult() -// } -// } -} +) open class BasePageQueryParm(var pageNum: Int = 1, var pageSize: Int = 10) \ No newline at end of file diff --git a/src/main/kotlin/echo/org/instructure/FolderExposed.kt b/src/main/kotlin/echo/org/instructure/FolderExposed.kt index 7a3d14d..ce2bd1f 100644 --- a/src/main/kotlin/echo/org/instructure/FolderExposed.kt +++ b/src/main/kotlin/echo/org/instructure/FolderExposed.kt @@ -135,4 +135,11 @@ class FolderRepositoryImpl : FolderRepository { (Folders.name like "%$query%") or (Folders.tags like "%$query%") }.map { Folders.toFolder(it) } } + + override suspend fun searchTopFolders(): List = dbQuery { + Folders.selectAll().where { + parentFolderId.isNull() + }.map { Folders.toFolder(it) } + } + } \ No newline at end of file diff --git a/src/main/kotlin/echo/org/plugins/Koin.kt b/src/main/kotlin/echo/org/plugins/Koin.kt index 9fd1876..462ef5d 100644 --- a/src/main/kotlin/echo/org/plugins/Koin.kt +++ b/src/main/kotlin/echo/org/plugins/Koin.kt @@ -1,19 +1,19 @@ package echo.org.plugins -import echo.org.application.route.handleDocuments import echo.org.domain.DocumentRepository import echo.org.domain.FileOSS +import echo.org.domain.FolderRepository import echo.org.instructure.AliyunOssFileServiceImpl import echo.org.instructure.DocumentRepositoryImpl +import echo.org.instructure.FolderRepositoryImpl import io.ktor.server.application.* -import io.ktor.server.http.content.* -import io.ktor.server.routing.* import org.koin.dsl.module import org.koin.ktor.plugin.Koin val DocumentModule = module { single { DocumentRepositoryImpl() } + single { FolderRepositoryImpl() } single { AliyunOssFileServiceImpl() } } diff --git a/src/main/kotlin/echo/org/plugins/Security.kt b/src/main/kotlin/echo/org/plugins/Security.kt index d366c1f..93cbe52 100644 --- a/src/main/kotlin/echo/org/plugins/Security.kt +++ b/src/main/kotlin/echo/org/plugins/Security.kt @@ -1,6 +1,55 @@ package echo.org.plugins +import com.auth0.jwt.JWT +import com.auth0.jwt.algorithms.Algorithm +import echo.org.domain.User import io.ktor.server.application.* +import io.ktor.server.application.ApplicationCallPipeline.ApplicationPhase.Call +import io.ktor.server.auth.* +import io.ktor.server.auth.jwt.* +import io.ktor.util.* + + +const val JWT_AUTH = "auth-jwt" +const val SECRET_KEY = "echo" +const val SAMPLE_AUDIENCE = "ktor-audience" +const val SAMPLE_ISSUER = "ktor-issuer" + +// 定义一个键,用于在上下文中存储用户信息 +val UserKey = AttributeKey("User") fun Application.configureSecurity() { + install(Authentication) { + jwt(JWT_AUTH) { + realm = "ktor sample app" + verifier( + JWT + .require(Algorithm.HMAC256(SECRET_KEY)) // 使用您的密钥 + .withAudience(SAMPLE_AUDIENCE) + .withIssuer(SAMPLE_ISSUER) + .build() + ) + validate { credential -> + if (credential.payload.audience.contains(SAMPLE_AUDIENCE)) { + val username = credential.payload.getClaim("username") + val password = credential.payload.getClaim("password") + JWTPrincipal(credential.payload).apply { + + } + } else null + } + } + } + + // 添加拦截器来存储用户信息到请求的 attributes 中 + intercept(Call) { + val principal = call.principal() + if (principal != null) { + val id = principal.payload.getClaim("id").asInt() + val username = principal.payload.getClaim("username").asString() + val email = principal.payload.getClaim("email").asString() + call.attributes.put(UserKey, User(id, username, email)) + } + } + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 5b69b39..0200893 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -8,7 +8,7 @@ ktor: storage: driverClassName: "com.mysql.cj.jdbc.Driver" - jdbcURL: "jdbc:mysql://47.97.21.20:3306" + jdbcURL: "jdbc:mysql://127.0.0.1:3306" database: "document" - username: "documentAdmin" + username: "root" password: "123456" \ No newline at end of file diff --git a/src/main/resources/files/styles.css b/src/main/resources/files/styles.css index 22542f5..8ec21f4 100644 --- a/src/main/resources/files/styles.css +++ b/src/main/resources/files/styles.css @@ -1,85 +1,61 @@ -/* 基础重置 */ -body, h1, h2, h3, p, ul, li, form, input, button { +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; padding: 0; - box-sizing: border-box; - font-family: 'Roboto', sans-serif; + background-color: #f5f5f5; } -body { - line-height: 1.6; +header { + display: flex; + justify-content: space-between; + align-items: center; padding: 20px; - background-color: #f9f9f9; - color: #333; -} - -h1, h2, h3 { - color: #2c3e50; -} - -/* 链接样式 */ -a { - color: #3498db; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -/* 列表样式 */ -ul { - list-style-type: none; - padding: 0; -} - -li { - margin-bottom: 10px; - padding-left: 10px; -} - -/* 表单样式 */ -form { - background: #ffffff; - padding: 20px; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); - max-width: 500px; - margin: 20px auto; -} - -input[type="file"] { - display: block; - margin-bottom: 10px; -} - -button { - background-color: #3498db; - border: none; + background-color: #90b7dc; color: white; - padding: 10px 20px; - text-align: center; - text-decoration: none; - display: inline-block; +} + +.logo { + font-size: 24px; + font-weight: bold; +} + +.user-info { font-size: 16px; - cursor: pointer; - border-radius: 5px; - transition: background-color 0.3s; } -button:hover { - background-color: #2980b9; +nav { + background-color: #f5f5f5; + border-bottom: 1px solid #ccc; + padding: 10px; } -/* 辅助类 */ -.container { - width: 95%; - max-width: 1200px; - margin: 0 auto; +nav ul { + list-style-type: none; + margin: 0; + padding: 0; + display: flex; } -.clearfix::after { - content: ""; - display: table; - clear: both; -} \ No newline at end of file +nav ul li { + margin-right: 20px; +} + +nav ul li a { + text-decoration: none; + color: #007aff; + font-weight: bold; +} + +main { + padding: 20px; +} + +footer { + text-align: center; + padding: 10px; + background-color: #007aff; + color: white; + position: fixed; + width: 100%; + bottom: 0; +} diff --git a/src/main/resources/templates/base.ftl b/src/main/resources/templates/base.ftl new file mode 100644 index 0000000..e9b0b7c --- /dev/null +++ b/src/main/resources/templates/base.ftl @@ -0,0 +1,18 @@ +<#macro main title> + + + + + ${title!""} + + + + <#include "header.ftl"> + <#include "nav.ftl"> +
+ <#nested> +
+ <#include "footer.ftl"> + + + diff --git a/src/main/resources/templates/files.ftl b/src/main/resources/templates/files.ftl new file mode 100644 index 0000000..11eeef1 --- /dev/null +++ b/src/main/resources/templates/files.ftl @@ -0,0 +1,10 @@ +<#import "base.ftl" as base> + +<@base.main title="Files"> +

Your Files

+
    + <#list documents as document> +
  • ${document.title} (${document.fileSize} MB)
  • + +
+ \ No newline at end of file diff --git a/src/main/resources/templates/footer.ftl b/src/main/resources/templates/footer.ftl new file mode 100644 index 0000000..fa1b893 --- /dev/null +++ b/src/main/resources/templates/footer.ftl @@ -0,0 +1,3 @@ +
+

© 2024 CloudStorage. All rights reserved.

+
\ No newline at end of file diff --git a/src/main/resources/templates/header.ftl b/src/main/resources/templates/header.ftl new file mode 100644 index 0000000..9dcc153 --- /dev/null +++ b/src/main/resources/templates/header.ftl @@ -0,0 +1,7 @@ +
+ + +
\ No newline at end of file diff --git a/src/main/resources/templates/home.ftl b/src/main/resources/templates/home.ftl new file mode 100644 index 0000000..92c33c8 --- /dev/null +++ b/src/main/resources/templates/home.ftl @@ -0,0 +1,6 @@ +<#import "base.ftl" as base> + +<@base.main title="Home"> +

Welcome to CloudStorage

+

Your files, always with you.

+ \ No newline at end of file diff --git a/src/main/resources/templates/index.ftl b/src/main/resources/templates/index.ftl index 5f81ec8..3c60a3c 100644 --- a/src/main/resources/templates/index.ftl +++ b/src/main/resources/templates/index.ftl @@ -7,7 +7,7 @@

文档列表

    - <#list Documents as doc> + <#list results as doc>
  • ${doc.id} - ${doc.name}
  • diff --git a/src/main/resources/templates/nav.ftl b/src/main/resources/templates/nav.ftl new file mode 100644 index 0000000..1c836b9 --- /dev/null +++ b/src/main/resources/templates/nav.ftl @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/~$16041746156.docx b/~$16041746156.docx new file mode 100644 index 0000000..fe6d251 Binary files /dev/null and b/~$16041746156.docx differ