Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ bin/

### IntelliJ IDEA ###
.idea
.kotlin
*.iws
*.iml
*.ipr
*.log
out/
!**/src/main/**/out/
!**/src/test/**/out/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cn.edu.buaa.scs.model

/**
*
* @param ticket 访问凭证
* @param host 服务器主机地址
*/
data class TicketResponse(
/* 访问凭证 */
val ticket: kotlin.String? = null,
/* 服务器主机地址 */
val host: kotlin.String? = null
)
2 changes: 2 additions & 0 deletions cloudapi-web/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ dependencies {
implementation("io.ktor:ktor-server-status-pages:$ktor_version")
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("io.ktor:ktor-server-test-host-jvm:$ktor_version")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.7.3")
implementation("ch.qos.logback:logback-classic:1.4.11")

// Redis
implementation("io.lettuce:lettuce-core:6.1.5.RELEASE")
Expand Down
10 changes: 5 additions & 5 deletions cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/auth/Token.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,16 @@ fun fetchToken(call: ApplicationCall) {
else -> {
// TODO 后续兼容JWT校验

// 1. try to get token from cookies
possibleTokenKey.firstNotNullOfOrNull { call.request.cookies[it] } ?:

// 2. try to get token from headers
// 1. try to get token from headers
possibleTokenKey.firstNotNullOfOrNull {
call.request.headers[it]?.let { auth ->
if (auth.startsWith("Bearer")) auth.split(" ")[1] else auth
}
} ?:

// 2. try to get token from cookies
possibleTokenKey.firstNotNullOfOrNull { call.request.cookies[it] } ?:

// 3. try to get token from query parameters
possibleTokenKey.firstNotNullOfOrNull { call.request.queryParameters[it] }

Expand Down Expand Up @@ -110,7 +110,7 @@ fun fetchToken(call: ApplicationCall) {
// redis uuid token
authRedis.checkToken(token) ?:
// error
throw throw AuthorizationException("incorrect token")
throw throw AuthorizationException("登录已过期,请重新登录")

val user = User.id(userId)
setUser(user)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* cloudapi_v2
* buaa scs cloud api v2
*
* The version of the OpenAPI document: 2.0
* Contact: loheagn@icloud.com
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package cn.edu.buaa.scs.controller.models


/**
*
* @param id
* @param chatId
* @param updateTime
* @param title
* @param top
*/
data class ChatHistoryItem(
val id: kotlin.Int? = null,
val chatId: kotlin.String? = null,
val updateTime: kotlin.Long? = null,
val title: kotlin.String? = null,
val top: kotlin.Boolean? = null
)

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cn.edu.buaa.scs.controller.models
* @param departmentId 所在单位
* @param role 1 for student, 2 for teacher, 4 for admin
* @param name 姓名
* @param email 邮箱
*/
data class CreateUserRequest(
/* 学工号 */
Expand All @@ -27,6 +28,8 @@ data class CreateUserRequest(
/* 1 for student, 2 for teacher, 4 for admin */
val role: kotlin.Int,
/* 姓名 */
val name: kotlin.String? = null
val name: kotlin.String? = null,
/* 邮箱 */
val email: kotlin.String? = null
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* cloudapi_v2
* buaa scs cloud api v2
*
* The version of the OpenAPI document: 2.0
* Contact: loheagn@icloud.com
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package cn.edu.buaa.scs.controller.models


/**
*
* @param chatId
* @param appId
* @param offset
* @param pageSize
* @param loadCustomFeedbacks
*/
data class GetChatRecordsRequest(
val chatId: kotlin.String,
val appId: kotlin.String? = null,
val offset: kotlin.Int? = null,
val pageSize: kotlin.Int? = null,
val loadCustomFeedbacks: kotlin.Boolean? = null
)

Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ package cn.edu.buaa.scs.controller.models
* @param name
* @param email
* @param nickname
* @param departmentId
*/
data class PatchUserRequest(
val name: kotlin.String? = null,
val email: kotlin.String? = null,
val nickname: kotlin.String? = null
val nickname: kotlin.String? = null,
val departmentId: kotlin.Int? = null
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* cloudapi_v2
* buaa scs cloud api v2
*
* The version of the OpenAPI document: 2.0
* Contact: loheagn@icloud.com
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package cn.edu.buaa.scs.controller.models


/**
*
* @param ticket 访问凭证
* @param host 服务器主机地址
*/
data class TicketResponse(
/* 访问凭证 */
val ticket: kotlin.String? = null,
/* 服务器主机地址 */
val host: kotlin.String? = null
)

Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fun Application.configureMonitoring() {
pathDescription = "",
headers = call.request.headers.toMap(),
version = version,
realIp = call.request.headers["X-Custom-Remote-Addr"] ?: call.request.origin.host,
realIp = call.request.headers["X-Custom-Remote-Addr"] ?: call.request.origin.localHost,
userAgent = userAgent,
)
val logRecordResp = LogRecordResp(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fun Application.configureRouting() {
route("/api/v2") {
authRoute()
userRoute()
chatRoute()
courseRoute()
experimentRoute()
fileRoute()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ spec:
apiVersion: cloudapi.scs.buaa.edu.cn/v1alpha1
kind: ResourcePool
metadata:
name: personal-$userId
name: personal-${userId.lowercase()}
labels:
owner: $userId
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ class VirtualMachineReconciler(val client: KubernetesClient) : Reconciler<Virtua
if (createVmProcessMutex.tryLock(vm)) {
try {
vmClient.createVM(vm.spec.toCreateVmOptions()).getOrThrow()
} catch (e: Throwable) {
null
} finally {
createVmProcessMutex.unlock(vm)
}
Expand Down
34 changes: 34 additions & 0 deletions cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/model/ChatHistory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cn.edu.buaa.scs.model

import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.*

interface ChatHistory : Entity<ChatHistory>, IEntity {
var id: Int
var userId: String
var chatId: String
var updateTime: Long
var title: String
var top: Boolean

companion object : Entity.Factory<ChatHistory>()

override fun entityId(): IntOrString {
return IntOrString(this.id)
}
}

object ChatHistories : Table<ChatHistory>("chat_history") {
val id = int("id").primaryKey().bindTo { it.id }
val userId = varchar("userId").bindTo { it.userId }
val chatId = varchar("chatId").bindTo { it.chatId }
val updateTime = long("updateTime").bindTo { it.updateTime }
val title = varchar("title").bindTo { it.title }
val top = boolean("top").bindTo { it.top }
}

@Suppress("unused")
val Database.chatHistory
get() = this.sequenceOf(ChatHistories)
3 changes: 2 additions & 1 deletion cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/route/Admin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ fun Route.adminRoute() {
val req = call.receive<CreateUserRequest>()
val role = UserRole.fromLevel(req.role)
val name = req.name
val email = req.email
val departmentId = req.departmentId
call.respond(convertUserModel(call.admin.addUser(req.id, name, role, departmentId)))
call.respond(convertUserModel(call.admin.addUser(req.id, name, role, email, departmentId)))
}

delete {
Expand Down
12 changes: 6 additions & 6 deletions cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/route/Auth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fun Route.authRoute() {

route("buaaSSOLogin") {
post {
val ssoToken = call.request.queryParameters["ssoToken"] ?: throw BadRequestException("ssoToken is required")
val ssoToken = call.request.queryParameters["ssoToken"] ?: throw BadRequestException("请传入统一认证 token")
call.respond(
call.auth.buaaSSOLogin(ssoToken)
)
Expand All @@ -55,8 +55,8 @@ fun Route.authRoute() {
route("/tokenInfo") {
post {
val req = call.receiveParameters()
val token = req["token"] ?: throw BadRequestException("token is required")
val service = req["service"] ?: throw BadRequestException("service is required")
val token = req["token"] ?: throw BadRequestException("请传入统一认证 token")
val service = req["service"] ?: throw BadRequestException("请传入统一认证服务名")
call.respond(
call.auth.getTokenInfo(token, service)
)
Expand All @@ -65,9 +65,9 @@ fun Route.authRoute() {

route("/checkPermission") {
get {
val entityType = call.parameters["entityType"] ?: throw BadRequestException("entityType is required")
val entityId = call.parameters["entityId"] ?: throw BadRequestException("entityId is required")
val action = call.parameters["action"] ?: throw BadRequestException("action is required")
val entityType = call.parameters["entityType"] ?: throw BadRequestException("请传入对象类型")
val entityId = call.parameters["entityId"] ?: throw BadRequestException("请传入对象Id")
val action = call.parameters["action"] ?: throw BadRequestException("请传入动作")
call.respond(call.auth.checkPermission(entityType, entityId, action))
}
}
Expand Down
32 changes: 32 additions & 0 deletions cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/route/Chat.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cn.edu.buaa.scs.route

import cn.edu.buaa.scs.controller.models.ChatHistoryItem
import cn.edu.buaa.scs.controller.models.GetChatRecordsRequest
import cn.edu.buaa.scs.service.chat
import cn.edu.buaa.scs.utils.userId
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun Route.chatRoute() {
route("/chat") {
route("/history") {
get {
call.respond(call.chat.getChatHistoryByUserId(call.userId()))
}

post {
val chatHistoryItem = call.receive<ChatHistoryItem>()
call.chat.createChatHistory(call.userId(), chatHistoryItem)
call.respond("OK")
}
}

post("/records") {
val requestBody = call.receive<GetChatRecordsRequest>()
val records = call.chat.getChatRecords(requestBody)
call.respond(records)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ fun Route.experimentRoute() {
call.respond(call.convertExperimentResponse(experiment))
}

delete {
val experimentId = call.getExpIdFromPath()
call.experiment.deleteById(experimentId)
call.respond("OK")
}

route("/assignments") {
get {
call.respond(
Expand Down
5 changes: 5 additions & 0 deletions cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/route/Vm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ fun Route.vmRoute() {
}
}

post("/ticket") {
val vmId = call.getVmIdFromPath()
val ticketResponse = call.vm.getWebTicket(vmId)
call.respond(ticketResponse)
}
}

route("/template") {
Expand Down
4 changes: 2 additions & 2 deletions cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/service/Admin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ val ApplicationCall.admin: AdminService
class AdminService(val call: ApplicationCall) : IService {
companion object : IService.Caller<AdminService>()

fun addUser(id: String, name: String?, role: UserRole, departmentId: Int): User {
fun addUser(id: String, name: String?, role: UserRole, email: String?, departmentId: Int): User {
if (!call.user().isAdmin()) {
throw AuthorizationException("only admin can add user")
}

return User.createNewUnActiveUser(id, name, role, departmentId)
return User.createNewUnActiveUser(id, name, role, email, departmentId)
}

fun deleteUsers(userIds: List<String>) {
Expand Down
6 changes: 3 additions & 3 deletions cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/service/Auth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ class AuthService(val call: ApplicationCall) : IService {
): LoginUserResponse {
// check useId
if (!mysql.users.exists { it.id.eq(userId) }) {
throw BadRequestException("")
throw BadRequestException("用户名或密码错误")
}
// check password
val user = mysql.users.find { it.id.eq(userId) and it.password.eq(passwordHash) }
?: throw BadRequestException("")
?: throw BadRequestException("用户名或密码错误")
// check active
if (!user.isAccepted) {
throw BadRequestException("账号未激活,或信息不完整,请重新激活账户")
Expand Down Expand Up @@ -194,7 +194,7 @@ class AuthService(val call: ApplicationCall) : IService {
fun sendActiveEmail(id: String, name: String, email: String) {
val user = User.id(id)
if (user.isAccepted) {
throw cn.edu.buaa.scs.error.BadRequestException("the user is already active")
throw cn.edu.buaa.scs.error.BadRequestException("用户已经激活")
}
val token = "${user.id}${user.password}${System.currentTimeMillis()}".md5() + UlidCreator.getUlid().toString()

Expand Down
Loading
Loading