diff --git a/ktor/src/main/kotlin/ink/snowflake/server/Application.kt b/ktor/src/main/kotlin/ink/snowflake/server/Application.kt index 8182885..84c3167 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/Application.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/Application.kt @@ -1,16 +1,22 @@ package ink.snowflake.server import com.google.gson.Gson -import ink.snowflake.server.plugins.* -import ink.snowflake.server.route.User -import ink.snowflake.server.route.chat -import ink.snowflake.server.plugins.configureSockets -import ink.snowflake.server.route.AI -import ink.snowflake.server.route.ImageAnalytics -import ink.snowflake.server.route.RemoteDebug -import ink.snowflake.server.route.VideoAnalytics -import ink.snowflake.server.route.VideoAnalyticsJetson +import ink.snowflake.server.controller.User +import ink.snowflake.server.controller.chat +import ink.snowflake.server.utils.plugins.configureSockets +import ink.snowflake.server.controller.AI +import ink.snowflake.server.controller.ImageAnalytics +import ink.snowflake.server.controller.RemoteDebug +import ink.snowflake.server.controller.VideoAnalytics +import ink.snowflake.server.controller.VideoAnalyticsJetson import ink.snowflake.server.utils.AppConfig +import ink.snowflake.server.utils.plugins.configureCORS +import ink.snowflake.server.utils.plugins.configureDatabases +import ink.snowflake.server.utils.plugins.configureSecurity +import ink.snowflake.server.utils.plugins.configureSerialization +import ink.snowflake.server.utils.plugins.configureStaticPath +import ink.snowflake.server.utils.plugins.configureStatusPages +import ink.snowflake.server.utils.plugins.configureTemplating import io.ktor.server.application.* import io.ktor.server.tomcat.jakarta.* diff --git a/ktor/src/main/kotlin/ink/snowflake/server/route/AI.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/AI.kt similarity index 77% rename from ktor/src/main/kotlin/ink/snowflake/server/route/AI.kt rename to ktor/src/main/kotlin/ink/snowflake/server/controller/AI.kt index 44df22f..1e35c85 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/route/AI.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/AI.kt @@ -1,13 +1,7 @@ -package ink.snowflake.server.route +package ink.snowflake.server.controller import ai.koog.agents.core.agent.AIAgent -import ai.koog.agents.core.tools.ToolRegistry -import ai.koog.agents.ext.tool.SayToUser -import ai.koog.prompt.executor.clients.openai.OpenAIModels import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor -import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor -import ai.koog.prompt.executor.ollama.client.OllamaClient -import ai.koog.prompt.executor.ollama.client.OllamaModelCard import ai.koog.prompt.llm.LLMCapability import ai.koog.prompt.llm.LLMProvider import ai.koog.prompt.llm.LLModel @@ -17,7 +11,6 @@ import io.ktor.server.response.respond import io.ktor.server.routing.get import io.ktor.server.routing.route import io.ktor.server.routing.routing -import kotlinx.coroutines.launch fun Application.AI() { diff --git a/ktor/src/main/kotlin/ink/snowflake/server/route/Chat.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/Chat.kt similarity index 98% rename from ktor/src/main/kotlin/ink/snowflake/server/route/Chat.kt rename to ktor/src/main/kotlin/ink/snowflake/server/controller/Chat.kt index cd1268d..4b4f769 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/route/Chat.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/Chat.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.route +package ink.snowflake.server.controller import com.cyberecho.mdoel.database.WSChatRecords import ink.snowflake.server.gson @@ -6,7 +6,7 @@ import ink.snowflake.server.model.request.ChatRequest import ink.snowflake.server.model.response.ChatResponse import ink.snowflake.server.model.response.BaseResponse import ink.snowflake.server.utils.WebSocketManager -import ink.snowflake.server.database.ChatRecordsDao +import ink.snowflake.server.utils.dao.ChatRecordsDao import ink.snowflake.server.utils.getUserIdByToken import io.ktor.client.* import io.ktor.client.engine.cio.* diff --git a/ktor/src/main/kotlin/ink/snowflake/server/route/ImageAnalytics.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/ImageAnalytics.kt similarity index 89% rename from ktor/src/main/kotlin/ink/snowflake/server/route/ImageAnalytics.kt rename to ktor/src/main/kotlin/ink/snowflake/server/controller/ImageAnalytics.kt index 271ecd8..f52198a 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/route/ImageAnalytics.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/ImageAnalytics.kt @@ -1,14 +1,13 @@ -package ink.snowflake.server.route +package ink.snowflake.server.controller import ink.snowflake.server.model.request.ImageAnalyticsRequest import ink.snowflake.server.model.response.* -import ink.snowflake.server.database.ImageDao +import ink.snowflake.server.utils.dao.ImageDao import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.routing.* import io.ktor.server.request.* import io.ktor.server.response.* -import kotlin.text.isNullOrEmpty fun Application.ImageAnalytics() { routing { diff --git a/ktor/src/main/kotlin/ink/snowflake/server/route/RemoteDebug.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/RemoteDebug.kt similarity index 92% rename from ktor/src/main/kotlin/ink/snowflake/server/route/RemoteDebug.kt rename to ktor/src/main/kotlin/ink/snowflake/server/controller/RemoteDebug.kt index b1c175f..a1f1112 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/route/RemoteDebug.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/RemoteDebug.kt @@ -1,8 +1,8 @@ -package ink.snowflake.server.route +package ink.snowflake.server.controller import com.google.gson.Gson import ink.snowflake.server.SERVER_PATH_FRP -import ink.snowflake.server.model.request.DevicesInfo +import ink.snowflake.server.model.request.DevicesInfoRequest import ink.snowflake.server.model.response.* import ink.snowflake.server.utils.runCommand import io.ktor.client.HttpClient @@ -79,13 +79,13 @@ fun Application.RemoteDebug() { val name = call.parameters["name"] val response: HttpResponse = client.get("http://171.212.101.201:65534/api/proxy/tcp") val responseBody: String = response.bodyAsText() - val devicesInfo: DevicesInfo = Gson().fromJson(responseBody, DevicesInfo::class.java) - val onlineDevices = devicesInfo.proxies.stream() + val devicesInfoRequest: DevicesInfoRequest = Gson().fromJson(responseBody, DevicesInfoRequest::class.java) + val onlineDevices = devicesInfoRequest.proxies.stream() .filter { it.status == "online" && it.conf != null && it.conf.remotePort >= 10000 && it.conf.remotePort <= 20000 } - val devices: MutableList = mutableListOf() + val devices: MutableList = mutableListOf() for (data in onlineDevices) { if (name == "null" || name == null || data.name.contains(name) && data.conf != null) { - devices.add(DeviceItem(data.name, data.conf!!.remotePort)) + devices.add(DeviceItemResponse(data.name, data.conf!!.remotePort)) } } call.respond(BaseResponse(data = devices)) diff --git a/ktor/src/main/kotlin/ink/snowflake/server/route/User.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/User.kt similarity index 64% rename from ktor/src/main/kotlin/ink/snowflake/server/route/User.kt rename to ktor/src/main/kotlin/ink/snowflake/server/controller/User.kt index cbc7dd5..d54eb4e 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/route/User.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/User.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.route +package ink.snowflake.server.controller import com.auth0.jwt.JWT import com.auth0.jwt.algorithms.Algorithm @@ -8,7 +8,8 @@ import ink.snowflake.server.model.request.RefreshTokenRequest import ink.snowflake.server.model.request.RegisterRequest import ink.snowflake.server.model.response.* import ink.snowflake.server.utils.AppConfig -import ink.snowflake.server.database.UserDAO +import ink.snowflake.server.utils.dao.AIDao +import ink.snowflake.server.utils.dao.UserDAO import ink.snowflake.server.utils.getUserIdByToken import io.ktor.server.application.* import io.ktor.server.auth.* @@ -49,31 +50,26 @@ fun Application.User(config: AppConfig) { val password = loginRequest.password // 查找用户 val userId = UserDAO.getUserIdByEmail(email) - val res = if(userId == null){ - // 账号不存在 - -1 - }else{ - val userPassword = UserDAO.getPasswordById(userId) - // 验证密码 - if (password == userPassword) { - // 登录成功 - userId - } else { - // 账号密码不匹配 - -2 - } - } call.respond( - when (res) { - -1 -> BaseResponse(status = false, message = "尚未注册", data = null) - -2 -> BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null) - else -> BaseResponse( - status = true, data = LoginResponse( - userId.toString(), - generateAccessToken(config, res), - generateRefreshToken(config, res) + if (userId == null) { + // 账号不存在 + BaseResponse(status = false, message = "尚未注册", data = null) + } else { + val userPassword = UserDAO.getPasswordById(userId) + // 验证密码 + if (password == userPassword) { + // 登录成功 + BaseResponse ( + status = true, data = LoginResponse( + userId, + generateAccessToken(config, userId), + generateRefreshToken(config, userId) + ) ) - ) + } else { + // 账号密码不匹配 + BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null) + } } ) } @@ -98,77 +94,76 @@ fun Application.User(config: AppConfig) { // 从 Redis 获取保存的验证码 val bucket: RBucket = redisClient.getBucket("verificationCode:$account") val storedCode = bucket.get() - if (storedCode == null) { - call.respond(BaseResponse(status = false, message = "验证码过期", data = null)) - } else if (storedCode != register.code) { - call.respond(BaseResponse(status = false, message = "验证码错误", data = null)) - } else { - // 查找用户 - val userId = UserDAO.getUserIdByEmail(register.account) - val res = if(userId != null){ - // 如果用户已存在,返回 0 - 0 - }else{ - // 用户不存在,插入新用户 - UserDAO.registerByEmailAndGetId(register.account,register.password) - } - if (res > 0) { - call.respond( - BaseResponse( - status = true, message = "注册成功", data = - BaseResponse( - status = true, data = LoginResponse( - account, - generateAccessToken(config, res), - generateRefreshToken(config, res) - ) - ) - ) - ) + call.respond( + if (storedCode == null) { + BaseResponse(status = false, message = "验证码过期,请重新获取", data = null) + } else if (storedCode != register.code) { + BaseResponse(status = false, message = "验证码错误,请检查或重新获取", data = null) } else { - call.respond(BaseResponse(status = false, message = "账户已存在", data = null)) + // 查找用户 + val userId = UserDAO.getUserIdByEmail(register.account) + if (userId == null) { + // 用户不存在,插入新用户 + val id = UserDAO.registerByEmailAndGetId(register.account, register.password) + if (id == null) { + BaseResponse(status = true, message = "注册失败,请联系管理员", data = null) + } else { + BaseResponse( + status = true, message = "注册成功", data = LoginResponse( + id, + generateAccessToken(config, id), + generateRefreshToken(config, id) + ) + ) + } + } else { + // 如果用户已存在,返回 0 + BaseResponse(status = false, message = "账户已存在", data = null) + } } - } + ) } post("/refreshToken") { - try { - val token = call.receive() - val verifier = JWT.require(Algorithm.HMAC256(config.jwtSecret)) - .withAudience(config.jwtAudience) - .withIssuer(config.jwtDomain) - .build() - val decodedJWT = verifier.verify(token.refreshToken) - val tokenType = decodedJWT.getClaim("token_type").asString() - if (tokenType != "refresh_token") { - call.respond( + val token = call.receive() + val verifier = JWT.require(Algorithm.HMAC256(config.jwtSecret)) + .withAudience(config.jwtAudience) + .withIssuer(config.jwtDomain) + .build() + val decodedJWT = verifier.verify(token.refreshToken) + val tokenType = decodedJWT.getClaim("token_type").asString() + call.respond( + try { + if (tokenType != "refresh_token") { BaseResponse( status = false, message = "拿什么乱七八糟的东西跟我换Access Token呢,???", data = null ) - ) - } - val userId = decodedJWT.getClaim("user_id").asInt() - // 生成新的access token和refresh token - call.respond( + } + val userId = UUID.fromString(decodedJWT.getClaim("user_id").asString()) + // 生成新的access token和refresh token BaseResponse( status = true, data = RefreshTokenResponse( generateAccessToken(config, userId), generateRefreshToken(config, userId) ) ) - ) - call.respond(BaseResponse(data = generateAccessToken(config, userId))) - } catch (ex: Exception) { - call.respond(BaseResponse(status = false, message = "token解析错误", data = null)) - } + BaseResponse(data = generateAccessToken(config, userId)) + } catch (ex: Exception) { + BaseResponse(status = false, message = "token解析错误", data = null) + } + ) } authenticate { get("/getAiList") { -// val allAIProfile = AIDao.getAllAIProfiles().stream().map { -// AiListForUser(aiId = it.id, aiName = it.name, aiAvatar = it.avatarUrl ?: "") -// }.toList() -// call.respond(BaseResponse(data = allAIProfile)) + val allAIProfile = AIDao.getAllAIProfiles().map { + AiListForUserResponse( + aiId = it.id, + aiName = it.name, + aiAvatar = it.avatarUrl ?: "" + ) + } + call.respond(BaseResponse(data = allAIProfile)) } get("/getUserInfo") { val userId = getUserIdByToken(call) @@ -190,22 +185,22 @@ fun Application.User(config: AppConfig) { } } -fun generateAccessToken(config: AppConfig, userId: Int): Token { +fun generateAccessToken(config: AppConfig, userId: UUID): Token { // return generateToken(config, userId, 120, "access_token") return generateToken(config, userId, 2 * 60 * 60, "access_token") } -fun generateRefreshToken(config: AppConfig, userId: Int): Token { +fun generateRefreshToken(config: AppConfig, userId: UUID): Token { return generateToken(config, userId, 10 * 24 * 60 * 60, "refresh_token") } -fun generateToken(config: AppConfig, userId: Int, second: Int, tokenType: String): Token { +fun generateToken(config: AppConfig, userId: UUID, second: Int, tokenType: String): Token { val expiresAt = Date(System.currentTimeMillis() + second * 1000) // val token = JWT.create() .withAudience(config.jwtAudience) .withIssuer(config.jwtDomain) - .withClaim("user_id", userId) + .withClaim("user_id", userId.toString()) .withClaim("token_type", tokenType) .withExpiresAt(expiresAt) .sign(Algorithm.HMAC256(config.jwtSecret)) @@ -238,13 +233,13 @@ fun sendVerificationEmail(config: AppConfig, recipientEmail: String, verificatio val message = MimeMessage(session).apply { setFrom(InternetAddress(config.smtpUser)) setRecipient(Message.RecipientType.TO, InternetAddress(recipientEmail)) - subject = "Welcome to CyberEcho! Verification Code:$verificationCode" + subject = "Welcome to BBIT-Lab! Verification Code:$verificationCode" val htmlContent = """
-

Welcome to CyberEcho!

-

Thank you for signing up with CyberEcho! We are excited to have you on board.

+

Welcome to BBIT-Lab!

+

Thank you for signing up with BBIT-Lab! We are excited to have you on board.

To complete your registration, please use the verification code below:

@@ -258,7 +253,7 @@ fun sendVerificationEmail(config: AppConfig, recipientEmail: String, verificatio

Best regards
- The CyberEcho Account Team + The BBIT-Lab Account Team

diff --git a/ktor/src/main/kotlin/ink/snowflake/server/route/VideoAnalytics.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalytics.kt similarity index 89% rename from ktor/src/main/kotlin/ink/snowflake/server/route/VideoAnalytics.kt rename to ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalytics.kt index 01713d9..0478d54 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/route/VideoAnalytics.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalytics.kt @@ -1,15 +1,15 @@ -package ink.snowflake.server.route +package ink.snowflake.server.controller import ink.snowflake.server.SERVER_PATH_OSS import ink.snowflake.server.VIDEO_INPUT_PATH -import ink.snowflake.server.database.VideoDao -import ink.snowflake.server.database.table.VideoTable -import ink.snowflake.server.database.table.VideoTable.vAAverageMaskedRatio -import ink.snowflake.server.database.table.VideoTable.vACountPeople -import ink.snowflake.server.database.table.VideoTable.vAMaxAction -import ink.snowflake.server.database.table.VideoTable.vAMaxStayTime -import ink.snowflake.server.database.table.VideoTable.vATotalPeople -import ink.snowflake.server.database.table.VideoTable.vStartDateTime +import ink.snowflake.server.utils.dao.VideoDao +import ink.snowflake.server.model.database.VideosTable +import ink.snowflake.server.model.database.VideosTable.vAAverageMaskedRatio +import ink.snowflake.server.model.database.VideosTable.vACountPeople +import ink.snowflake.server.model.database.VideosTable.vAMaxAction +import ink.snowflake.server.model.database.VideosTable.vAMaxStayTime +import ink.snowflake.server.model.database.VideosTable.vATotalPeople +import ink.snowflake.server.model.database.VideosTable.vStartDateTime import ink.snowflake.server.model.request.VideoAnalyticsRequest import ink.snowflake.server.model.response.* import ink.snowflake.server.utils.WebSocketManager.broadcastMessage @@ -23,8 +23,6 @@ import io.ktor.server.routing.* import io.ktor.server.websocket.* import io.ktor.websocket.* import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.channels.consumeEach -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.datetime.LocalDateTime import kotlinx.datetime.toJavaLocalDateTime @@ -114,12 +112,12 @@ fun Application.VideoAnalytics() { get("/getAnalyticsDetailByVideoId") { // 获取 vId 参数 val vIdParam = call.parameters["vId"] - val vId = vIdParam?.toIntOrNull() // 将 vId 转换为 Int,确保安全 - if (vId == null) { - call.respond(BaseResponse(status = true, message = "Invalid vId", data = null)) + if (vIdParam == null) { + call.respond(BaseResponse(status = true, message = "缺少参数vId", data = null)) return@get } // 查询视频信息 + val vId = UUID.fromString(vIdParam) val video = VideoDao.getAnalyticsDetailByVideoId(vId) if (video == null) { call.respond(BaseResponse(status = false, message = "不存在该视频", data = null)) @@ -216,17 +214,17 @@ fun Application.VideoAnalytics() { BaseResponse( data = VideoAnalyticsDetail( v_id = vId, - v_name = video[VideoTable.vName], - v_video_play_path = "http://${SERVER_PATH_OSS}:9000/video/" + video[VideoTable.vObjectName], - v_file_name = video[VideoTable.vFileName], - v_duration = video[VideoTable.vDuration], - v_size = video[VideoTable.vSize], + v_name = video[VideosTable.vName], + v_video_play_path = "http://${SERVER_PATH_OSS}:9000/video/" + video[VideosTable.vObjectName], + v_file_name = video[VideosTable.vFileName], + v_duration = video[VideosTable.vDuration], + v_size = video[VideosTable.vSize], v_start_datetime = video[vStartDateTime]?.toString() ?: "", - v_video_codec = video[VideoTable.vVideoCodec], - v_audio_codec = video[VideoTable.vAudioCodec], - v_overall_bit_rate = video[VideoTable.vOverallBitRate], - v_resolution = video[VideoTable.vResolution], - v_a_time = video[VideoTable.vATime].toString(), + v_video_codec = video[VideosTable.vVideoCodec], + v_audio_codec = video[VideosTable.vAudioCodec], + v_overall_bit_rate = video[VideosTable.vOverallBitRate], + v_resolution = video[VideosTable.vResolution], + v_a_time = video[VideosTable.vATime].toString(), v_a_total_people = video[vATotalPeople], // 总人数 v_a_count_people = video[vACountPeople], // 佩戴口罩人数 v_a_max_stay_time = video[vAMaxStayTime], // 最大停留时间 diff --git a/ktor/src/main/kotlin/ink/snowflake/server/route/VideoAnalyticsJetson.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalyticsJetson.kt similarity index 99% rename from ktor/src/main/kotlin/ink/snowflake/server/route/VideoAnalyticsJetson.kt rename to ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalyticsJetson.kt index 820f8d4..df87cc9 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/route/VideoAnalyticsJetson.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalyticsJetson.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.route +package ink.snowflake.server.controller import ink.snowflake.server.model.response.* import io.ktor.server.application.* diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/ChatRecordsDao.kt b/ktor/src/main/kotlin/ink/snowflake/server/database/ChatRecordsDao.kt deleted file mode 100644 index 2331265..0000000 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/ChatRecordsDao.kt +++ /dev/null @@ -1,51 +0,0 @@ -package ink.snowflake.server.database - -import ink.snowflake.server.database.table.ChatRecord -import ink.snowflake.server.database.table.ChatRecordsTable -import ink.snowflake.server.database.table.toChatRecord -import kotlinx.datetime.Clock -import org.jetbrains.exposed.v1.core.SortOrder -import org.jetbrains.exposed.v1.datetime.timestampLiteral -import org.jetbrains.exposed.v1.jdbc.insertAndGetId -import org.jetbrains.exposed.v1.jdbc.selectAll -import org.jetbrains.exposed.v1.jdbc.transactions.transaction - -object ChatRecordsDao { - /** - * 插入聊天记录 - */ - fun insertChatRecord( - userId: Int, - message: String, - messageType: String, - sendAt: Long, - from: String, - emotion: String? - ): Boolean { - return transaction { - ChatRecordsTable - .insertAndGetId { - it[ChatRecordsTable.userId] = userId - it[ChatRecordsTable.message] = message - it[ChatRecordsTable.messageType] = messageType - it[ChatRecordsTable.sentAt] = timestampLiteral(Clock.System.now()) - it[ChatRecordsTable.from] = from - it[ChatRecordsTable.emotion] = emotion - }.value > 0 - } - } - - /** - * 取最近五条聊天记录 - */ - fun getRecentChatRecords(userId: Int): List { - return transaction { - ChatRecordsTable - .selectAll() - .where { ChatRecordsTable.userId eq userId } - .orderBy(ChatRecordsTable.sentAt to SortOrder.DESC) - .limit(5) - .toList().map { it.toChatRecord() } - } - } -} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/UserDAO.kt b/ktor/src/main/kotlin/ink/snowflake/server/database/UserDAO.kt deleted file mode 100644 index aef03f2..0000000 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/UserDAO.kt +++ /dev/null @@ -1,89 +0,0 @@ -package ink.snowflake.server.database - -import ink.snowflake.server.database.table.UserTable -import ink.snowflake.server.model.response.UserInfo -import kotlinx.datetime.Clock -import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.eq -import org.jetbrains.exposed.v1.datetime.timestampLiteral -import org.jetbrains.exposed.v1.jdbc.insertAndGetId -import org.jetbrains.exposed.v1.jdbc.selectAll -import org.jetbrains.exposed.v1.jdbc.transactions.transaction - - -object UserDAO { - /** - * 根据 email 获取密码 - * @return 密码的 SHA-256 哈希值 - */ - fun getPasswordById(id: Int): String? { - return transaction { - // 查询指定 email 的用户并返回 passwordHash - UserTable - .selectAll().where { UserTable.id eq id } - .map { it[UserTable.passwordHash] } - .singleOrNull() // 如果没有找到用户,返回 null - } - } - - /** - * 根据 email 获取用户 ID - * @return 用户的 ID - */ - fun getUserIdByEmail(email: String): Int? { - return transaction { - // 查询指定 email 的用户并返回 id - UserTable - .selectAll().where { UserTable.email eq email } - .map { it[UserTable.id].value } - .singleOrNull() // 如果没有找到用户,返回 null - } - } - - /** - * 添加用户 - * @return 如果添加成功,返回 1;如果用户已存在,返回 0;如果添加失败,返回 -1 - */ - fun registerByEmailAndGetId(account: String, hashPassword: String): Int { - return transaction { - try { - val newUserId = UserTable - .insertAndGetId { - it[email] = account - it[passwordHash] = hashPassword - it[createdAt] = timestampLiteral(Clock.System.now()) - it[updatedAt] = timestampLiteral(Clock.System.now()) - } - // 如果插入成功,返回 userId - if (newUserId.value > 0) { - newUserId.value - } else { - // 插入失败,返回 -1 - -1 - } - } catch (e: Exception) { - // 如果发生异常(例如数据库连接问题),返回 -1 - e.printStackTrace() // 打印异常堆栈 - -1 - } - } - } - - fun getUserInfoByUserId(userId: Int?): UserInfo? { - if (userId == null) return null - - return transaction { - UserTable - .selectAll().where { UserTable.id eq userId } - .map { - UserInfo( - username = it[UserTable.username], - email = it[UserTable.email], - phone = it[UserTable.phone], - roles = it[UserTable.roles]?.removePrefix("{")!!.removeSuffix("}").split(","), - ) - } - .singleOrNull() - } - } - -} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/table/VideoAnalyticsDetailTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/database/table/VideoAnalyticsDetailTable.kt deleted file mode 100644 index 7cccf16..0000000 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/table/VideoAnalyticsDetailTable.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ink.snowflake.server.database.table - -import org.jetbrains.exposed.v1.core.dao.id.IntIdTable - - -object VideoAnalyticsDetailTable : IntIdTable("video_analytics_detail") { - val vId = integer("v_id").references(VideoTable.id) - val aTimeStamp = integer("a_time_stamp") - val aTotalPeople = integer("a_total_people") - val aTotalPeopleMasked = integer("a_total_people_masked") - val aAction = varchar("a_action", 50) -} diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/table/VideoTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/database/table/VideoTable.kt deleted file mode 100644 index 25f669a..0000000 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/table/VideoTable.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ink.snowflake.server.database.table - -import org.jetbrains.exposed.v1.core.dao.id.IntIdTable -import org.jetbrains.exposed.v1.datetime.datetime - - -object VideoTable : IntIdTable("video") { - val vName = varchar("v_name", 255) - val vObjectName = varchar("v_object_name", 255) - val vFileName = varchar("v_file_name", 255) - val vStartDateTime = datetime("v_start_datetime").nullable() - val vDuration = float("v_duration") - val vSize = float("v_size") - val vVideoCodec = varchar("v_video_codec", 50) - val vAudioCodec = varchar("v_audio_codec", 50) - val vOverallBitRate = integer("v_overall_bit_rate") - val vResolution = varchar("v_resolution", 20) - val vATime = datetime("v_a_time") - val vATotalPeople = integer("v_a_total_people") - val vACountPeople = integer("v_a_count_people") - val vAMaxStayTime = float("v_a_max_stay_time") - val vAMaxAction = varchar("v_a_max_action", 50) - val vAAverageMaskedRatio = float("v_a_average_masked_ratio") -} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/table/AIProfilesTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/database/AIProfilesTable.kt similarity index 86% rename from ktor/src/main/kotlin/ink/snowflake/server/database/table/AIProfilesTable.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/database/AIProfilesTable.kt index bdf4a6f..837ffc7 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/table/AIProfilesTable.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/database/AIProfilesTable.kt @@ -1,15 +1,18 @@ -package ink.snowflake.server.database.table +package ink.snowflake.server.model.database import kotlinx.serialization.Serializable import com.google.gson.reflect.TypeToken import ink.snowflake.server.gson +import kotlinx.serialization.Contextual import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.UUIDTable import org.jetbrains.exposed.v1.datetime.timestamp import org.jetbrains.exposed.v1.json.json +import java.util.UUID // 定义 AI 表 -object AIProfilesTable : IntIdTable("ai_profiles") { +object AIProfilesTable : UUIDTable("ai_profiles") { val name = varchar("name", 100) // AI 名称 val avatarUrl = varchar("avatar_url", 255).nullable() // AI 头像 URL val aiPersonality = json("ai_personality", serialize = { gson.toJson(it) }, deserialize = { @@ -22,7 +25,8 @@ object AIProfilesTable : IntIdTable("ai_profiles") { @Serializable data class AIProfile( - val id: Int, + @Contextual + val id: UUID, val name: String, val avatarUrl: String?, val aiPersonality: Map, diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/table/ChatRecordsTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/database/ChatRecordsTable.kt similarity index 79% rename from ktor/src/main/kotlin/ink/snowflake/server/database/table/ChatRecordsTable.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/database/ChatRecordsTable.kt index ad4d7cc..d0bc490 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/table/ChatRecordsTable.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/database/ChatRecordsTable.kt @@ -1,13 +1,15 @@ -package ink.snowflake.server.database.table +package ink.snowflake.server.model.database import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.v1.core.ResultRow -import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.UUIDTable import org.jetbrains.exposed.v1.datetime.timestamp +import java.util.UUID +import kotlin.uuid.Uuid -object ChatRecordsTable : IntIdTable("chat_records") { +object ChatRecordsTable : UUIDTable("chat_records") { // 用户 ID(外键) - val userId: Column = integer("user_id").references(UserTable.id) + val userId: Column = uuid("user_id").references(UsersTable.id) // 消息内容 val message: Column = text("message") @@ -26,8 +28,8 @@ object ChatRecordsTable : IntIdTable("chat_records") { } data class ChatRecord( - val id: Int, - val userId: Int, + val id: UUID, + val userId: UUID, val message: String, val messageType: String, val sentAt: String,// eg. "2024-12-10T06:19:04.433006Z" diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/table/ImageTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/database/ScaImagesTable.kt similarity index 85% rename from ktor/src/main/kotlin/ink/snowflake/server/database/table/ImageTable.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/database/ScaImagesTable.kt index 422490c..c0b4da0 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/table/ImageTable.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/database/ScaImagesTable.kt @@ -1,12 +1,12 @@ -package ink.snowflake.server.database.table +package ink.snowflake.server.model.database import com.google.gson.reflect.TypeToken import ink.snowflake.server.gson -import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.UUIDTable import org.jetbrains.exposed.v1.datetime.datetime import org.jetbrains.exposed.v1.json.json -object ImageTable : IntIdTable("image_sca") { +object ScaImagesTable : UUIDTable("sca_images") { val name = varchar("name", 255) val upload_datetime = datetime("upload_datetime") val file_name = varchar("file_name", 255) diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/table/UserTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/database/UsersTable.kt similarity index 82% rename from ktor/src/main/kotlin/ink/snowflake/server/database/table/UserTable.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/database/UsersTable.kt index 7c70f61..853c108 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/table/UserTable.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/database/UsersTable.kt @@ -1,9 +1,9 @@ -package ink.snowflake.server.database.table +package ink.snowflake.server.model.database -import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.UUIDTable import org.jetbrains.exposed.v1.datetime.timestamp -object UserTable : IntIdTable("users") { +object UsersTable : UUIDTable("users") { // 用户名 val username = varchar("username", length = 50).nullable() diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/database/VideoAnalyticsDetailsTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/database/VideoAnalyticsDetailsTable.kt new file mode 100644 index 0000000..500348f --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/database/VideoAnalyticsDetailsTable.kt @@ -0,0 +1,13 @@ +package ink.snowflake.server.model.database + +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.UUIDTable + + +object VideoAnalyticsDetailsTable : UUIDTable("video_analytics_details") { + val vId = uuid("v_id").references(VideosTable.id) + val aTimeStamp = integer("time_stamp") + val aTotalPeople = integer("total_people") + val aTotalPeopleMasked = integer("total_people_masked") + val aAction = varchar("action", 50) +} diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/database/VideosTable.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/database/VideosTable.kt new file mode 100644 index 0000000..cfded9b --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/database/VideosTable.kt @@ -0,0 +1,25 @@ +package ink.snowflake.server.model.database + +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.UUIDTable +import org.jetbrains.exposed.v1.datetime.datetime + + +object VideosTable : UUIDTable("videos") { + val vName = varchar("name", 255) + val vObjectName = varchar("object_name", 255) + val vFileName = varchar("file_name", 255) + val vStartDateTime = datetime("start_datetime").nullable() + val vDuration = float("duration") + val vSize = float("size") + val vVideoCodec = varchar("video_codec", 50) + val vAudioCodec = varchar("audio_codec", 50) + val vOverallBitRate = integer("overall_bit_rate") + val vResolution = varchar("resolution", 20) + val vATime = datetime("a_time") + val vATotalPeople = integer("a_total_people") + val vACountPeople = integer("a_count_people") + val vAMaxStayTime = float("a_max_stay_time") + val vAMaxAction = varchar("a_max_action", 50) + val vAAverageMaskedRatio = float("a_average_masked_ratio") +} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/request/DevicesInfo.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/request/DevicesInfoRequest.kt similarity index 98% rename from ktor/src/main/kotlin/ink/snowflake/server/model/request/DevicesInfo.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/request/DevicesInfoRequest.kt index 350db49..1458666 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/request/DevicesInfo.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/request/DevicesInfoRequest.kt @@ -2,7 +2,7 @@ package ink.snowflake.server.model.request import com.google.gson.annotations.SerializedName -data class DevicesInfo( +data class DevicesInfoRequest( @SerializedName("proxies") val proxies: List = listOf() ) { diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/request/ImageAnalyticsRequest.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/request/ImageAnalyticsRequest.kt index 776a2c6..5e2dfc0 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/request/ImageAnalyticsRequest.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/request/ImageAnalyticsRequest.kt @@ -1,10 +1,13 @@ package ink.snowflake.server.model.request +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.util.UUID @Serializable data class ImageAnalyticsRequest( - val id : Int, + @Contextual + val id : UUID, val name : String, val upload_datetime: String, // 上传时间 val file_name: String, // 文件名 diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/request/RefreshTokenRequest.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/request/RefreshTokenRequest.kt index 578ec0e..0ffbc14 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/request/RefreshTokenRequest.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/request/RefreshTokenRequest.kt @@ -1,6 +1,5 @@ package ink.snowflake.server.model.request -import ink.snowflake.server.model.response.Token import kotlinx.serialization.Serializable @Serializable diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/AiListForUser.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/AiListForUserResponse.kt similarity index 53% rename from ktor/src/main/kotlin/ink/snowflake/server/model/response/AiListForUser.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/response/AiListForUserResponse.kt index fff9fbe..c92325d 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/AiListForUser.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/AiListForUserResponse.kt @@ -1,10 +1,13 @@ package ink.snowflake.server.model.response +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.util.UUID @Serializable -data class AiListForUser( - val aiId :Int, +data class AiListForUserResponse( + @Contextual + val aiId: UUID, val aiName :String, val aiAvatar : String ) \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/DeviceItem.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/DeviceItemResponse.kt similarity index 82% rename from ktor/src/main/kotlin/ink/snowflake/server/model/response/DeviceItem.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/response/DeviceItemResponse.kt index b4485e5..0fd091e 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/DeviceItem.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/DeviceItemResponse.kt @@ -3,7 +3,7 @@ package ink.snowflake.server.model.response import kotlinx.serialization.Serializable @Serializable -data class DeviceItem ( +data class DeviceItemResponse ( val deviceName:String, val devicePort:Int, ) diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/LoginResponse.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/LoginResponse.kt index 10971fe..ea30aae 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/LoginResponse.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/LoginResponse.kt @@ -1,8 +1,12 @@ package ink.snowflake.server.model.response +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.util.UUID @Serializable -data class LoginResponse(val userId: String, val accessToken: Token, val refreshToken: Token) +data class LoginResponse( + @Contextual + val userId: UUID, val accessToken: Token, val refreshToken: Token) diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/Token.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/Token.kt index d4ced83..0aeaf73 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/Token.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/Token.kt @@ -3,4 +3,4 @@ package ink.snowflake.server.model.response import kotlinx.serialization.Serializable @Serializable -data class Token(val token: String, val expiresAt: Long = 0,val expiresStr:String) \ No newline at end of file +data class Token(val token: String, val expiresAt: Long = 0, val expiresStr:String) \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/UserInfo.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/UserInfoResponse.kt similarity index 78% rename from ktor/src/main/kotlin/ink/snowflake/server/model/response/UserInfo.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/response/UserInfoResponse.kt index 6f6c05b..e3531ae 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/UserInfo.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/UserInfoResponse.kt @@ -1,10 +1,9 @@ package ink.snowflake.server.model.response -import kotlinx.datetime.LocalDateTime import kotlinx.serialization.Serializable @Serializable -data class UserInfo( +data class UserInfoResponse( val username: String?, val email: String?, val phone: String?, diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoAnalyticsDetail.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoAnalyticsDetailReponse.kt similarity index 92% rename from ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoAnalyticsDetail.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoAnalyticsDetailReponse.kt index 593b388..0a21b5b 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoAnalyticsDetail.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoAnalyticsDetailReponse.kt @@ -1,10 +1,13 @@ package ink.snowflake.server.model.response +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.util.UUID @Serializable data class VideoAnalyticsDetail( - val v_id: Int, + @Contextual + val v_id: UUID, val v_name: String, val v_video_play_path: String, val v_file_name: String, diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoListResponse.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoListResponse.kt index 49bade8..d5cfc63 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoListResponse.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/VideoListResponse.kt @@ -1,10 +1,13 @@ package ink.snowflake.server.model.response +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.util.UUID @Serializable data class VideoListResponse( - val v_id: Int, + @Contextual + val v_id: UUID, val v_name: String, val v_a_time: String? ) \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/model/response/WSChatMessage.kt b/ktor/src/main/kotlin/ink/snowflake/server/model/response/WSChatMessageReponse.kt similarity index 86% rename from ktor/src/main/kotlin/ink/snowflake/server/model/response/WSChatMessage.kt rename to ktor/src/main/kotlin/ink/snowflake/server/model/response/WSChatMessageReponse.kt index cb7893c..3af8061 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/model/response/WSChatMessage.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/model/response/WSChatMessageReponse.kt @@ -1,9 +1,11 @@ package com.cyberecho.mdoel.database +import java.util.UUID + data class WSChatRecords( val id: Long = 0, // 消息的唯一标识符,自增 - val aiId: Int, // 交流的对象AI_ID + val aiId: UUID, // 交流的对象AI_ID val messageType: String,// 消息类型(文本、图片、语音等) val content: String, // 消息内容 val timestamp: Long, // 发送时间(时间戳,单位为毫秒) diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Serialization.kt b/ktor/src/main/kotlin/ink/snowflake/server/plugins/Serialization.kt deleted file mode 100644 index 54a1345..0000000 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Serialization.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ink.snowflake.server.plugins - -import io.ktor.http.* -import io.ktor.serialization.* -import io.ktor.serialization.gson.* -import io.ktor.serialization.kotlinx.json.* -import io.ktor.server.application.* -import io.ktor.server.plugins.contentnegotiation.* -import io.ktor.server.request.* -import io.ktor.server.response.* -import io.ktor.server.routing.* - -fun Application.configureSerialization() { - install(ContentNegotiation) { - json() - gson{ - setPrettyPrinting() - } - } -} diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/MyUtils.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/MyUtils.kt index f3f8dcc..cf08949 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/utils/MyUtils.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/MyUtils.kt @@ -1,6 +1,5 @@ package ink.snowflake.server.utils -import ink.snowflake.server.model.response.UserInfo import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.auth.jwt.* @@ -8,9 +7,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.format import kotlinx.datetime.toJavaLocalDateTime -import org.jetbrains.exposed.v1.jdbc.transactions.experimental.newSuspendedTransaction import java.io.BufferedReader import java.io.InputStreamReader import java.text.SimpleDateFormat @@ -18,10 +15,9 @@ import java.time.format.DateTimeFormatter import java.util.* -fun getUserIdByToken(call: ApplicationCall) : Int?{ +fun getUserIdByToken(call: ApplicationCall) : UUID?{ // 通过token获取user_id - return call.principal()?.payload?.getClaim("user_id")?.asInt() - + return UUID.fromString(call.principal()?.payload?.getClaim("user_id")?.asString()) } fun formatDateToTargetString(date: Date, targetFormat: String): String { diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/WebSocketManager.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/WebSocketManager.kt index 78f9bd7..08d563b 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/utils/WebSocketManager.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/WebSocketManager.kt @@ -7,6 +7,7 @@ import io.ktor.websocket.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.util.UUID import java.util.concurrent.ConcurrentHashMap object WebSocketManager { @@ -14,17 +15,17 @@ object WebSocketManager { val scope = CoroutineScope(Dispatchers.IO) // 存储所有 WebSocket 连接的 Map,key 为 user_id,value 为 WebSocketSession - private val connections = ConcurrentHashMap() + private val connections = ConcurrentHashMap() // 添加 WebSocket 连接 - fun addConnection(userId: Int, session: WebSocketSession) { + fun addConnection(userId: UUID, session: WebSocketSession) { connections[userId] = session println("新连接:$userId") } // 移除 WebSocket 连接 - fun removeConnection(userId: Int) { + fun removeConnection(userId: UUID) { connections.remove(userId) println("断开连接:$userId") } @@ -39,7 +40,7 @@ object WebSocketManager { } // 向特定用户发送消息 - suspend fun sendMessageToUser(userId: Int, message: WSChatRecords) { + suspend fun sendMessageToUser(userId: UUID, message: WSChatRecords) { connections[userId]?.send(gson.toJson(message)) } diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/AIDao.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/AIDao.kt similarity index 61% rename from ktor/src/main/kotlin/ink/snowflake/server/database/AIDao.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/dao/AIDao.kt index b13432b..7cddb55 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/AIDao.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/AIDao.kt @@ -1,8 +1,8 @@ -package ink.snowflake.server.database +package ink.snowflake.server.utils.dao -import ink.snowflake.server.database.table.AIProfile -import ink.snowflake.server.database.table.AIProfilesTable -import ink.snowflake.server.database.table.toAIProfile +import ink.snowflake.server.model.database.AIProfile +import ink.snowflake.server.model.database.AIProfilesTable +import ink.snowflake.server.model.database.toAIProfile import org.jetbrains.exposed.v1.jdbc.selectAll import org.jetbrains.exposed.v1.jdbc.transactions.transaction diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ChatRecordsDao.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ChatRecordsDao.kt new file mode 100644 index 0000000..465f412 --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ChatRecordsDao.kt @@ -0,0 +1,61 @@ +package ink.snowflake.server.utils.dao + +import ink.snowflake.server.model.database.ChatRecord +import ink.snowflake.server.model.database.ChatRecordsTable +import ink.snowflake.server.model.database.toChatRecord +import kotlinx.datetime.Clock +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.datetime.timestampLiteral +import org.jetbrains.exposed.v1.exceptions.ExposedSQLException +import org.jetbrains.exposed.v1.jdbc.insert +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import java.util.UUID +import kotlin.uuid.Uuid + +object ChatRecordsDao { + /** + * 插入聊天记录 + */ + fun insertChatRecord( + userId: UUID, + message: String, + messageType: String, + sendAt: Long, + from: String, + emotion: String? + ): Boolean { + return transaction { + try { + ChatRecordsTable + .insert { + it[ChatRecordsTable.userId] = userId + it[ChatRecordsTable.message] = message + it[ChatRecordsTable.messageType] = messageType + it[ChatRecordsTable.sentAt] = timestampLiteral(Clock.System.now()) + it[ChatRecordsTable.from] = from + it[ChatRecordsTable.emotion] = emotion + } + true + } catch (e: ExposedSQLException) { + e.printStackTrace() + false + } + } + } + + /** + * 取最近五条聊天记录 + */ + fun getRecentChatRecords(userId: UUID): List { + return transaction { + ChatRecordsTable + .selectAll() + .where { ChatRecordsTable.userId eq userId } + .orderBy(ChatRecordsTable.sentAt to SortOrder.DESC) + .limit(5) + .toList().map { it.toChatRecord() } + } + } +} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/ImageDao.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ImageDao.kt similarity index 60% rename from ktor/src/main/kotlin/ink/snowflake/server/database/ImageDao.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ImageDao.kt index 210bce1..e9f8efc 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/ImageDao.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ImageDao.kt @@ -1,8 +1,8 @@ -package ink.snowflake.server.database +package ink.snowflake.server.utils.dao import ink.snowflake.server.SERVER_PATH_OSS import ink.snowflake.server.model.request.ImageAnalyticsRequest -import ink.snowflake.server.database.table.ImageTable +import ink.snowflake.server.model.database.ScaImagesTable import ink.snowflake.server.utils.formatLocalDateTimeToString import kotlinx.datetime.toKotlinLocalDateTime import org.jetbrains.exposed.v1.core.SortOrder @@ -15,7 +15,7 @@ object ImageDao { fun insertImageAnalyticsData(request: ImageAnalyticsRequest) { return transaction { - ImageTable.insert { + ScaImagesTable.insert { it[upload_datetime] = Timestamp.valueOf(request.upload_datetime) .toLocalDateTime().toKotlinLocalDateTime() it[file_name] = request.file_name @@ -34,25 +34,25 @@ object ImageDao { fun getImageList(name: String): List { return transaction { - ImageTable.selectAll() - .where { ImageTable.name like "%$name%" } - .orderBy(ImageTable.upload_datetime, SortOrder.DESC) + ScaImagesTable.selectAll() + .where { ScaImagesTable.name like "%$name%" } + .orderBy(ScaImagesTable.upload_datetime, SortOrder.DESC) .map { ImageAnalyticsRequest( - id = it[ImageTable.id].value, - name = it[ImageTable.name], - upload_datetime = formatLocalDateTimeToString(it[ImageTable.upload_datetime]), - file_name = it[ImageTable.file_name], - image_pre = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ImageTable.image_pre], - image_after = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ImageTable.image_after], - resolution = it[ImageTable.resolution], - size = it[ImageTable.size], - cocoon_count = it[ImageTable.cocoon_count], - max_confidence = it[ImageTable.max_confidence], - min_confidence = it[ImageTable.min_confidence], - average_confidence = it[ImageTable.average_confidence], - other_info = it[ImageTable.other_info] as Map, - processing_time = formatLocalDateTimeToString(it[ImageTable.processing_time]) + id = it[ScaImagesTable.id].value, + name = it[ScaImagesTable.name], + upload_datetime = formatLocalDateTimeToString(it[ScaImagesTable.upload_datetime]), + file_name = it[ScaImagesTable.file_name], + image_pre = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ScaImagesTable.image_pre], + image_after = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ScaImagesTable.image_after], + resolution = it[ScaImagesTable.resolution], + size = it[ScaImagesTable.size], + cocoon_count = it[ScaImagesTable.cocoon_count], + max_confidence = it[ScaImagesTable.max_confidence], + min_confidence = it[ScaImagesTable.min_confidence], + average_confidence = it[ScaImagesTable.average_confidence], + other_info = it[ScaImagesTable.other_info] as Map, + processing_time = formatLocalDateTimeToString(it[ScaImagesTable.processing_time]) ) } } diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/UserDAO.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/UserDAO.kt new file mode 100644 index 0000000..5376d18 --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/UserDAO.kt @@ -0,0 +1,90 @@ +package ink.snowflake.server.utils.dao + +import ink.snowflake.server.model.database.UsersTable +import ink.snowflake.server.model.response.UserInfoResponse +import kotlinx.datetime.Clock +import org.jetbrains.exposed.v1.datetime.timestampLiteral +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import java.util.UUID + + +object UserDAO { + /** + * 根据 email 获取密码 + * @return 密码的 SHA-256 哈希值 + */ + fun getPasswordById(id: UUID): String? { + return transaction { + // 查询指定 email 的用户并返回 passwordHash + UsersTable + .selectAll().where { UsersTable.id eq id } + .map { it[UsersTable.passwordHash] } + .singleOrNull() // 如果没有找到用户,返回 null + } + } + + /** + * 根据 email 获取用户 ID + * @return 用户的 ID + */ + fun getUserIdByEmail(email: String): UUID? { + return transaction { + // 查询指定 email 的用户并返回 id + UsersTable + .selectAll().where { UsersTable.email eq email } + .map { it[UsersTable.id].value } + .singleOrNull() // 如果没有找到用户,返回 null + } + } + + /** + * 添加用户 + * @return 如果添加成功,返回 1;如果用户已存在,返回 0;如果添加失败,返回 -1 + */ + fun registerByEmailAndGetId(account: String, hashPassword: String): UUID? { + return transaction { + try { + val newUserId = UsersTable + .insertAndGetId { + it[username] = account + it[email] = account + it[passwordHash] = hashPassword + it[createdAt] = timestampLiteral(Clock.System.now()) + it[updatedAt] = timestampLiteral(Clock.System.now()) + } + // 如果插入成功,返回 userId + newUserId.value + } catch (e: Exception) { + // 如果发生异常(例如数据库连接问题),返回 -1 + e.printStackTrace() + null + } + } + } + + fun getUserInfoByUserId(userId: UUID?): UserInfoResponse? { + if (userId == null) return null + return transaction { + try { + UsersTable + .selectAll().where { UsersTable.id eq userId } + .map { + UserInfoResponse( + username = it[UsersTable.username], + email = it[UsersTable.email], + phone = it[UsersTable.phone], + roles = it[UsersTable.roles]?.removePrefix("{")!!.removeSuffix("}").split(","), + ) + } + .singleOrNull() + } catch (e: Exception) { + // 如果发生异常(例如数据库连接问题),返回 -1 + e.printStackTrace() + null + } + } + } + +} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/database/VideoDao.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/VideoDao.kt similarity index 71% rename from ktor/src/main/kotlin/ink/snowflake/server/database/VideoDao.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/dao/VideoDao.kt index 1c43eda..a1c8716 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/database/VideoDao.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/VideoDao.kt @@ -1,9 +1,9 @@ -package ink.snowflake.server.database +package ink.snowflake.server.utils.dao -import ink.snowflake.server.database.table.VideoAnalyticsDetailTable -import ink.snowflake.server.database.table.VideoTable -import ink.snowflake.server.database.table.VideoTable.vATime -import ink.snowflake.server.database.table.VideoTable.vName +import ink.snowflake.server.model.database.VideoAnalyticsDetailsTable +import ink.snowflake.server.model.database.VideosTable +import ink.snowflake.server.model.database.VideosTable.vATime +import ink.snowflake.server.model.database.VideosTable.vName import ink.snowflake.server.model.request.VideoAnalyticsRequest import ink.snowflake.server.model.request.VideoDetail import ink.snowflake.server.model.response.VideoListResponse @@ -16,13 +16,14 @@ import org.jetbrains.exposed.v1.jdbc.insertAndGetId import org.jetbrains.exposed.v1.jdbc.selectAll import org.jetbrains.exposed.v1.jdbc.transactions.transaction import java.sql.Timestamp +import java.util.UUID object VideoDao { fun insertVideoAnalyticsData(request: VideoAnalyticsRequest) { return transaction { // 插入视频信息 - val videoId = VideoTable.insertAndGetId { + val videoId = VideosTable.insertAndGetId { it[vName] = request.v_name it[vObjectName] = request.v_object_name it[vFileName] = request.v_file_name @@ -43,7 +44,7 @@ object VideoDao { } // 插入视频分析详情 request.v_a_details.forEach { detail -> - VideoAnalyticsDetailTable.insert { + VideoAnalyticsDetailsTable.insert { it[vId] = videoId.value it[aTimeStamp] = detail.a_time_stamp it[aTotalPeople] = detail.a_total_people @@ -56,39 +57,39 @@ object VideoDao { fun getVideoList(name: String?): List { return transaction { - val query = VideoTable.selectAll() + val query = VideosTable.selectAll() // 动态加 where 条件 if (!name.isNullOrBlank()) { - query.where { VideoTable.vName like "%${name}%" } + query.where { vName like "%${name}%" } } - query.orderBy(VideoTable.vATime, SortOrder.DESC) + query.orderBy(vATime, SortOrder.DESC) .map { VideoListResponse( - v_id = it[VideoTable.id].value, - v_name = it[VideoTable.vName], - v_a_time = formatLocalDateTimeToString(it[VideoTable.vATime]) + v_id = it[VideosTable.id].value, + v_name = it[vName], + v_a_time = formatLocalDateTimeToString(it[vATime]) ) } } } - fun getAnalyticsDetailByVideoId(vId: Int): ResultRow? { + fun getAnalyticsDetailByVideoId(vId: UUID): ResultRow? { return transaction { - VideoTable.selectAll().where { VideoTable.id eq vId }.singleOrNull() + VideosTable.selectAll().where { VideosTable.id eq vId }.singleOrNull() } } - fun selectVideoDetailByVid(vId: Int): List { + fun selectVideoDetailByVid(vId: UUID): List { return transaction { - VideoAnalyticsDetailTable + VideoAnalyticsDetailsTable .selectAll() - .where { VideoAnalyticsDetailTable.vId eq vId } + .where { VideoAnalyticsDetailsTable.vId eq vId } .map { VideoDetail( - a_time_stamp = it[VideoAnalyticsDetailTable.aTimeStamp], - a_total_people = it[VideoAnalyticsDetailTable.aTotalPeople], - a_total_people_masked = it[VideoAnalyticsDetailTable.aTotalPeopleMasked], - a_action = it[VideoAnalyticsDetailTable.aAction] + a_time_stamp = it[VideoAnalyticsDetailsTable.aTimeStamp], + a_total_people = it[VideoAnalyticsDetailsTable.aTotalPeople], + a_total_people_masked = it[VideoAnalyticsDetailsTable.aTotalPeopleMasked], + a_action = it[VideoAnalyticsDetailsTable.aAction] ) } } diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/CORS.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/CORS.kt similarity index 88% rename from ktor/src/main/kotlin/ink/snowflake/server/plugins/CORS.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/CORS.kt index a489214..2003ee0 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/CORS.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/CORS.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.plugins +package ink.snowflake.server.utils.plugins import ink.snowflake.server.utils.AppConfig import io.ktor.http.* @@ -14,7 +14,7 @@ fun Application.configureCORS() { allowHost("localhost:8089") allowHost("127.0.0.1:8089") allowHost("171.212.101.199:8089") - allowHost("debug.scaitcn.com:8089") + allowHost("s1.ronsunny.cn:8089") // 进一步配置 CORS allowMethod(HttpMethod.Get) diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Databases.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Databases.kt similarity index 87% rename from ktor/src/main/kotlin/ink/snowflake/server/plugins/Databases.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Databases.kt index 042e6b4..64536ee 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Databases.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Databases.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.plugins +package ink.snowflake.server.utils.plugins import ink.snowflake.server.utils.AppConfig import org.jetbrains.exposed.v1.jdbc.Database diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Security.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Security.kt similarity index 96% rename from ktor/src/main/kotlin/ink/snowflake/server/plugins/Security.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Security.kt index fbf80aa..284d6db 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Security.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Security.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.plugins +package ink.snowflake.server.utils.plugins import com.auth0.jwt.JWT import com.auth0.jwt.algorithms.Algorithm diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Serialization.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Serialization.kt new file mode 100644 index 0000000..18dfae3 --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Serialization.kt @@ -0,0 +1,28 @@ +package ink.snowflake.server.utils.plugins + +import ink.snowflake.server.model.UUIDSerializer +import io.ktor.serialization.gson.* +import io.ktor.serialization.kotlinx.json.* +import io.ktor.server.application.* +import io.ktor.server.plugins.contentnegotiation.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import java.util.UUID + +fun Application.configureSerialization() { + install(ContentNegotiation) { + json( + Json { + prettyPrint = true + serializersModule = module + encodeDefaults = true + } + ) + gson{ + setPrettyPrinting() + } + } +} +val module = SerializersModule { + contextual(UUID::class, UUIDSerializer) +} diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/StaticPath.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/StaticPath.kt similarity index 89% rename from ktor/src/main/kotlin/ink/snowflake/server/plugins/StaticPath.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/StaticPath.kt index ea64743..4032c74 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/StaticPath.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/StaticPath.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.plugins +package ink.snowflake.server.utils.plugins import ink.snowflake.server.utils.AppConfig import io.ktor.server.application.* diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/StatusPages.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/StatusPages.kt similarity index 96% rename from ktor/src/main/kotlin/ink/snowflake/server/plugins/StatusPages.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/StatusPages.kt index 09e5b6c..d3abe1b 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/StatusPages.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/StatusPages.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.plugins +package ink.snowflake.server.utils.plugins import ink.snowflake.server.model.response.BaseResponse import io.ktor.http.* @@ -22,7 +22,7 @@ fun Application.configureStatusPages() { call.respond( HttpStatusCode.InternalServerError, BaseResponse( status = false, - message = cause.localizedMessage + message = cause.localizedMessage?:"Unknown error" ) ) } diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Templating.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Templating.kt similarity index 93% rename from ktor/src/main/kotlin/ink/snowflake/server/plugins/Templating.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Templating.kt index b54f0f5..21450d5 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/Templating.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/Templating.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.plugins +package ink.snowflake.server.utils.plugins import io.ktor.http.* import io.ktor.server.application.* diff --git a/ktor/src/main/kotlin/ink/snowflake/server/plugins/configureSockets.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/configureSockets.kt similarity index 93% rename from ktor/src/main/kotlin/ink/snowflake/server/plugins/configureSockets.kt rename to ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/configureSockets.kt index a827a9c..e1f6551 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/plugins/configureSockets.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/plugins/configureSockets.kt @@ -1,4 +1,4 @@ -package ink.snowflake.server.plugins +package ink.snowflake.server.utils.plugins import io.ktor.serialization.gson.* import io.ktor.server.application.* diff --git a/ktor/src/main/resources/application.yaml b/ktor/src/main/resources/application.yaml index bb2cb5b..c1c412c 100644 --- a/ktor/src/main/resources/application.yaml +++ b/ktor/src/main/resources/application.yaml @@ -16,7 +16,8 @@ ktor: realm: "Snowflake Server" secret: "secret_jwt" database: - url: "jdbc:postgresql://localhost:5432/ktor" + url: "jdbc:postgresql://10.10.10.9:5432/ktor2" +# url: "jdbc:postgresql://localhost:5432/ktor2" driver: "org.postgresql.Driver" user: "postgres" password: "123456"