Compare commits

...

10 Commits

Author SHA1 Message Date
Raymond Yang
a2acb7b928 實作ar model刪除、查詢、修改 2023-05-16 14:59:30 +08:00
Raymond Yang
d787932e45 實作ar model新增 2023-05-16 14:53:35 +08:00
Raymond Yang
bd214f0423 移除Dto(難用) 2023-05-16 13:48:09 +08:00
Raymond Yang
8999ab3272 清理無用的引用 2023-05-16 10:51:54 +08:00
Raymond Yang
02849a7396 實作素材查詢、修改、刪除 2023-05-16 10:50:39 +08:00
Raymond Yang
07597ad5f2 優化sendUnauthorized、sendNotFound 2023-05-16 10:36:54 +08:00
Raymond Yang
50dbcf60e1 實作素材上傳 2023-05-16 10:02:29 +08:00
Raymond Yang
acf72d0aa7 重新命名routing 2023-05-15 17:56:32 +08:00
Raymond Yang
0516a20351 整理專案目錄並加上ResponseExtension 2023-05-15 17:48:59 +08:00
Raymond Yang
1d6cca58c2 更正資料庫名稱 2023-05-15 17:04:56 +08:00
34 changed files with 1690 additions and 119 deletions

193
ActionParams.json Normal file
View File

@ -0,0 +1,193 @@
[
{
"type": 1,
"availableParams": {
"duration": "Float",
"webUrl": "String",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 2,
"availableParams": {
"duration": "Float",
"translation": "VectorArray",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 3,
"availableParams": {
"duration": "Float",
"rotation": "VectorArray",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 4,
"availableParams": {
"duration": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 5,
"availableParams": {
"duration": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 6,
"availableParams": {
"duration": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 7,
"availableParams": {
"duration": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 8,
"availableParams": {
"duration": "Float",
"speed": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 9,
"availableParams": {
"duration": "Float",
"startFrame": "Float",
"endFrame": "Float",
"fps": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 10,
"availableParams": {
"duration": "Float",
"repeatCount": "Int",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 11,
"availableParams": {
"duration": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 12,
"availableParams": {
"duration": "Float",
"scale": "VectorArray",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 13,
"availableParams": {
"duration": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 14,
"availableParams": {
"duration": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 15,
"availableParams": {
"duration": "Float",
"alpha": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 16,
"availableParams": {
"duration": "Float",
"alpha": "Float",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 17,
"availableParams": {
"squareMoveWidth": "Float",
"squareMoveHeight": "Float",
"squareMoveTime": "Float",
"squareMoveRotationTime": "Float",
"clockwise": "Boolean",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 18,
"availableParams": {
"duration": "Float",
"roundMoveRadius": "Float",
"roundMoveStartAngle": "Float",
"roundMoveEndAngel": "Float",
"resolution": "Float",
"clockwise": "Boolean",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 19,
"availableParams": {
"duration": "Float",
"moveToFaceDistance": "Float",
"isAutoReturn": "Boolean",
"autoReturnDelay": "Float",
"isFaceToMe": "Boolean",
"targetID": "Int",
"groupNumber": "Int"
}
},
{
"type": 20,
"availableParams": {
"duration": "Float",
"webUrl": "String",
"groupNumber": "Int"
}
},
{
"type": 21,
"availableParams": {
"vibrationTime": "Float",
"groupNumber": "Int"
}
},
]

491
ArModelData.json Normal file
View File

@ -0,0 +1,491 @@
[
{
"id": 0,
"name": "Plane",
"position": {
"x": 0,
"y": -1,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"type": 0,
"material": {
"contentType": "image/jpeg",
"id": "d6350683.jpg",
"textureUrl": "https://cdn2.ettoday.net/images/6350/d6350683.jpg"
},
"params": {
"width": 1,
"height": 0.815,
"isSizeScaleLock": true
}
},
"events": [
{
"actions": [
{
"actionType": 2,
"alpha": 1,
"autoReturnDelay": 3,
"clockwise": true,
"duration": 1,
"endFrame": 100,
"fps": 24,
"groupNumber": 0,
"isAutoReturn": true,
"isFaceToMe": true,
"moveToFaceDistance": 1,
"repeatCount": 1,
"resolution": 16,
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"roundMoveEndAngel": 360,
"roundMoveRadius": 1,
"roundMoveStartAngle": 0,
"scale": {
"x": 0,
"y": 0,
"z": 0
},
"speed": 1,
"squareMoveHeight": 1,
"squareMoveRotationTime": 0.8,
"squareMoveTime": 1,
"squareMoveWidth": 1,
"startFrame": 0,
"targetId": 0,
"translation": {
"x": 1,
"y": 0,
"z": 0
},
"vibrationTime": 1,
"webUrl": ""
}
],
"eventType": 1
}
]
},
{
"id": 1,
"name": "Cube",
"position": {
"x": 1,
"y": -1,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentType": "image/jpeg",
"id": "d6350683.jpg",
"textureUrl": "https://cdn2.ettoday.net/images/6350/d6350683.jpg"
},
"params": {
"width": 0.75,
"height": 0.75,
"depth": 0.75
},
"type": 1
}
},
{
"id": 2,
"name": "Sphere",
"position": {
"x": 2,
"y": -1,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"type": 2,
"material": {
"contentType": "image/jpeg",
"id": "d6350683.jpg",
"textureUrl": "https://cdn2.ettoday.net/images/6350/d6350683.jpg"
},
"params": {
"availableRange": 20,
"isDoubleSided": true,
"isFaceMe": false,
"isHidden": false,
"isIgnore": false,
"isOcclusion": false,
"radius": 0.45
}
}
},
{
"id": 3,
"name": "Cylinder",
"position": {
"x": 0,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentType": "image/jpeg",
"id": "d6350683.jpg",
"textureUrl": "https://cdn2.ettoday.net/images/6350/d6350683.jpg"
},
"params": {
"availableRange": 20,
"height": 1,
"isDoubleSided": true,
"isFaceMe": false,
"isHidden": false,
"isIgnore": false,
"isOcclusion": false,
"radius": 0.25
},
"type": 3
}
},
{
"id": 4,
"name": "Model3D",
"position": {
"x": 1,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentType": "model/gltf-binary",
"id": "spiderbot.glb",
"textureUrl": "https://www.ray650128.com/spiderbot.glb"
},
"params": {
"availableRange": 20,
"isDoubleSided": true,
"isFaceMe": false,
"isHidden": false,
"isIgnore": false,
"isMultiplyPlane": false,
"isOcclusion": false,
"modelEndFrame": 100,
"modelFPS": 24,
"modelStartFrame": 0,
"multiplyNumber": 10,
"multiplyRadius": 1,
"multiplyRange": 1
},
"type": 4
}
},
{
"id": 5,
"name": "Video1",
"position": {
"x": 1,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentType": "video/mpeg4",
"id": "ker1.mp4",
"textureUrl": "https://www.ray650128.com/video/ker1.mp4"
},
"params": {
"availableRange": 20,
"hueAngle": 120,
"hueRange": 20,
"isDoubleSided": true,
"isFaceMe": false,
"isHidden": false,
"isIgnore": false,
"isOcclusion": false,
"isPlayWhenReady": true,
"isRepeat": true,
"saturation": 80,
"videoThumbnail": "https://cdn2.ettoday.net/images/6350/d6350683.jpg"
},
"type": 5
}
},
{
"id": 6,
"name": "Video2",
"position": {
"x": 2,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentType": "video/mpeg4",
"id": "ker2.mp4",
"textureUrl": "https://www.ray650128.com/video/ker2.mp4"
},
"params": {
"availableRange": 20,
"hueAngle": 124,
"hueRange": 20,
"isDoubleSided": true,
"isFaceMe": false,
"isHidden": false,
"isIgnore": false,
"isOcclusion": false,
"saturation": 80
},
"type": 5
}
},
{
"id": 7,
"name": "Video3",
"position": {
"x": 3,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentType": "video/mpeg4",
"id": "hou1.mp4",
"textureUrl": "https://www.ray650128.com/video/hou1.mp4"
},
"params": {
"availableRange": 20,
"hueAngle": 35,
"hueRange": 20,
"isDoubleSided": true,
"isFaceMe": false,
"isHidden": false,
"isIgnore": false,
"isOcclusion": false,
"saturation": 50
},
"type": 5
}
},
{
"id": 8,
"name": "Light",
"position": {
"x": 0,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"params": {
"availableRange": 20,
"color": {
"r": 1,
"g": 1,
"b": 0,
"a": 0
},
"lightIntensity": 25000
},
"type": 100
}
},
{
"id": 9,
"name": "MultiFace",
"position": {
"x": 0,
"y": 1,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentTypes": [
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg",
"image/jpeg"
],
"textureUrls": [
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"https://cdn2.ettoday.net/images/6350/d6350683.jpg"
]
},
"params": {
"levelAngles": [
45,
0,
-45
],
"levelBorders": [
0.1,
0.1,
0.1
],
"levelCount": 3,
"planeBorder": 0.1,
"planeCount": 5,
"speed": 1
},
"type": 6
}
},
{
"id": 10,
"name": "Sign",
"position": {
"x": 1,
"y": 1,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"material": {
"contentType": "image/jpeg",
"id": "d6350683.jpg",
"textureUrl": "https://cdn2.ettoday.net/images/6350/d6350683.jpg"
},
"params": {
"availableRange": 20,
"signIcon": "https://cdn2.ettoday.net/images/6350/d6350683.jpg",
"signName": "0.0/",
"signTextColor": {
"r": 1,
"g": 0,
"b": 0,
"a": 0
}
},
"type": 7
}
}
]

151
ModelParams.json Normal file
View File

@ -0,0 +1,151 @@
[
{
"type": 0,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"width": "Float",
"height": "Float",
"isSizeScaleLock": "Float"
}
},
{
"type": 1,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"width": "Float",
"height": "Float",
"depth": "Float"
}
},
{
"type": 2,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"radius": "Float"
}
},
{
"type": 3,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"radius": "Float",
"height": "Float"
}
},
{
"type": 4,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"modelAnimSpeed": "Float",
"modelStartFrame": "Float",
"modelEndFrame": "Float",
"modelFPS": "Float",
"multiplyNumber": "Int",
"multiplyRadius": "Float",
"multiplyRange": "Float",
"isMultiplyPlane": "Boolean"
}
},
{
"type": 5,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"hueAngle": "Float",
"hueRange": "Float",
"saturation": "Float",
"isPlayWhenReady": "Boolean",
"isRepeat": "Boolean",
"videoThumbnail": "String"
}
},
{
"type": 6,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"levelCount": "Int",
"levelBorders": "FloatArray",
"levelAngles": "FloatArray",
"planeCount": "Int",
"planeBorder": "Boolean",
"speed": "Float",
"isClockwise": "Boolean"
}
},
{
"type": 7,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"width": "Float",
"height": "Float",
"signIcon": "String",
"signName": "String",
"signTextColor": "ColorArray"
}
},
{
"type": 100,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"lightIntensity": "Float",
"color": "ColorArray"
}
},
{
"type": 101,
"availableParams": {
"isFaceMe": "Boolean",
"isIgnore": "Boolean",
"isHidden": "Boolean",
"isDoubleSided": "Boolean",
"isOcclusion": "Boolean",
"availableRange": "Float",
"lightIntensity": "Float",
"color": "ColorArray"
}
}
]

131
NewArModelData.json Normal file
View File

@ -0,0 +1,131 @@
{
"id": 0,
"name": "TEST",
"type": 0,
"location": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"scale": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"params": {
"width": 0.0,
"height": 0.0,
"depth": 0.0,
"radius": 0.0,
"isSizeScaleLock": false,
"availableRange": 20.0,
"isFaceMe": false,
"isIgnore": false,
"isHidden": false,
"isDoubleSided": false,
"isOcclusion": false,
"modelAnimSpeed": 0.0,
"modelStartFrame": 0,
"modelEndFrame": 0,
"modelFPS": 0.0,
"multiplyNumber": 0,
"multiplyRadius": 0.0,
"multiplyRange": 0.0,
"isMultiplyPlane": false,
"hueAngle": 0.0,
"hueRange": 0.0,
"saturation": 0.0,
"isPlayWhenReady": false,
"isRepeat": true,
"videoThumbnail": "",
"levelCount": 3,
"levelBorders": [0.5, 0.5, 0.5],
"levelAngles": [30.0, 0.0, -30.0],
"planeCount": 5,
"planeBorder": 0.25,
"speed": 60,
"isClockwise": true,
"signIcon": {
"textureId": 0,
"fileName": "",
"materialUrl": "",
"contentType": "image/png"
},
"signName": "",
"signTextColor": {
"r": 0.0,
"g": 0.0,
"b": 0.0,
"a": 0.0
},
"lightIntensity": 1000.0,
"color": {
"r": 0.0,
"g": 0.0,
"b": 0.0,
"a": 0.0
}
},
"material": [
{
"textureId": 0,
"fileName": "",
"materialUrl": "",
"contentType": "image/png"
}
],
"events": [
{
"eventType": 1,
"actions": [
{
"actionType": 2,
"alpha": 1,
"autoReturnDelay": 3,
"clockwise": true,
"duration": 1,
"endFrame": 100,
"fps": 24,
"groupNumber": 0,
"isAutoReturn": true,
"isFaceToMe": true,
"moveToFaceDistance": 1,
"repeatCount": 1,
"resolution": 16,
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"roundMoveEndAngel": 360,
"roundMoveRadius": 1,
"roundMoveStartAngle": 0,
"scale": {
"x": 0,
"y": 0,
"z": 0
},
"speed": 1,
"squareMoveHeight": 1,
"squareMoveRotationTime": 0.8,
"squareMoveTime": 1,
"squareMoveWidth": 1,
"startFrame": 0,
"targetId": 0,
"translation": {
"x": 1,
"y": 0,
"z": 0
},
"vibrationTime": 1,
"webUrl": ""
}
]
}
]
}

View File

@ -31,6 +31,8 @@ dependencies {
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
implementation("org.litote.kmongo:kmongo:$kmongo_version")
implementation("org.litote.kmongo:kmongo-id-serialization:$kmongo_version")
implementation("io.ktor:ktor-server-auth-jvm:$ktor_version")
implementation("io.ktor:ktor-server-auth-jwt-jvm:$ktor_version")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
}

View File

@ -1,32 +1,44 @@
package com.ray650128
import com.ray650128.dto.UserDto
import com.ray650128.model.User
import com.ray650128.plugins.configureRouting
import com.ray650128.plugins.configureSerialization
import com.ray650128.model.pojo.User
import com.ray650128.plugins.configureUserRouting
import com.ray650128.plugins.configureArMaterialRouting
import com.ray650128.plugins.configureArModelRouting
import com.ray650128.service.UserService
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import kotlinx.serialization.json.Json
import org.litote.kmongo.id.serialization.IdKotlinXSerializationModule
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
install(ContentNegotiation) {
json(
Json { serializersModule = IdKotlinXSerializationModule }
)
}
install(Authentication) {
jwt {
verifier(JwtConfig.verifier)
realm = JwtConfig.myRealm
validate {
val service = UserService()
val name = it.payload.getClaim("account").asString()
if (name != null) {
UserDto(account = name, password = "")
service.findByAccount(name)
} else {
null
}
}
}
}
configureRouting()
configureSerialization()
configureUserRouting()
configureArMaterialRouting()
configureArModelRouting()
}.start(wait = true)
}

View File

@ -3,7 +3,7 @@ package com.ray650128
import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm
import com.ray650128.dto.UserDto
import com.ray650128.model.pojo.User
import java.util.*
object JwtConfig {
@ -21,7 +21,7 @@ object JwtConfig {
/**
* Produce a token for this combination of name and password
*/
fun generateToken(user: UserDto): String = JWT.create()
fun generateToken(user: User): String = JWT.create()
.withSubject("Authentication")
.withIssuer(issuer)
.withClaim("account", user.account)

View File

@ -0,0 +1,32 @@
package com.ray650128.extension
import com.ray650128.model.pojo.arModel.ArModel
import com.ray650128.model.pojo.arModelDto.ArModelDto
fun ArModel.toDto(): ArModelDto =
ArModelDto(
_id = this._id.toString(),
ownerId = this.ownerId.toString(),
name = this.name,
position = this.position,
rotation = this.rotation,
scale = this.scale,
modelData = this.modelData.toDto(),
events = this.events,
childEvents = this.childEvents,
createAt = this.createAt,
updatedAt = this.updatedAt
)
fun ArModelDto.toArModel(): ArModel =
ArModel(
name = this.name,
position = this.position,
rotation = this.rotation,
scale = this.scale,
modelData = this.modelData.toModelData(),
events = this.events,
childEvents = this.childEvents,
createAt = this.createAt,
updatedAt = this.updatedAt
)

View File

@ -0,0 +1,22 @@
package com.ray650128.extension
import com.ray650128.model.pojo.arModel.ModelData
import com.ray650128.model.pojo.arModelDto.ModelDataDto
import com.ray650128.service.MaterialService
fun ModelData.toDto(): ModelDataDto {
val service = MaterialService()
return ModelDataDto(
type = this.type,
material = service.findById(this.material.toString()),
params = this.params
)
}
fun ModelDataDto.toModelData(): ModelData =
ModelData(
type = this.type,
material = this.material?._id,
params = this.params
)

View File

@ -0,0 +1,42 @@
package com.ray650128.extension
import com.ray650128.model.ErrorResponse
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.response.*
suspend fun <T> ApplicationCall.sendCreated(data: T?) {
this.respond(
status = HttpStatusCode.Created,
message = data ?: mapOf("message" to "success.")
)
}
suspend fun <T : Any> ApplicationCall.sendSuccess(data: T?) {
this.respond(
status = HttpStatusCode.OK,
message = data ?: mapOf("message" to "success.")
)
}
suspend fun ApplicationCall.sendUnauthorized() {
this.respond(
status = HttpStatusCode.Unauthorized,
message = ErrorResponse.UNAUTHORIZED_RESPONSE
)
}
suspend fun ApplicationCall.sendBadRequest(errorResponse: ErrorResponse) {
this.respond(
status = HttpStatusCode.BadRequest,
message = errorResponse
)
}
suspend fun ApplicationCall.sendNotFound() {
this.respond(
status = HttpStatusCode.NotFound,
message = ErrorResponse.NOT_FOUND_RESPONSE
)
}

View File

@ -1,25 +0,0 @@
package com.ray650128.extension
import com.ray650128.dto.UserDto
import com.ray650128.model.User
fun User.toDto(): UserDto =
UserDto(
id = this.id.toString(),
account = this.account,
password = this.password,
name = this.name,
token = this.token,
createAt = this.createAt,
updatedAt = this.updatedAt
)
fun UserDto.toUser(): User =
User(
account = this.account,
password = this.password,
name = this.name,
token = this.token,
createAt = this.createAt,
updatedAt = this.updatedAt
)

View File

@ -7,5 +7,6 @@ data class ErrorResponse(val message: String) {
companion object {
val NOT_FOUND_RESPONSE = ErrorResponse("Person was not found")
val BAD_REQUEST_RESPONSE = ErrorResponse("Invalid request")
val UNAUTHORIZED_RESPONSE = ErrorResponse("Unauthorized")
}
}

View File

@ -1,15 +0,0 @@
package com.ray650128.model
import org.bson.codecs.pojo.annotations.BsonId
import org.litote.kmongo.Id
data class User(
@BsonId
val id: Id<User>? = null,
val account: String,
val password: String,
var name: String? = null,
var token: String? = null,
var createAt: Long? = null,
var updatedAt: Long? = null
)

View File

@ -1,4 +1,4 @@
package com.ray650128.model
package com.ray650128.model.pojo
import kotlinx.serialization.Serializable

View File

@ -0,0 +1,18 @@
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 Material(
@Contextual var _id: Id<Material> = newId(),
@Contextual var ownerId: Id<User>? = null,
var name: String,
var path: String? = null,
var contentType: String? = null,
var fileTag: ArrayList<String>? = null,
var createAt: Long? = null,
var updatedAt: Long? = null
)

View File

@ -0,0 +1,8 @@
package com.ray650128.model.pojo
import kotlinx.serialization.Serializable
@Serializable
data class NewMaterial(
var name: String
)

View File

@ -1,14 +1,17 @@
package com.ray650128.dto
package com.ray650128.model.pojo
import io.ktor.server.auth.*
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.litote.kmongo.Id
import org.litote.kmongo.newId
@Serializable
data class UserDto(
val id: String? = null,
data class User(
@Contextual val _id: Id<User>? = newId(),
val account: String,
val password: String,
val name: String? = null,
var name: String? = null,
var token: String? = null,
var createAt: Long? = null,
var updatedAt: Long? = null

View File

@ -0,0 +1,22 @@
package com.ray650128.model.pojo.arModel
import com.ray650128.model.pojo.User
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.litote.kmongo.Id
import org.litote.kmongo.newId
@Serializable
data class ArModel(
@Contextual var _id: Id<ArModel>? = newId(),
@Contextual var ownerId: Id<User>? = null,
var name: String,
var position: Float3,
var rotation: Float3,
var scale: Float3,
var modelData: ModelData,
var events: ArrayList<ModelEvent>? = null,
var childEvents: HashMap<String, ArrayList<ModelEvent>?>? = null,
var createAt: Long? = null,
var updatedAt: Long? = null
)

View File

@ -0,0 +1,11 @@
package com.ray650128.model.pojo.arModel
import kotlinx.serialization.Serializable
@Serializable
data class Color(
var r: Float = 0.0f,
var g: Float = 0.0f,
var b: Float = 0.0f,
var a: Float = 0.0f
)

View File

@ -0,0 +1,10 @@
package com.ray650128.model.pojo.arModel
import kotlinx.serialization.Serializable
@Serializable
data class Float3(
var x: Float,
var y: Float,
var z: Float
)

View File

@ -0,0 +1,44 @@
package com.ray650128.model.pojo.arModel
import kotlinx.serialization.Serializable
@Serializable
data class ModelAction(
var actionType: Int,
var duration: Float?,
var translation: Float3? = null,
var rotation: Float3? = null,
var scale: Float3? = null,
var alpha: Float? = null,
var webUrl: String? = null,
var speed: Float? = null,
var startFrame: Float? = null,
var endFrame: Float? = null,
var fps: Float? = null,
var repeatCount: Int? = null,
var squareMoveWidth: Float? = null,
var squareMoveHeight: Float? = null,
var squareMoveTime: Float? = null,
var squareMoveRotationTime: Float? = null,
var clockwise: Boolean? = null,
var roundMoveRadius: Float? = null,
var roundMoveStartAngle: Float? = null,
var roundMoveEndAngel: Float? = null,
var resolution: Float? = null,
var moveToFaceDistance: Float? = null,
var isAutoReturn: Boolean? = null,
var autoReturnDelay: Float? = null,
var isFaceToMe: Boolean? = null,
var vibrationTime: Int? = null,
var targetId: Int? = null,
var groupNumber: Int? = null
)

View File

@ -0,0 +1,13 @@
package com.ray650128.model.pojo.arModel
import com.ray650128.model.pojo.Material
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.litote.kmongo.Id
@Serializable
data class ModelData(
var type: Int = -1,
@Contextual var material: Id<Material>? = null,
var params: ModelParams
)

View File

@ -0,0 +1,9 @@
package com.ray650128.model.pojo.arModel
import kotlinx.serialization.Serializable
@Serializable
data class ModelEvent(
var eventType: Int,
var actions: ArrayList<ModelAction>? = null
)

View File

@ -0,0 +1,51 @@
package com.ray650128.model.pojo.arModel
import kotlinx.serialization.Serializable
@Serializable
data class ModelParams(
var isFaceMe: Boolean = false,
var isIgnore: Boolean = false,
var isHidden: Boolean = false,
var isDoubleSided: Boolean = true,
var isOcclusion: Boolean = false,
var availableRange: Float = 20f,
var width: Float? = null,
var height: Float? = null,
var depth: Float? = null,
var radius: Float? = null,
var isSizeScaleLock: Boolean? = null,
var modelAnimSpeed: Float? = null,
var modelStartFrame: Float? = null,
var modelEndFrame: Float? = null,
var modelFPS: Float? = null,
var multiplyNumber: Int? = null,
var multiplyRadius: Float? = null,
var multiplyRange: Float? = null,
var isMultiplyPlane: Boolean? = null,
var hueAngle: Float? = null,
var hueRange: Float? = null,
var saturation: Float? = null,
var isPlayWhenReady: Boolean? = null,
var isRepeat: Boolean? = null,
var videoThumbnail: String? = null,
var lightIntensity: Float? = null,
var color: Color? = null,
var levelCount: Int? = null,
var levelBorders: ArrayList<Float>? = null,
var levelAngles: ArrayList<Float>? = null,
var planeCount: Int? = null,
var planeBorder: Float? = null,
var speed: Float? = null,
var isClockwise: Boolean? = null,
var signIcon: String? = null,
var signName: String? = null,
var signTextColor: Color? = null
)

View File

@ -0,0 +1,20 @@
package com.ray650128.model.pojo.arModelDto
import com.ray650128.model.pojo.arModel.Float3
import com.ray650128.model.pojo.arModel.ModelEvent
import kotlinx.serialization.Serializable
@Serializable
data class ArModelDto(
var _id: String? = null,
var ownerId: String? = null,
var name: String,
var position: Float3,
var rotation: Float3,
var scale: Float3,
var modelData: ModelDataDto,
var events: ArrayList<ModelEvent>? = null,
var childEvents: HashMap<String, ArrayList<ModelEvent>?>? = null,
var createAt: Long? = null,
var updatedAt: Long? = null
)

View File

@ -0,0 +1,14 @@
package com.ray650128.model.pojo.arModelDto
import com.ray650128.model.pojo.Material
import com.ray650128.model.pojo.arModel.ModelParams
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.litote.kmongo.Id
@Serializable
data class ModelDataDto(
var type: Int = -1,
var material: Material? = null,
var params: ModelParams
)

View File

@ -0,0 +1,167 @@
package com.ray650128.plugins
import com.ray650128.extension.*
import com.ray650128.model.ErrorResponse
import com.ray650128.model.pojo.Material
import com.ray650128.model.pojo.NewMaterial
import com.ray650128.model.pojo.User
import com.ray650128.service.MaterialService
import com.ray650128.service.UserService
import io.ktor.http.content.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.json.Json
import java.io.File
fun Application.configureArMaterialRouting() {
val userService = UserService()
val materialService = MaterialService()
routing {
route("/upload") {
get("/{name}") {
val fileName = call.parameters["name"] ?: run {
call.sendNotFound()
return@get
}
println("[File]filename: $fileName")
val file = File("./upload/$fileName")
println("[File]file: ${file.absolutePath}")
if(!file.exists()) {
call.sendNotFound()
return@get
}
call.respondFile(file)
}
}
authenticate {
route("/api") {
route("/v1") {
route("/materials") {
get {
val user = call.authentication.principal<User>() ?: run {
call.sendUnauthorized()
return@get
}
val list = materialService.findByOwnerId(user._id.toString())
call.sendSuccess(list)
}
post("/upload") {
val user = call.authentication.principal<User>() ?: run {
call.sendUnauthorized()
return@post
}
var materialId: String? = null
// retrieve all multipart data (suspending)
val multipart = call.receiveMultipart()
var params: NewMaterial? = null
multipart.forEachPart { part ->
// Check part type
when (part) {
is PartData.FormItem -> {
params = Json.decodeFromString(NewMaterial.serializer(), part.value)
println(part.value)
}
is PartData.FileItem -> {
// retrieve file name of upload
println("File content type: ${part.contentType?.contentType}/${part.contentType?.contentSubtype}")
val extensionName = when (part.contentType?.contentSubtype) {
"gltf-binary" -> "glb"
"gltf+json" -> "gltf"
else -> 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 tmpMaterial = Material(
ownerId = user._id!!,
name = if (params == null) {
part.originalFileName!!
} else {
params!!.name
},
path = "/upload/$name",
contentType = "${part.contentType}",
createAt = System.currentTimeMillis()
)
materialId = materialService.create(tmpMaterial).toString()
}
else -> {}
}
// make sure to dispose of the part after use to prevent leaks
part.dispose()
}
if (materialId != null) {
val data = materialService.findById(materialId!!)
call.sendSuccess(data)
} else {
call.sendBadRequest(ErrorResponse("Add material fail."))
}
}
put("/{id}") {
call.authentication.principal<User>() ?: run {
call.sendUnauthorized()
return@put
}
val body = call.receive<Material>()
val id = call.parameters["id"].toString()
val material = materialService.findById(id) ?: run {
call.sendNotFound()
return@put
}
material.apply {
name = body.name
fileTag = body.fileTag
updatedAt = System.currentTimeMillis()
}
val isSuccess = materialService.updateById(id, material)
call.sendSuccess(mapOf("success" to isSuccess))
}
delete("/{id}") {
call.authentication.principal<User>() ?: run {
call.sendUnauthorized()
return@delete
}
val id = call.parameters["id"].toString()
val material = materialService.findById(id) ?: run {
call.sendNotFound()
return@delete
}
val filePath = material.path
val file = File(".$filePath")
println(file.absolutePath)
if (file.exists()) {
file.delete()
}
val isSuccess = materialService.deleteById(id)
call.sendSuccess(mapOf("success" to isSuccess))
}
}
}
}
}
}
}

View File

@ -0,0 +1,81 @@
package com.ray650128.plugins
import com.ray650128.extension.*
import com.ray650128.model.ErrorResponse
import com.ray650128.model.pojo.arModel.ArModel
import com.ray650128.model.pojo.User
import com.ray650128.service.ModelService
import com.ray650128.service.UserService
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.routing.*
fun Application.configureArModelRouting() {
val userService = UserService()
val modelService = ModelService()
routing {
route("/api") {
route("/v1") {
authenticate {
route("/models") {
get {
val account = call.authentication.principal<User>()?.account ?: run {
call.sendUnauthorized()
return@get
}
val user = userService.findByAccount(account) ?: run {
call.sendUnauthorized()
return@get
}
val list = modelService.findByOwnerId(user._id.toString()).map(ArModel::toDto)
call.sendSuccess(list)
}
post("/create") {
val user = call.authentication.principal<User>() ?: run {
call.sendUnauthorized()
return@post
}
val body = call.receive<ArModel>()
body.apply {
ownerId = user._id
createAt = System.currentTimeMillis()
}
val modelId = modelService.create(body)
if (modelId != null) {
val data = modelService.findById(modelId.toString())?.toDto()
call.sendSuccess(data)
} else {
call.sendBadRequest(ErrorResponse("Add ar model fail."))
}
}
put("/{id}") {
call.authentication.principal<User>()?.account ?: run {
call.sendUnauthorized()
return@put
}
val body = call.receive<ArModel>()
val id = call.parameters["id"].toString()
val isSuccess = modelService.updateById(id, body)
call.sendSuccess(mapOf("success" to isSuccess))
}
delete("/{id}") {
call.authentication.principal<User>()?.account ?: run {
call.sendUnauthorized()
return@delete
}
val id = call.parameters["id"].toString()
val isSuccess = modelService.deleteById(id)
call.sendSuccess(mapOf("success" to isSuccess))
}
}
}
}
}
}
}

View File

@ -1,18 +0,0 @@
package com.ray650128.plugins
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.response.*
import io.ktor.server.application.*
import io.ktor.server.routing.*
fun Application.configureSerialization() {
install(ContentNegotiation) {
json()
}
routing {
get("/json/kotlinx-serialization") {
call.respond(mapOf("hello" to "world"))
}
}
}

View File

@ -1,12 +1,10 @@
package com.ray650128.plugins
import com.ray650128.JwtConfig
import com.ray650128.dto.UserDto
import com.ray650128.extension.toDto
import com.ray650128.extension.toUser
import com.ray650128.extension.*
import com.ray650128.model.ErrorResponse
import com.ray650128.model.LoginResult
import com.ray650128.model.User
import com.ray650128.model.pojo.LoginResult
import com.ray650128.model.pojo.User
import com.ray650128.service.UserService
import io.ktor.http.*
import io.ktor.server.routing.*
@ -15,32 +13,31 @@ import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
fun Application.configureRouting() {
fun Application.configureUserRouting() {
val service = UserService()
routing {
get("/") {
call.respondText("Hello World!")
}
route("/api") {
route("/v1") {
post("/register") {
val request = call.receive<UserDto>()
val request = call.receive<User>()
if (service.findByAccount(request.account) != null) {
call.sendBadRequest(ErrorResponse("User has existed."))
return@post
}
val newToken = JwtConfig.generateToken(request)
val user = request.toUser().apply {
val user = request.apply {
token = newToken
createAt = System.currentTimeMillis()
}
service.create(user)
?.let { userId ->
call.response.headers.append("My-User-Id-Header", userId.toString())
call.respond(HttpStatusCode.Created, LoginResult(request.account, newToken))
} ?: call.respond(HttpStatusCode.BadRequest, ErrorResponse.BAD_REQUEST_RESPONSE)
service.create(user)?.let { userId ->
call.response.headers.append("My-User-Id-Header", userId.toString())
call.sendCreated(LoginResult(request.account, newToken))
} ?: call.sendBadRequest(ErrorResponse.BAD_REQUEST_RESPONSE)
}
post("/login") {
val request = call.receive<UserDto>()
val request = call.receive<User>()
val user = service.findByLoginInfo(request.account, request.password)
if (user != null) {
var token = user.token
@ -48,66 +45,56 @@ fun Application.configureRouting() {
token = JwtConfig.generateToken(request)
user.token = token
user.updatedAt = System.currentTimeMillis()
service.updateById(user.id.toString(), user)
service.updateById(user._id.toString(), user)
}
call.respond(HttpStatusCode.OK, LoginResult(request.account, token!!))
call.sendSuccess(LoginResult(request.account, token!!))
} else {
call.respond(
status = HttpStatusCode.Unauthorized,
message = mapOf("message" to "Account or Password wrong.")
)
call.sendBadRequest(ErrorResponse("Account or Password wrong."))
}
}
authenticate {
post("/logout") {
val account = call.authentication.principal<UserDto>()?.account ?: run {
call.respond(
status = HttpStatusCode.Unauthorized,
message = mapOf("message" to "token wrong")
)
val account = call.authentication.principal<User>()?.account ?: run {
call.sendUnauthorized()
return@post
}
val user = service.findByAccount(account) ?: run {
call.respond(
status = HttpStatusCode.Unauthorized,
message = mapOf("message" to "token wrong")
)
call.sendUnauthorized()
return@post
}
user.apply {
token = null
updatedAt = System.currentTimeMillis()
}
service.updateById(user.id.toString(), user)
call.respond(HttpStatusCode.OK, "User has logged out")
service.updateById(user._id.toString(), user)
call.sendSuccess(null)
}
}
}
get {
val peopleList = service.findAll().map(User::toDto)
val peopleList = service.findAll()
call.respond(peopleList)
}
get("/{id}") {
val id = call.parameters["id"].toString()
service.findById(id)
?.let { foundPerson -> call.respond(foundPerson.toDto()) }
?: call.respond(HttpStatusCode.NotFound, ErrorResponse.NOT_FOUND_RESPONSE)
?.let { foundPerson -> call.respond(foundPerson) }
?: call.sendNotFound()
}
get("/search") {
val name = call.request.queryParameters["name"].toString()
val foundPeople = service.findByName(name).map(User::toDto)
val foundPeople = service.findByName(name)
call.respond(foundPeople)
}
put("/{id}") {
val id = call.parameters["id"].toString()
val userRequest = call.receive<UserDto>()
val user = userRequest.toUser()
val updatedSuccessfully = service.updateById(id, user)
val userRequest = call.receive<User>()
val updatedSuccessfully = service.updateById(id, userRequest)
if (updatedSuccessfully) {
call.respond(HttpStatusCode.NoContent)
} else {

View File

@ -0,0 +1,44 @@
package com.ray650128.service
import com.ray650128.model.pojo.Material
import com.ray650128.model.pojo.User
import org.bson.types.ObjectId
import org.litote.kmongo.*
import org.litote.kmongo.id.toId
class MaterialService {
private val client = KMongo.createClient("mongodb://www.ray650128.com:27017")
private val database = client.getDatabase("ar_system")
private val materialCollection = database.getCollection<Material>()
fun create(material: Material): Id<Material> {
materialCollection.insertOne(material)
return material._id
}
fun findById(id: String): Material? {
val bsonId: Id<Material> = ObjectId(id).toId()
return materialCollection.findOne(Material::_id eq bsonId)
}
fun findByOwnerId(ownerId: String): List<Material> {
val bsonId: Id<User> = ObjectId(ownerId).toId()
return materialCollection.find(Material::ownerId eq bsonId).toList()
}
fun updateById(id: String, request: Material): Boolean =
findById(id)?.let { material ->
val updateResult = materialCollection.replaceOne(
material.copy(
name = request.name,
updatedAt = request.updatedAt
)
)
updateResult.modifiedCount == 1L
} ?: false
fun deleteById(id: String): Boolean {
val deleteResult = materialCollection.deleteOneById(ObjectId(id))
return deleteResult.deletedCount == 1L
}
}

View File

@ -0,0 +1,50 @@
package com.ray650128.service
import com.ray650128.model.pojo.arModel.ArModel
import com.ray650128.model.pojo.User
import org.bson.types.ObjectId
import org.litote.kmongo.*
import org.litote.kmongo.id.toId
class ModelService {
private val client = KMongo.createClient("mongodb://www.ray650128.com:27017")
private val database = client.getDatabase("ar_system")
private val arModelCollection = database.getCollection<ArModel>()
fun create(arModel: ArModel): Id<ArModel>? {
arModelCollection.insertOne(arModel)
return arModel._id
}
fun findById(id: String): ArModel? {
val bsonId: Id<ArModel> = ObjectId(id).toId()
return arModelCollection.findOne(ArModel::_id eq bsonId)
}
fun findByOwnerId(ownerId: String): List<ArModel> {
val bsonId: Id<User> = ObjectId(ownerId).toId()
return arModelCollection.find(ArModel::ownerId eq bsonId).toList()
}
fun updateById(id: String, request: ArModel): Boolean =
findById(id)?.let { arModel ->
val updateResult = arModelCollection.replaceOne(
arModel.copy(
name = request.name,
position = request.position,
rotation = request.rotation,
scale = request.scale,
modelData = request.modelData,
events = request.events,
childEvents = request.childEvents,
updatedAt = request.updatedAt
)
)
updateResult.modifiedCount == 1L
} ?: false
fun deleteById(id: String): Boolean {
val deleteResult = arModelCollection.deleteOneById(ObjectId(id))
return deleteResult.deletedCount == 1L
}
}

View File

@ -1,25 +1,25 @@
package com.ray650128.service
import com.ray650128.model.User
import com.ray650128.model.pojo.User
import org.bson.types.ObjectId
import org.litote.kmongo.*
import org.litote.kmongo.id.toId
class UserService {
private val client = KMongo.createClient("mongodb://www.ray650128.com:27017")
private val database = client.getDatabase("users")
private val database = client.getDatabase("ar_system")
private val userCollection = database.getCollection<User>()
fun create(user: User): Id<User>? {
userCollection.insertOne(user)
return user.id
return user._id
}
fun findAll(): List<User> = userCollection.find().toList()
fun findById(id: String): User? {
val bsonId: Id<User> = ObjectId(id).toId()
return userCollection.findOne(User::id eq bsonId)
return userCollection.findOne(User::_id eq bsonId)
}
fun findByName(name: String): List<User> {

View File

@ -11,7 +11,7 @@ class ApplicationTest {
@Test
fun testRoot() = testApplication {
application {
configureRouting()
configureUserRouting()
}
client.get("/").apply {
assertEquals(HttpStatusCode.OK, status)