备份代码

This commit is contained in:
Superdandan 2024-06-04 22:15:22 +08:00
parent d17dbeeda4
commit 95af56f9c1
20 changed files with 203 additions and 98 deletions

View File

@ -29,6 +29,7 @@ repositories {
dependencies { dependencies {
implementation("io.ktor:ktor-server-core-jvm") implementation("io.ktor:ktor-server-core-jvm")
implementation("io.ktor:ktor-server-auth-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-server-content-negotiation-jvm")
implementation("io.ktor:ktor-serialization-gson-jvm") implementation("io.ktor:ktor-serialization-gson-jvm")
implementation("io.ktor:ktor-server-freemarker-jvm") implementation("io.ktor:ktor-server-freemarker-jvm")

View File

@ -1,8 +1,6 @@
package echo.org.application.route package echo.org.application.route
import echo.org.domain.* import echo.org.domain.*
import echo.org.instructure.AliyunOssFileServiceImpl
import echo.org.instructure.DocumentRepositoryImpl
import io.ktor.http.* import io.ktor.http.*
import io.ktor.http.content.* import io.ktor.http.content.*
import io.ktor.server.application.* import io.ktor.server.application.*
@ -10,7 +8,6 @@ import io.ktor.server.freemarker.*
import io.ktor.server.request.* import io.ktor.server.request.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
import org.koin.dsl.module
import org.koin.ktor.ext.inject import org.koin.ktor.ext.inject
import java.io.File import java.io.File
import java.net.URLEncoder import java.net.URLEncoder
@ -20,11 +17,18 @@ import java.time.LocalDateTime
fun Route.handleDocuments() { fun Route.handleDocuments() {
val documentRepository by inject<DocumentRepository>() val documentRepository by inject<DocumentRepository>()
val folderRepository by inject<FolderRepository>()
val fileOSS by inject<FileOSS>() val fileOSS by inject<FileOSS>()
route("/documents") { route("/documents") {
get("") { get("") {
val res = documentRepository.pageQuery(DocumentQueryParam()) call.respond(FreeMarkerContent("home.ftl", null, "e"))
call.respond(FreeMarkerContent("index.ftl", res.results, "e")) }
get("/files") {
val topFolders = folderRepository.searchTopFolders()
val dataModel = mapOf(
"topFolders" to topFolders
)
call.respond(FreeMarkerContent("files.ftl", dataModel, "e"))
} }
post("/add") { post("/add") {
// 添加一个文件 // 添加一个文件
@ -90,5 +94,9 @@ fun Route.handleDocuments() {
val pageQuery = documentRepository.pageQuery(documentQueryParam) val pageQuery = documentRepository.pageQuery(documentQueryParam)
call.respond(Result.success(pageQuery)) call.respond(Result.success(pageQuery))
} }
post("/folder/top") {
val topFolders = folderRepository.searchTopFolders()
call.respond(Result.success(topFolders))
}
} }
} }

View File

@ -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") {}
}
}

View File

@ -1,14 +1,15 @@
package echo.org.domain 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 import java.time.LocalDateTime
open class BaseDoc(
var id: Long,
var name: String
)
data class Document( class Document(
val id: Long, id: Long,
name: String,
val title: String, val title: String,
val uploadName: String, val uploadName: String,
val fileType: String, val fileType: String,
@ -16,8 +17,9 @@ data class Document(
val createdAt: LocalDateTime, val createdAt: LocalDateTime,
val updatedAt: LocalDateTime, val updatedAt: LocalDateTime,
val fileSize: String, val fileSize: String,
val filePath: String val filePath: String,
)
) : BaseDoc(id, name)
data class DocumentVO( data class DocumentVO(
val title: String, val authorId: String, val title: String, val authorId: String,

View File

@ -57,4 +57,6 @@ interface FolderRepository {
// Search folders by name or tags // Search folders by name or tags
suspend fun searchFolders(query: String): List<Folder> suspend fun searchFolders(query: String): List<Folder>
suspend fun searchTopFolders():List<Folder>
} }

View File

@ -0,0 +1,3 @@
package echo.org.domain
data class User(val id: Int?, val username: String?, val password: String?)

View File

@ -28,12 +28,6 @@ data class Result<T>(
data class PageResult<T>( data class PageResult<T>(
val results: List<T>, // 查询结果列表 val results: List<T>, // 查询结果列表
val totalCount: Long // 总记录数 val totalCount: Long // 总记录数
) { )
// companion object {
// fun <T> of(results: List<T>,totalCount: Long ): PageResult<T> {
// return PageResult()
// }
// }
}
open class BasePageQueryParm(var pageNum: Int = 1, var pageSize: Int = 10) open class BasePageQueryParm(var pageNum: Int = 1, var pageSize: Int = 10)

View File

@ -135,4 +135,11 @@ class FolderRepositoryImpl : FolderRepository {
(Folders.name like "%$query%") or (Folders.tags like "%$query%") (Folders.name like "%$query%") or (Folders.tags like "%$query%")
}.map { Folders.toFolder(it) } }.map { Folders.toFolder(it) }
} }
override suspend fun searchTopFolders(): List<Folder> = dbQuery {
Folders.selectAll().where {
parentFolderId.isNull()
}.map { Folders.toFolder(it) }
}
} }

View File

@ -1,19 +1,19 @@
package echo.org.plugins package echo.org.plugins
import echo.org.application.route.handleDocuments
import echo.org.domain.DocumentRepository import echo.org.domain.DocumentRepository
import echo.org.domain.FileOSS import echo.org.domain.FileOSS
import echo.org.domain.FolderRepository
import echo.org.instructure.AliyunOssFileServiceImpl import echo.org.instructure.AliyunOssFileServiceImpl
import echo.org.instructure.DocumentRepositoryImpl import echo.org.instructure.DocumentRepositoryImpl
import echo.org.instructure.FolderRepositoryImpl
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.http.content.*
import io.ktor.server.routing.*
import org.koin.dsl.module import org.koin.dsl.module
import org.koin.ktor.plugin.Koin import org.koin.ktor.plugin.Koin
val DocumentModule = module { val DocumentModule = module {
single<DocumentRepository> { DocumentRepositoryImpl() } single<DocumentRepository> { DocumentRepositoryImpl() }
single<FolderRepository> { FolderRepositoryImpl() }
single<FileOSS> { AliyunOssFileServiceImpl() } single<FileOSS> { AliyunOssFileServiceImpl() }
} }

View File

@ -1,6 +1,55 @@
package echo.org.plugins 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.*
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>("User")
fun Application.configureSecurity() { 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<JWTPrincipal>()
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))
}
}
} }

View File

@ -8,7 +8,7 @@ ktor:
storage: storage:
driverClassName: "com.mysql.cj.jdbc.Driver" driverClassName: "com.mysql.cj.jdbc.Driver"
jdbcURL: "jdbc:mysql://47.97.21.20:3306" jdbcURL: "jdbc:mysql://127.0.0.1:3306"
database: "document" database: "document"
username: "documentAdmin" username: "root"
password: "123456" password: "123456"

View File

@ -1,85 +1,61 @@
/* 基础重置 */ body {
body, h1, h2, h3, p, ul, li, form, input, button { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; background-color: #f5f5f5;
font-family: 'Roboto', sans-serif;
} }
body { header {
line-height: 1.6; display: flex;
justify-content: space-between;
align-items: center;
padding: 20px; padding: 20px;
background-color: #f9f9f9; background-color: #90b7dc;
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;
color: white; color: white;
padding: 10px 20px; }
text-align: center;
text-decoration: none; .logo {
display: inline-block; font-size: 24px;
font-weight: bold;
}
.user-info {
font-size: 16px; font-size: 16px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
} }
button:hover { nav {
background-color: #2980b9; background-color: #f5f5f5;
border-bottom: 1px solid #ccc;
padding: 10px;
} }
/* 辅助类 */ nav ul {
.container { list-style-type: none;
width: 95%; margin: 0;
max-width: 1200px; padding: 0;
margin: 0 auto; display: flex;
} }
.clearfix::after { nav ul li {
content: ""; margin-right: 20px;
display: table; }
clear: both;
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;
} }

View File

@ -0,0 +1,18 @@
<#macro main title>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${title!""}</title>
<link rel="stylesheet" href="/files/styles.css">
</head>
<body>
<#include "header.ftl">
<#include "nav.ftl">
<main>
<#nested>
</main>
<#include "footer.ftl">
</body>
</html>
</#macro>

View File

@ -0,0 +1,10 @@
<#import "base.ftl" as base>
<@base.main title="Files">
<h2>Your Files</h2>
<ul>
<#list documents as document>
<li>${document.title} (${document.fileSize} MB)</li>
</#list>
</ul>
</@base.main>

View File

@ -0,0 +1,3 @@
<footer>
<p>&copy; 2024 CloudStorage. All rights reserved.</p>
</footer>

View File

@ -0,0 +1,7 @@
<header>
<div class="logo">CloudStorage</div>
<div class="user-info">
<span>${user!""}</span>
<a href="/logout">Logout</a>
</div>
</header>

View File

@ -0,0 +1,6 @@
<#import "base.ftl" as base>
<@base.main title="Home">
<h2>Welcome to CloudStorage</h2>
<p>Your files, always with you.</p>
</@base.main>

View File

@ -7,7 +7,7 @@
<body class="container"> <body class="container">
<h1>文档列表</h1> <h1>文档列表</h1>
<ul> <ul>
<#list Documents as doc> <#list results as doc>
<li> <li>
${doc.id} - ${doc.name} ${doc.id} - ${doc.name}
</li> </li>

View File

@ -0,0 +1,7 @@
<nav>
<ul>
<li><a href="/documents">Home</a></li>
<li><a href="/documents/files">Files</a></li>
<li><a href="/settings">Settings</a></li>
</ul>
</nav>

BIN
~$16041746156.docx Normal file

Binary file not shown.