调整数据库结构,ID不再自增,使用UUID,修改后端逻辑

This commit is contained in:
BBIT-Kai
2025-08-22 10:05:24 +08:00
parent 10ca219970
commit f79a82bb87
46 changed files with 462 additions and 429 deletions
@@ -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.*
@@ -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() {
@@ -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.*
@@ -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 {
@@ -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<DeviceItem> = mutableListOf()
val devices: MutableList<DeviceItemResponse> = 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))
@@ -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,32 +50,27 @@ fun Application.User(config: AppConfig) {
val password = loginRequest.password
// 查找用户
val userId = UserDAO.getUserIdByEmail(email)
val res = if(userId == null){
call.respond(
if (userId == null) {
// 账号不存在
-1
}else{
BaseResponse(status = false, message = "尚未注册", data = null)
} else {
val userPassword = UserDAO.getPasswordById(userId)
// 验证密码
if (password == userPassword) {
// 登录成功
userId
BaseResponse (
status = true, data = LoginResponse(
userId,
generateAccessToken(config, userId),
generateRefreshToken(config, userId)
)
)
} else {
// 账号密码不匹配
-2
BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null)
}
}
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)
)
)
}
)
}
post("/sendCode") {
@@ -98,40 +94,36 @@ fun Application.User(config: AppConfig) {
// 从 Redis 获取保存的验证码
val bucket: RBucket<String> = redisClient.getBucket("verificationCode:$account")
val storedCode = bucket.get()
call.respond(
if (storedCode == null) {
call.respond(BaseResponse(status = false, message = "验证码过期", data = null))
BaseResponse(status = false, message = "验证码过期,请重新获取", data = null)
} else if (storedCode != register.code) {
call.respond(BaseResponse(status = false, message = "验证码错误", data = null))
BaseResponse(status = false, message = "验证码错误,请检查或重新获取", data = null)
} else {
// 查找用户
val userId = UserDAO.getUserIdByEmail(register.account)
val res = if(userId != null){
// 如果用户已存在,返回 0
0
}else{
if (userId == null) {
// 用户不存在,插入新用户
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)
)
)
)
)
val id = UserDAO.registerByEmailAndGetId(register.account, register.password)
if (id == null) {
BaseResponse(status = true, message = "注册失败,请联系管理员", data = null)
} else {
call.respond(BaseResponse(status = false, message = "账户已存在", data = null))
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<RefreshTokenRequest>()
val verifier = JWT.require(Algorithm.HMAC256(config.jwtSecret))
.withAudience(config.jwtAudience)
@@ -139,36 +131,39 @@ fun Application.User(config: AppConfig) {
.build()
val decodedJWT = verifier.verify(token.refreshToken)
val tokenType = decodedJWT.getClaim("token_type").asString()
if (tokenType != "refresh_token") {
call.respond(
try {
if (tokenType != "refresh_token") {
BaseResponse(
status = false,
message = "拿什么乱七八糟的东西跟我换Access Token呢,???",
data = null
)
)
}
val userId = decodedJWT.getClaim("user_id").asInt()
val userId = UUID.fromString(decodedJWT.getClaim("user_id").asString())
// 生成新的access token和refresh token
call.respond(
BaseResponse(
status = true, data = RefreshTokenResponse(
generateAccessToken(config, userId),
generateRefreshToken(config, userId)
)
)
)
call.respond(BaseResponse(data = generateAccessToken(config, userId)))
BaseResponse(data = generateAccessToken(config, userId))
} catch (ex: Exception) {
call.respond(BaseResponse(status = false, message = "token解析错误", data = null))
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 = """
<html>
<body style="font-family: Arial, sans-serif; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 10px; background-color: #f9f9f9;">
<h2 style="color: #4CAF50;">Welcome to CyberEcho!</h2>
<p style="font-size: 16px;">Thank you for signing up with <strong>CyberEcho</strong>! We are excited to have you on board.</p>
<h2 style="color: #4CAF50;">Welcome to BBIT-Lab!</h2>
<p style="font-size: 16px;">Thank you for signing up with <strong>BBIT-Lab</strong>! We are excited to have you on board.</p>
<p style="font-size: 16px;">To complete your registration, please use the verification code below:</p>
@@ -258,7 +253,7 @@ fun sendVerificationEmail(config: AppConfig, recipientEmail: String, verificatio
<hr style="border-top: 1px solid #ddd; margin-top: 30px;">
<p style="font-size: 14px; color: #888; text-align: center;">
Best regards<br>
<strong>The CyberEcho Account Team</strong>
<strong>The BBIT-Lab Account Team</strong>
</p>
</div>
</body>
@@ -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], // 最大停留时间
@@ -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.*
@@ -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<ChatRecord> {
return transaction {
ChatRecordsTable
.selectAll()
.where { ChatRecordsTable.userId eq userId }
.orderBy(ChatRecordsTable.sentAt to SortOrder.DESC)
.limit(5)
.toList().map { it.toChatRecord() }
}
}
}
@@ -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()
}
}
}
@@ -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)
}
@@ -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")
}
@@ -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<String, String>,
@@ -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<Int> = integer("user_id").references(UserTable.id)
val userId: Column<UUID> = uuid("user_id").references(UsersTable.id)
// 消息内容
val message: Column<String> = 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"
@@ -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)
@@ -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()
@@ -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)
}
@@ -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")
}
@@ -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<Proxy> = listOf()
) {
@@ -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, // 文件名
@@ -1,6 +1,5 @@
package ink.snowflake.server.model.request
import ink.snowflake.server.model.response.Token
import kotlinx.serialization.Serializable
@Serializable
@@ -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
)
@@ -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,
)
@@ -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)
@@ -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)
data class Token(val token: String, val expiresAt: Long = 0, val expiresStr:String)
@@ -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?,
@@ -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,
@@ -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?
)
@@ -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, // 发送时间(时间戳,单位为毫秒)
@@ -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()
}
}
}
@@ -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<JWTPrincipal>()?.payload?.getClaim("user_id")?.asInt()
return UUID.fromString(call.principal<JWTPrincipal>()?.payload?.getClaim("user_id")?.asString())
}
fun formatDateToTargetString(date: Date, targetFormat: String): String {
@@ -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 连接的 Mapkey 为 user_idvalue 为 WebSocketSession
private val connections = ConcurrentHashMap<Int, WebSocketSession>()
private val connections = ConcurrentHashMap<UUID, WebSocketSession>()
// 添加 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))
}
@@ -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
@@ -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<ChatRecord> {
return transaction {
ChatRecordsTable
.selectAll()
.where { ChatRecordsTable.userId eq userId }
.orderBy(ChatRecordsTable.sentAt to SortOrder.DESC)
.limit(5)
.toList().map { it.toChatRecord() }
}
}
}
@@ -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<ImageAnalyticsRequest> {
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<String, String>,
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<String, String>,
processing_time = formatLocalDateTimeToString(it[ScaImagesTable.processing_time])
)
}
}
@@ -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
}
}
}
}
@@ -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<VideoListResponse> {
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<VideoDetail> {
fun selectVideoDetailByVid(vId: UUID): List<VideoDetail> {
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]
)
}
}
@@ -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)
@@ -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
@@ -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
@@ -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)
}
@@ -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.*
@@ -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<Nothing>(
status = false,
message = cause.localizedMessage
message = cause.localizedMessage?:"Unknown error"
)
)
}
@@ -1,4 +1,4 @@
package ink.snowflake.server.plugins
package ink.snowflake.server.utils.plugins
import io.ktor.http.*
import io.ktor.server.application.*
@@ -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.*
+2 -1
View File
@@ -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"