Compare commits

...

16 Commits

Author SHA1 Message Date
Raymond Yang 386abf850d 加入shadowJar外掛 2023-05-23 14:28:50 +08:00
Raymond Yang de7171b72b 新增get(id: String)功能 2023-05-23 14:19:16 +08:00
Raymond Yang b811352620 修正put model功能 2023-05-18 11:35:10 +08:00
Raymond Yang 51d5eae6c0 修正create model功能 2023-05-18 11:33:12 +08:00
Raymond Yang 2b18af2b2d 加入swagger 2023-05-18 09:52:34 +08:00
Raymond Yang 7c68dc5ceb 將mongoDB改成需登入 2023-05-18 09:29:20 +08:00
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
36 changed files with 2387 additions and 123 deletions
+193
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"
}
},
]
+548
View File
@@ -0,0 +1,548 @@
[
{
"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": {
"_id": "6464410c9c67517fcbccf447",
"ownerId": "646435a39c67517fcbccf426",
"name": "奸笑的綿芽",
"path": "/upload/1684291852002.jpeg",
"contentType": "image/jpeg",
"fileTag": [
"圖片",
"Hololive"
],
"createAt": 1684291852023,
"updatedAt": 1684294290879
},
"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": {
"_id": "6464410c9c67517fcbccf447",
"ownerId": "646435a39c67517fcbccf426",
"name": "奸笑的綿芽",
"path": "/upload/1684291852002.jpeg",
"contentType": "image/jpeg",
"fileTag": [
"圖片",
"Hololive"
],
"createAt": 1684291852023,
"updatedAt": 1684294290879
},
"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": {
"_id": "6464410c9c67517fcbccf447",
"ownerId": "646435a39c67517fcbccf426",
"name": "奸笑的綿芽",
"path": "/upload/1684291852002.jpeg",
"contentType": "image/jpeg",
"fileTag": [
"圖片",
"Hololive"
],
"createAt": 1684291852023,
"updatedAt": 1684294290879
},
"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": {
"_id": "6464410c9c67517fcbccf447",
"ownerId": "646435a39c67517fcbccf426",
"name": "奸笑的綿芽",
"path": "/upload/1684291852002.jpeg",
"contentType": "image/jpeg",
"fileTag": [
"圖片",
"Hololive"
],
"createAt": 1684291852023,
"updatedAt": 1684294290879
},
"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": {
"_id": "6464410c9c67517fcbccf447",
"ownerId": "646435a39c67517fcbccf426",
"name": "奸笑的綿芽",
"path": "/upload/1684291852002.jpeg",
"contentType": "image/jpeg",
"fileTag": [
"圖片",
"Hololive"
],
"createAt": 1684291852023,
"updatedAt": 1684294290879
},
"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": {
"_id": "6465a6e2f5c86b7c50520486",
"ownerId": "646435a39c67517fcbccf426",
"name": "獅子影片",
"path": "/upload/1684383458922.mp4",
"contentType": "video/mp4",
"createAt": 1684383458939
},
"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": {
"_id": "6465a6e2f5c86b7c50520486",
"ownerId": "646435a39c67517fcbccf426",
"name": "獅子影片",
"path": "/upload/1684383458922.mp4",
"contentType": "video/mp4",
"createAt": 1684383458939
},
"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": {
"_id": "6464410c9c67517fcbccf447",
"ownerId": "646435a39c67517fcbccf426",
"name": "奸笑的綿芽",
"path": "/upload/1684291852002.jpeg",
"contentType": "image/jpeg",
"fileTag": [
"圖片",
"Hololive"
],
"createAt": 1684291852023,
"updatedAt": 1684294290879
},
"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": "MultiplePlane",
"position": {
"x": 1,
"y": -1,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 1,
"z": 1
},
"modelData": {
"materials": [
{
"_id": "6464410c9c67517fcbccf447",
"ownerId": "646435a39c67517fcbccf426",
"name": "奸笑的綿芽",
"path": "/upload/1684291852002.jpeg",
"contentType": "image/jpeg",
"fileTag": [
"圖片",
"Hololive"
],
"createAt": 1684291852023,
"updatedAt": 1684294290879
},
{
"_id": "646590294df2e46df0f7a51b",
"ownerId": "646435a39c67517fcbccf426",
"name": "供三小",
"path": "/upload/1684377641965.jpeg",
"contentType": "image/jpeg",
"createAt": 1684377641977
},
{
"_id": "646590674df2e46df0f7a524",
"ownerId": "646435a39c67517fcbccf426",
"name": "解散囉",
"path": "/upload/1684377703013.png",
"contentType": "image/png",
"createAt": 1684377703020
},
{
"_id": "646590e04df2e46df0f7a52f",
"ownerId": "646435a39c67517fcbccf426",
"name": "QRCode",
"path": "/upload/1684377824564.png",
"contentType": "image/png",
"createAt": 1684377824573
},
{
"_id": "646593304df2e46df0f7a54b",
"ownerId": "646435a39c67517fcbccf426",
"name": "煙霧",
"path": "/upload/1684378416412.png",
"contentType": "image/png",
"createAt": 1684378416417
}
],
"params": {
"levelAngles": [ 0 ],
"levelBorders": [ 0 ],
"levelCount": 1,
"planeBorder": 0.1,
"planeCount": 4,
"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
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
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": ""
}
]
}
]
}
+12
View File
@@ -2,11 +2,13 @@ val ktor_version: String by project
val kotlin_version: String by project val kotlin_version: String by project
val logback_version: String by project val logback_version: String by project
val kmongo_version: String by project val kmongo_version: String by project
val swagger_codegen_version: String by project
plugins { plugins {
kotlin("jvm") version "1.8.21" kotlin("jvm") version "1.8.21"
id("io.ktor.plugin") version "2.3.0" id("io.ktor.plugin") version "2.3.0"
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.21" id("org.jetbrains.kotlin.plugin.serialization") version "1.8.21"
id("com.github.johnrengelman.shadow") version "7.1.2"
} }
group = "com.ray650128" group = "com.ray650128"
@@ -22,8 +24,15 @@ repositories {
mavenCentral() mavenCentral()
} }
tasks {
create("stage").dependsOn("installDist")
}
dependencies { dependencies {
implementation("io.ktor:ktor-server-core-jvm:$ktor_version") implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
implementation("io.ktor:ktor-server-swagger:$ktor_version")
implementation("io.ktor:ktor-server-openapi:$ktor_version")
implementation("io.ktor:ktor-server-cors:$ktor_version")
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version") implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version") implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version")
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version") implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
@@ -31,6 +40,9 @@ dependencies {
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version") testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
implementation("org.litote.kmongo:kmongo:$kmongo_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-jvm:$ktor_version")
implementation("io.ktor:ktor-server-auth-jwt-jvm:$ktor_version") implementation("io.ktor:ktor-server-auth-jwt-jvm:$ktor_version")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
implementation("io.swagger.codegen.v3:swagger-codegen-generators:1.0.39")
} }
+29 -8
View File
@@ -1,32 +1,53 @@
package com.ray650128 package com.ray650128
import com.ray650128.dto.UserDto import com.ray650128.plugins.configureArMaterialRouting
import com.ray650128.model.User import com.ray650128.plugins.configureArModelRouting
import com.ray650128.plugins.configureRouting import com.ray650128.plugins.configureSwaggerRouting
import com.ray650128.plugins.configureSerialization import com.ray650128.plugins.configureUserRouting
import com.ray650128.service.UserService
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.* import io.ktor.server.auth.jwt.*
import io.ktor.server.engine.* import io.ktor.server.engine.*
import io.ktor.server.netty.* import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.swagger.codegen.v3.generators.html.*
import kotlinx.serialization.json.Json
import org.litote.kmongo.id.serialization.IdKotlinXSerializationModule
fun main() { fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") { embeddedServer(Netty, port = 8787, host = "0.0.0.0") {
install(ContentNegotiation) {
json(
Json { serializersModule = IdKotlinXSerializationModule }
)
}
install(Authentication) { install(Authentication) {
jwt { jwt {
verifier(JwtConfig.verifier) verifier(JwtConfig.verifier)
realm = JwtConfig.myRealm realm = JwtConfig.myRealm
validate { validate {
val service = UserService()
val name = it.payload.getClaim("account").asString() val name = it.payload.getClaim("account").asString()
if (name != null) { if (name != null) {
UserDto(account = name, password = "") service.findByAccount(name)
} else { } else {
null null
} }
} }
} }
} }
configureRouting()
configureSerialization() install(CORS) {
anyHost()
allowHeader(HttpHeaders.ContentType)
}
configureUserRouting()
configureArMaterialRouting()
configureArModelRouting()
configureSwaggerRouting()
}.start(wait = true) }.start(wait = true)
} }
+2 -2
View File
@@ -3,7 +3,7 @@ package com.ray650128
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
import com.ray650128.dto.UserDto import com.ray650128.model.pojo.User
import java.util.* import java.util.*
object JwtConfig { object JwtConfig {
@@ -21,7 +21,7 @@ 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: UserDto): String = JWT.create() fun generateToken(user: User): String = JWT.create()
.withSubject("Authentication") .withSubject("Authentication")
.withIssuer(issuer) .withIssuer(issuer)
.withClaim("account", user.account) .withClaim("account", user.account)
@@ -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
)
@@ -0,0 +1,41 @@
package com.ray650128.extension
import com.ray650128.model.pojo.Material
import com.ray650128.model.pojo.arModel.ModelData
import com.ray650128.model.pojo.arModelDto.ModelDataDto
import com.ray650128.service.MaterialService
import org.litote.kmongo.Id
fun ModelData.toDto(): ModelDataDto {
val service = MaterialService()
return ModelDataDto(
type = this.type,
material = if (this.material != null) {
service.findById(this.material.toString())
} else null,
materials = if (this.materials != null) {
ArrayList<Material>().apply {
materials?.forEach { id ->
val material = service.findById(id.toString())
if (material != null) {
add(material)
}
}
}
} else null,
params = this.params
)
}
fun ModelDataDto.toModelData(): ModelData =
ModelData(
type = this.type,
material = this.material?._id,
materials = ArrayList<Id<Material>>().apply {
materials?.forEach { material ->
add(material._id)
}
},
params = this.params
)
@@ -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
)
}
@@ -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
)
@@ -7,5 +7,6 @@ data class ErrorResponse(val message: String) {
companion object { companion object {
val NOT_FOUND_RESPONSE = ErrorResponse("Person was not found") val NOT_FOUND_RESPONSE = ErrorResponse("Person was not found")
val BAD_REQUEST_RESPONSE = ErrorResponse("Invalid request") val BAD_REQUEST_RESPONSE = ErrorResponse("Invalid request")
val UNAUTHORIZED_RESPONSE = ErrorResponse("Unauthorized")
} }
} }
@@ -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
)
@@ -1,4 +1,4 @@
package com.ray650128.model package com.ray650128.model.pojo
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -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
)
@@ -0,0 +1,8 @@
package com.ray650128.model.pojo
import kotlinx.serialization.Serializable
@Serializable
data class NewMaterial(
var name: String
)
@@ -1,14 +1,17 @@
package com.ray650128.dto package com.ray650128.model.pojo
import io.ktor.server.auth.* import io.ktor.server.auth.*
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import org.litote.kmongo.Id
import org.litote.kmongo.newId
@Serializable @Serializable
data class UserDto( data class User(
val id: String? = null, @Contextual val _id: Id<User>? = newId(),
val account: String, val account: String,
val password: String, val password: String,
val name: String? = null, var name: String? = null,
var token: String? = null, var token: String? = null,
var createAt: Long? = null, var createAt: Long? = null,
var updatedAt: Long? = null var updatedAt: Long? = null
@@ -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
)
@@ -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
)
@@ -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
)
@@ -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
)
@@ -0,0 +1,14 @@
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,
@Contextual var materials: List<Id<Material>>? = null,
var params: ModelParams
)
@@ -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
)
@@ -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
)
@@ -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
)
@@ -0,0 +1,15 @@
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 materials: List<Material>? = null,
var params: ModelParams
)
@@ -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))
}
}
}
}
}
}
}
@@ -0,0 +1,95 @@
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.model.pojo.arModelDto.ArModelDto
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)
}
get("/{id}") {
call.authentication.principal<User>()?.account ?: run {
call.sendUnauthorized()
return@get
}
val id = call.parameters["id"].toString()
val arModel = modelService.findById(id) ?: run {
call.sendNotFound()
return@get
}
call.sendSuccess(arModel.toDto())
}
post("/create") {
val user = call.authentication.principal<User>() ?: run {
call.sendUnauthorized()
return@post
}
val body = call.receive<ArModelDto>().toArModel()
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<ArModelDto>().toArModel()
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))
}
}
}
}
}
}
}
@@ -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"))
}
}
}
@@ -0,0 +1,12 @@
package com.ray650128.plugins
import io.ktor.server.application.*
import io.ktor.server.plugins.swagger.*
import io.ktor.server.routing.*
fun Application.configureSwaggerRouting() {
routing {
swaggerUI(path = "swagger", swaggerFile = "openapi/documentation.yaml")
}
}
@@ -1,12 +1,10 @@
package com.ray650128.plugins package com.ray650128.plugins
import com.ray650128.JwtConfig import com.ray650128.JwtConfig
import com.ray650128.dto.UserDto import com.ray650128.extension.*
import com.ray650128.extension.toDto
import com.ray650128.extension.toUser
import com.ray650128.model.ErrorResponse import com.ray650128.model.ErrorResponse
import com.ray650128.model.LoginResult import com.ray650128.model.pojo.LoginResult
import com.ray650128.model.User import com.ray650128.model.pojo.User
import com.ray650128.service.UserService import com.ray650128.service.UserService
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
@@ -15,32 +13,31 @@ import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
import io.ktor.server.request.* import io.ktor.server.request.*
fun Application.configureRouting() { fun Application.configureUserRouting() {
val service = UserService() val service = UserService()
routing { routing {
get("/") {
call.respondText("Hello World!")
}
route("/api") { route("/api") {
route("/v1") { route("/v1") {
post("/register") { 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 newToken = JwtConfig.generateToken(request)
val user = request.toUser().apply { val user = request.apply {
token = newToken token = newToken
createAt = System.currentTimeMillis() createAt = System.currentTimeMillis()
} }
service.create(user) service.create(user)?.let { userId ->
?.let { userId ->
call.response.headers.append("My-User-Id-Header", userId.toString()) call.response.headers.append("My-User-Id-Header", userId.toString())
call.respond(HttpStatusCode.Created, LoginResult(request.account, newToken)) call.sendCreated(LoginResult(request.account, newToken))
} ?: call.respond(HttpStatusCode.BadRequest, ErrorResponse.BAD_REQUEST_RESPONSE) } ?: call.sendBadRequest(ErrorResponse.BAD_REQUEST_RESPONSE)
} }
post("/login") { post("/login") {
val request = call.receive<UserDto>() val request = call.receive<User>()
val user = service.findByLoginInfo(request.account, request.password) val user = service.findByLoginInfo(request.account, request.password)
if (user != null) { if (user != null) {
var token = user.token var token = user.token
@@ -48,66 +45,56 @@ fun Application.configureRouting() {
token = JwtConfig.generateToken(request) token = JwtConfig.generateToken(request)
user.token = token user.token = token
user.updatedAt = System.currentTimeMillis() 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 { } else {
call.respond( call.sendBadRequest(ErrorResponse("Account or Password wrong."))
status = HttpStatusCode.Unauthorized,
message = mapOf("message" to "Account or Password wrong.")
)
} }
} }
authenticate { authenticate {
post("/logout") { post("/logout") {
val account = call.authentication.principal<UserDto>()?.account ?: run { val account = call.authentication.principal<User>()?.account ?: run {
call.respond( call.sendUnauthorized()
status = HttpStatusCode.Unauthorized,
message = mapOf("message" to "token wrong")
)
return@post return@post
} }
val user = service.findByAccount(account) ?: run { val user = service.findByAccount(account) ?: run {
call.respond( call.sendUnauthorized()
status = HttpStatusCode.Unauthorized,
message = mapOf("message" to "token wrong")
)
return@post return@post
} }
user.apply { user.apply {
token = null token = null
updatedAt = System.currentTimeMillis() updatedAt = System.currentTimeMillis()
} }
service.updateById(user.id.toString(), user) service.updateById(user._id.toString(), user)
call.respond(HttpStatusCode.OK, "User has logged out") call.sendSuccess(null)
} }
} }
} }
get { /*get {
val peopleList = service.findAll().map(User::toDto) val peopleList = service.findAll()
call.respond(peopleList) call.respond(peopleList)
} }
get("/{id}") { get("/{id}") {
val id = call.parameters["id"].toString() val id = call.parameters["id"].toString()
service.findById(id) service.findById(id)
?.let { foundPerson -> call.respond(foundPerson.toDto()) } ?.let { foundPerson -> call.respond(foundPerson) }
?: call.respond(HttpStatusCode.NotFound, ErrorResponse.NOT_FOUND_RESPONSE) ?: call.sendNotFound()
} }
get("/search") { get("/search") {
val name = call.request.queryParameters["name"].toString() val name = call.request.queryParameters["name"].toString()
val foundPeople = service.findByName(name).map(User::toDto) val foundPeople = service.findByName(name)
call.respond(foundPeople) call.respond(foundPeople)
} }
put("/{id}") { put("/{id}") {
val id = call.parameters["id"].toString() val id = call.parameters["id"].toString()
val userRequest = call.receive<UserDto>() val userRequest = call.receive<User>()
val user = userRequest.toUser() val updatedSuccessfully = service.updateById(id, userRequest)
val updatedSuccessfully = service.updateById(id, user)
if (updatedSuccessfully) { if (updatedSuccessfully) {
call.respond(HttpStatusCode.NoContent) call.respond(HttpStatusCode.NoContent)
} else { } else {
@@ -123,7 +110,7 @@ fun Application.configureRouting() {
} else { } else {
call.respond(HttpStatusCode.NotFound, ErrorResponse.NOT_FOUND_RESPONSE) call.respond(HttpStatusCode.NotFound, ErrorResponse.NOT_FOUND_RESPONSE)
} }
} }*/
} }
} }
} }
@@ -0,0 +1,45 @@
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://ray650128:Zx650128!@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,
fileTag = request.fileTag,
updatedAt = request.updatedAt
)
)
updateResult.modifiedCount == 1L
} ?: false
fun deleteById(id: String): Boolean {
val deleteResult = materialCollection.deleteOneById(ObjectId(id))
return deleteResult.deletedCount == 1L
}
}
@@ -0,0 +1,49 @@
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://ray650128:Zx650128!@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
}
}
@@ -1,25 +1,25 @@
package com.ray650128.service package com.ray650128.service
import com.ray650128.model.User import com.ray650128.model.pojo.User
import org.bson.types.ObjectId import org.bson.types.ObjectId
import org.litote.kmongo.* import org.litote.kmongo.*
import org.litote.kmongo.id.toId import org.litote.kmongo.id.toId
class UserService { class UserService {
private val client = KMongo.createClient("mongodb://www.ray650128.com:27017") private val client = KMongo.createClient("mongodb://ray650128:Zx650128!@www.ray650128.com:27017")
private val database = client.getDatabase("users") private val database = client.getDatabase("ar_system")
private val userCollection = database.getCollection<User>() private val userCollection = database.getCollection<User>()
fun create(user: User): Id<User>? { fun create(user: User): Id<User>? {
userCollection.insertOne(user) userCollection.insertOne(user)
return user.id return user._id
} }
fun findAll(): List<User> = userCollection.find().toList() fun findAll(): List<User> = userCollection.find().toList()
fun findById(id: String): User? { fun findById(id: String): User? {
val bsonId: Id<User> = ObjectId(id).toId() 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> { fun findByName(name: String): List<User> {
@@ -0,0 +1,570 @@
openapi: "3.0.3"
info:
title: "KtorMongoTest API"
description: "KtorMongoTest API"
version: "1.0.0"
servers:
- url: "https://KtorMongoTest"
paths:
/api/v1/models:
get:
description: ""
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
type: "object"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
/api/v1/models/{id}:
delete:
description: ""
parameters:
- name: "id"
in: "path"
required: true
schema:
type: "string"
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
put:
description: ""
parameters:
- name: "id"
in: "path"
required: true
schema:
type: "string"
requestBody:
content:
'*/*':
schema:
$ref: "#/components/schemas/ArModel"
required: true
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
/api/v1/models/create:
post:
description: ""
requestBody:
content:
'*/*':
schema:
$ref: "#/components/schemas/ArModel"
required: true
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
"400":
description: "Bad Request"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
/api/v1/login:
post:
description: ""
requestBody:
content:
'*/*':
schema:
$ref: "#/components/schemas/User"
required: true
responses:
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
"400":
description: "Bad Request"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
/api/v1/logout:
post:
description: ""
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
type: "object"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
/api/v1/register:
post:
description: ""
requestBody:
content:
'*/*':
schema:
$ref: "#/components/schemas/User"
required: true
responses:
"400":
description: "Bad Request"
content:
'*/*':
schema:
type: "object"
headers:
My-User-Id-Header:
required: true
schema:
type: "string"
"201":
description: "Created"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
headers:
My-User-Id-Header:
required: true
schema:
type: "string"
/api/v1/materials:
get:
description: ""
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
/api/v1/materials/{id}:
delete:
description: ""
parameters:
- name: "id"
in: "path"
required: true
schema:
type: "string"
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: "Not Found"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
put:
description: ""
parameters:
- name: "id"
in: "path"
required: true
schema:
type: "string"
requestBody:
content:
'*/*':
schema:
$ref: "#/components/schemas/Material"
required: true
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: "Not Found"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
/api/v1/materials/upload:
post:
description: ""
responses:
"401":
description: "Unauthorized"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
"200":
description: "OK"
content:
'*/*':
schema:
$ref: "#/components/schemas/Object"
"400":
description: "Bad Request"
content:
'*/*':
schema:
$ref: "#/components/schemas/ErrorResponse"
/upload/{name}:
get:
description: ""
parameters:
- name: "name"
in: "path"
required: true
schema:
type: "string"
responses:
"404":
description: "Not Found"
content:
'*/*':
schema:
type: "object"
"200":
description: "OK <br> A file response"
content:
application/*:
schema:
type: "object"
format: "binary"
components:
schemas:
ErrorResponse:
type: "object"
properties:
message:
type: "string"
Object:
type: "object"
properties: {}
User:
type: "object"
properties:
_id:
$ref: "#/components/schemas/User"
account:
type: "string"
password:
type: "string"
name:
type: "string"
token:
type: "string"
createAt:
type: "integer"
format: "int64"
updatedAt:
type: "integer"
format: "int64"
Float3:
type: "object"
properties:
x:
type: "number"
format: "float"
"y":
type: "number"
format: "float"
z:
type: "number"
format: "float"
Material:
type: "object"
properties:
_id:
$ref: "#/components/schemas/Material"
ownerId:
$ref: "#/components/schemas/User"
name:
type: "string"
path:
type: "string"
contentType:
type: "string"
fileTag:
type: "string"
createAt:
type: "integer"
format: "int64"
updatedAt:
type: "integer"
format: "int64"
Color:
type: "object"
properties:
r:
type: "number"
format: "float"
g:
type: "number"
format: "float"
b:
type: "number"
format: "float"
a:
type: "number"
format: "float"
ModelParams:
type: "object"
properties:
availableRange:
type: "number"
format: "float"
width:
type: "number"
format: "float"
height:
type: "number"
format: "float"
depth:
type: "number"
format: "float"
radius:
type: "number"
format: "float"
modelAnimSpeed:
type: "number"
format: "float"
modelStartFrame:
type: "number"
format: "float"
modelEndFrame:
type: "number"
format: "float"
modelFPS:
type: "number"
format: "float"
multiplyNumber:
type: "integer"
format: "int32"
multiplyRadius:
type: "number"
format: "float"
multiplyRange:
type: "number"
format: "float"
hueAngle:
type: "number"
format: "float"
hueRange:
type: "number"
format: "float"
saturation:
type: "number"
format: "float"
videoThumbnail:
type: "string"
lightIntensity:
type: "number"
format: "float"
color:
$ref: "#/components/schemas/Color"
levelCount:
type: "integer"
format: "int32"
levelBorders:
type: "number"
format: "float"
levelAngles:
type: "number"
format: "float"
planeCount:
type: "integer"
format: "int32"
planeBorder:
type: "number"
format: "float"
speed:
type: "number"
format: "float"
signIcon:
type: "string"
signName:
type: "string"
signTextColor:
$ref: "#/components/schemas/Color"
ModelData:
type: "object"
properties:
type:
type: "integer"
format: "int32"
material:
$ref: "#/components/schemas/Material"
params:
$ref: "#/components/schemas/ModelParams"
ModelAction:
type: "object"
properties:
actionType:
type: "integer"
format: "int32"
duration:
type: "number"
format: "float"
translation:
$ref: "#/components/schemas/Float3"
rotation:
$ref: "#/components/schemas/Float3"
scale:
$ref: "#/components/schemas/Float3"
alpha:
type: "number"
format: "float"
webUrl:
type: "string"
speed:
type: "number"
format: "float"
startFrame:
type: "number"
format: "float"
endFrame:
type: "number"
format: "float"
fps:
type: "number"
format: "float"
repeatCount:
type: "integer"
format: "int32"
squareMoveWidth:
type: "number"
format: "float"
squareMoveHeight:
type: "number"
format: "float"
squareMoveTime:
type: "number"
format: "float"
squareMoveRotationTime:
type: "number"
format: "float"
clockwise:
type: "boolean"
roundMoveRadius:
type: "number"
format: "float"
roundMoveStartAngle:
type: "number"
format: "float"
roundMoveEndAngel:
type: "number"
format: "float"
resolution:
type: "number"
format: "float"
moveToFaceDistance:
type: "number"
format: "float"
autoReturnDelay:
type: "number"
format: "float"
vibrationTime:
type: "integer"
format: "int32"
targetId:
type: "integer"
format: "int32"
groupNumber:
type: "integer"
format: "int32"
ModelEvent:
type: "object"
properties:
eventType:
type: "integer"
format: "int32"
actions:
$ref: "#/components/schemas/ModelAction"
ArModel:
type: "object"
properties:
_id:
$ref: "#/components/schemas/ArModel"
ownerId:
$ref: "#/components/schemas/User"
name:
type: "string"
position:
$ref: "#/components/schemas/Float3"
rotation:
$ref: "#/components/schemas/Float3"
scale:
$ref: "#/components/schemas/Float3"
modelData:
$ref: "#/components/schemas/ModelData"
events:
$ref: "#/components/schemas/ModelEvent"
childEvents:
type: "string"
createAt:
type: "integer"
format: "int64"
updatedAt:
type: "integer"
format: "int64"
@@ -11,7 +11,7 @@ class ApplicationTest {
@Test @Test
fun testRoot() = testApplication { fun testRoot() = testApplication {
application { application {
configureRouting() configureUserRouting()
} }
client.get("/").apply { client.get("/").apply {
assertEquals(HttpStatusCode.OK, status) assertEquals(HttpStatusCode.OK, status)