實作新增、查詢、刪除角色
This commit is contained in:
parent
d537ec1f76
commit
7e7c8b0698
@ -11,12 +11,13 @@ object JwtConfig {
|
|||||||
val jwtDomain = "com.ray650128"
|
val jwtDomain = "com.ray650128"
|
||||||
val jwtRealm = "pc re:dive rank table"
|
val jwtRealm = "pc re:dive rank table"
|
||||||
val jwtSecret = "secret"
|
val jwtSecret = "secret"
|
||||||
//private val validityInMs = 36_000_00 * 24 // 1 day
|
private val validityInMs = 36_000_00 * 24 // 1 day
|
||||||
private const val validityInMs = 300000 // 5 min
|
//private const val validityInMs = 300000 // 5 min
|
||||||
private val algorithm = Algorithm.HMAC512(jwtSecret)
|
private val algorithm = Algorithm.HMAC512(jwtSecret)
|
||||||
|
|
||||||
val verifier: JWTVerifier = JWT
|
val verifier: JWTVerifier = JWT
|
||||||
.require(algorithm)
|
.require(algorithm)
|
||||||
|
.withAudience(jwtAudience)
|
||||||
.withIssuer(jwtDomain)
|
.withIssuer(jwtDomain)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@ -24,9 +25,10 @@ object JwtConfig {
|
|||||||
* Produce a token for this combination of name and password
|
* Produce a token for this combination of name and password
|
||||||
*/
|
*/
|
||||||
fun generateToken(user: User): String = JWT.create()
|
fun generateToken(user: User): String = JWT.create()
|
||||||
.withSubject("Authentication")
|
.withAudience(jwtAudience)
|
||||||
.withIssuer(jwtDomain)
|
.withIssuer(jwtDomain)
|
||||||
.withClaim("account", user.account)
|
.withClaim("account", user.account)
|
||||||
|
.withClaim("password", user.password)
|
||||||
.withExpiresAt(getExpiration()) // optional
|
.withExpiresAt(getExpiration()) // optional
|
||||||
.sign(algorithm)
|
.sign(algorithm)
|
||||||
|
|
||||||
|
|||||||
36
src/main/kotlin/com/ray650128/model/dto/GameCharacterDto.kt
Normal file
36
src/main/kotlin/com/ray650128/model/dto/GameCharacterDto.kt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package com.ray650128.model.dto
|
||||||
|
|
||||||
|
import com.ray650128.model.pojo.GameCharacter
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CharacterDto(
|
||||||
|
val _id: String? = null,
|
||||||
|
val name: String,
|
||||||
|
val imgSrc: String,
|
||||||
|
val position: Int,
|
||||||
|
val standIndex: Int,
|
||||||
|
var createAt: Long? = null,
|
||||||
|
var updatedAt: Long? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
fun GameCharacter.toDto(baseUrl: String? = null): CharacterDto =
|
||||||
|
CharacterDto(
|
||||||
|
_id = this._id.toString(),
|
||||||
|
name = this.name,
|
||||||
|
imgSrc = if (baseUrl.isNullOrEmpty()) this.imgSrc else "$baseUrl${this.imgSrc}",
|
||||||
|
position = this.position,
|
||||||
|
standIndex = this.standIndex,
|
||||||
|
createAt = this.createAt,
|
||||||
|
updatedAt = this.updatedAt
|
||||||
|
)
|
||||||
|
|
||||||
|
fun CharacterDto.toGameCharacter(): GameCharacter =
|
||||||
|
GameCharacter(
|
||||||
|
name = this.name,
|
||||||
|
imgSrc = this.imgSrc,
|
||||||
|
position = this.position,
|
||||||
|
standIndex = this.standIndex,
|
||||||
|
createAt = this.createAt,
|
||||||
|
updatedAt = this.updatedAt
|
||||||
|
)
|
||||||
17
src/main/kotlin/com/ray650128/model/pojo/GameCharacter.kt
Normal file
17
src/main/kotlin/com/ray650128/model/pojo/GameCharacter.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.ray650128.model.pojo
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.litote.kmongo.Id
|
||||||
|
import org.litote.kmongo.newId
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GameCharacter(
|
||||||
|
@Contextual val _id: Id<GameCharacter>? = newId(),
|
||||||
|
val name: String,
|
||||||
|
val imgSrc: String,
|
||||||
|
val position: Int,
|
||||||
|
val standIndex: Int,
|
||||||
|
var createAt: Long? = null,
|
||||||
|
var updatedAt: Long? = null
|
||||||
|
)
|
||||||
10
src/main/kotlin/com/ray650128/model/pojo/NewCharacter.kt
Normal file
10
src/main/kotlin/com/ray650128/model/pojo/NewCharacter.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.ray650128.model.pojo
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class NewCharacter(
|
||||||
|
val name: String,
|
||||||
|
val position: Int,
|
||||||
|
val standIndex: Int
|
||||||
|
)
|
||||||
@ -1,28 +1,101 @@
|
|||||||
package com.ray650128.plugins
|
package com.ray650128.plugins
|
||||||
|
|
||||||
import io.ktor.serialization.gson.*
|
import com.google.gson.Gson
|
||||||
import io.ktor.server.plugins.contentnegotiation.*
|
import com.ray650128.extensions.sendBadRequest
|
||||||
|
import com.ray650128.extensions.sendNotFound
|
||||||
|
import com.ray650128.extensions.sendSuccess
|
||||||
|
import com.ray650128.extensions.sendUnauthorized
|
||||||
|
import com.ray650128.model.ErrorResponse
|
||||||
|
import com.ray650128.model.User
|
||||||
|
import com.ray650128.model.dto.toDto
|
||||||
|
import com.ray650128.model.pojo.GameCharacter
|
||||||
|
import com.ray650128.model.pojo.NewCharacter
|
||||||
|
import com.ray650128.service.CharacterService
|
||||||
|
import io.ktor.http.content.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
import io.ktor.server.util.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
fun Application.configureCharacter() {
|
fun Application.configureCharacter() {
|
||||||
routing {
|
routing {
|
||||||
route("/api") {
|
route("/api") {
|
||||||
route("/v1") {
|
route("/v1") {
|
||||||
get("/characters") {
|
get("/characters") {
|
||||||
call.respond(mapOf("hello" to "world"))
|
val gameCharacterList = CharacterService.findAll().map(GameCharacter::toDto)
|
||||||
|
call.sendSuccess(gameCharacterList)
|
||||||
}
|
}
|
||||||
|
|
||||||
route("/character") {
|
route("/character") {
|
||||||
get("/{id}") {
|
get("/{id}") {
|
||||||
call.respond(mapOf("hello" to "world"))
|
val id = call.parameters["id"].toString()
|
||||||
|
val character = CharacterService.findById(id)?.toDto() ?: run {
|
||||||
|
call.sendNotFound()
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
call.sendSuccess(character)
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticate {
|
authenticate {
|
||||||
post {
|
post {
|
||||||
|
call.authentication.principal<User>() ?: run {
|
||||||
|
call.sendUnauthorized()
|
||||||
|
return@post
|
||||||
|
}
|
||||||
|
var characterId: String? = null
|
||||||
|
// retrieve all multipart data (suspending)
|
||||||
|
val multipart = call.receiveMultipart()
|
||||||
|
var params: NewCharacter? = null
|
||||||
|
multipart.forEachPart { part ->
|
||||||
|
// Check part type
|
||||||
|
when (part) {
|
||||||
|
is PartData.FormItem -> {
|
||||||
|
params = Gson().fromJson(part.value, NewCharacter::class.java)
|
||||||
|
println(part.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
is PartData.FileItem -> {
|
||||||
|
val extensionName = part.contentType?.contentSubtype
|
||||||
|
val name = "${System.currentTimeMillis()}.$extensionName"
|
||||||
|
val directory = File("./upload/")
|
||||||
|
if (!directory.exists()) {
|
||||||
|
directory.mkdir()
|
||||||
|
}
|
||||||
|
val file = File("${directory.path}/$name")
|
||||||
|
|
||||||
|
// use InputStream from part to save file
|
||||||
|
part.streamProvider().use { its ->
|
||||||
|
// copy the stream to the file with buffering
|
||||||
|
file.outputStream().buffered().use {
|
||||||
|
// note that this is blocking
|
||||||
|
its.copyTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val tmpCharacter = GameCharacter(
|
||||||
|
name = params!!.name,
|
||||||
|
imgSrc = "/upload/$name",
|
||||||
|
position = params!!.position,
|
||||||
|
standIndex = params!!.standIndex,
|
||||||
|
createAt = System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
characterId = CharacterService.create(tmpCharacter).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
// make sure to dispose of the part after use to prevent leaks
|
||||||
|
part.dispose()
|
||||||
|
}
|
||||||
|
if (characterId != null) {
|
||||||
|
val data = CharacterService.findById(characterId!!)?.toDto()
|
||||||
|
call.sendSuccess(data)
|
||||||
|
} else {
|
||||||
|
call.sendBadRequest(ErrorResponse("Add material fail."))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
put {
|
put {
|
||||||
@ -30,7 +103,14 @@ fun Application.configureCharacter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete("/{id}") {
|
delete("/{id}") {
|
||||||
|
call.authentication.principal<User>() ?: run {
|
||||||
|
call.sendUnauthorized()
|
||||||
|
return@delete
|
||||||
|
}
|
||||||
|
val id = call.parameters["id"].toString()
|
||||||
|
CharacterService.deleteById(id)
|
||||||
|
val gameCharacterList = CharacterService.findAll().map(GameCharacter::toDto)
|
||||||
|
call.sendSuccess(gameCharacterList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.ray650128.plugins
|
|||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
import io.ktor.server.auth.jwt.*
|
import io.ktor.server.auth.jwt.*
|
||||||
import com.ray650128.JwtConfig
|
import com.ray650128.JwtConfig
|
||||||
|
import com.ray650128.model.User
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.serialization.gson.*
|
import io.ktor.serialization.gson.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
@ -16,8 +17,10 @@ fun Application.configureModules() {
|
|||||||
realm = JwtConfig.jwtRealm
|
realm = JwtConfig.jwtRealm
|
||||||
verifier(JwtConfig.verifier)
|
verifier(JwtConfig.verifier)
|
||||||
validate { credential ->
|
validate { credential ->
|
||||||
if (credential.payload.audience.contains(JwtConfig.jwtAudience)) {
|
val account = credential.payload.getClaim("account").asString()
|
||||||
JWTPrincipal(credential.payload)
|
val password = credential.payload.getClaim("password").asString()
|
||||||
|
if (account != "" && password != "") {
|
||||||
|
User(account = account, password = password)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,8 +43,8 @@ fun Application.configureUser() {
|
|||||||
val password = md5encode(request.password)
|
val password = md5encode(request.password)
|
||||||
val user = UserService.findByLoginInfo(request.account, password)
|
val user = UserService.findByLoginInfo(request.account, password)
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
val token = JwtConfig.generateToken(request)
|
val token = JwtConfig.generateToken(user)
|
||||||
call.sendSuccess(LoginResult(request.account, token))
|
call.sendSuccess(LoginResult(user.account, token))
|
||||||
} else {
|
} else {
|
||||||
call.sendUnauthorized()
|
call.sendUnauthorized()
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/main/kotlin/com/ray650128/service/CharacterService.kt
Normal file
45
src/main/kotlin/com/ray650128/service/CharacterService.kt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package com.ray650128.service
|
||||||
|
|
||||||
|
import com.ray650128.model.pojo.GameCharacter
|
||||||
|
import org.bson.types.ObjectId
|
||||||
|
import org.litote.kmongo.*
|
||||||
|
import org.litote.kmongo.id.toId
|
||||||
|
|
||||||
|
object CharacterService {
|
||||||
|
private val client = KMongo.createClient("mongodb://ray650128:Zx650128!@www.ray650128.com:27017")
|
||||||
|
private val database = client.getDatabase("test_system")
|
||||||
|
private val characterCollection = database.getCollection<GameCharacter>()
|
||||||
|
|
||||||
|
fun create(character: GameCharacter): Id<GameCharacter>? {
|
||||||
|
characterCollection.insertOne(character)
|
||||||
|
return character._id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAll(): List<GameCharacter> = characterCollection.find().toList()
|
||||||
|
|
||||||
|
fun findById(id: String): GameCharacter? {
|
||||||
|
val bsonId: Id<GameCharacter> = ObjectId(id).toId()
|
||||||
|
return characterCollection.findOne(GameCharacter::_id eq bsonId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findByName(name: String): List<GameCharacter> {
|
||||||
|
val caseSensitiveTypeSafeFilter = GameCharacter::name regex name
|
||||||
|
return characterCollection.find(caseSensitiveTypeSafeFilter).toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateById(id: String, request: GameCharacter): Boolean =
|
||||||
|
findById(id)?.let { character ->
|
||||||
|
val updateResult = characterCollection.replaceOne(
|
||||||
|
character.copy(
|
||||||
|
name = request.name,
|
||||||
|
updatedAt = request.updatedAt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
updateResult.modifiedCount == 1L
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
fun deleteById(id: String): Boolean {
|
||||||
|
val deleteResult = characterCollection.deleteOneById(ObjectId(id))
|
||||||
|
return deleteResult.deletedCount == 1L
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user