整理代码

This commit is contained in:
BBIT-Kai
2025-08-25 09:17:59 +08:00
parent bbc56b313d
commit d2823b51af
11 changed files with 346 additions and 342 deletions
@@ -5,9 +5,9 @@ import ink.snowflake.server.gson
import ink.snowflake.server.model.request.ChatRequest import ink.snowflake.server.model.request.ChatRequest
import ink.snowflake.server.model.response.ChatResponse import ink.snowflake.server.model.response.ChatResponse
import ink.snowflake.server.model.response.BaseResponse import ink.snowflake.server.model.response.BaseResponse
import ink.snowflake.server.utils.TokenUtils.getUserIdByToken
import ink.snowflake.server.utils.WebSocketManager import ink.snowflake.server.utils.WebSocketManager
import ink.snowflake.server.utils.dao.ChatRecordsDao import ink.snowflake.server.utils.dao.ChatRecordsDao
import ink.snowflake.server.utils.getUserIdByToken
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.engine.cio.* import io.ktor.client.engine.cio.*
import io.ktor.client.request.* import io.ktor.client.request.*
@@ -4,7 +4,7 @@ import com.google.gson.Gson
import ink.snowflake.server.SERVER_PATH_FRP import ink.snowflake.server.SERVER_PATH_FRP
import ink.snowflake.server.model.request.DevicesInfoRequest import ink.snowflake.server.model.request.DevicesInfoRequest
import ink.snowflake.server.model.response.* import ink.snowflake.server.model.response.*
import ink.snowflake.server.utils.runCommand import ink.snowflake.server.utils.CommandUtils.runCommand
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.auth.Auth import io.ktor.client.plugins.auth.Auth
@@ -8,9 +8,13 @@ import ink.snowflake.server.model.request.RefreshTokenRequest
import ink.snowflake.server.model.request.RegisterRequest import ink.snowflake.server.model.request.RegisterRequest
import ink.snowflake.server.model.response.* import ink.snowflake.server.model.response.*
import ink.snowflake.server.utils.AppConfig import ink.snowflake.server.utils.AppConfig
import ink.snowflake.server.utils.EmailUtils.generateVerificationCode
import ink.snowflake.server.utils.EmailUtils.sendVerificationEmail
import ink.snowflake.server.utils.TokenUtils.generateAccessToken
import ink.snowflake.server.utils.TokenUtils.generateRefreshToken
import ink.snowflake.server.utils.TokenUtils.getUserIdByToken
import ink.snowflake.server.utils.dao.AIDao import ink.snowflake.server.utils.dao.AIDao
import ink.snowflake.server.utils.dao.UserDAO import ink.snowflake.server.utils.dao.UserDAO
import ink.snowflake.server.utils.getUserIdByToken
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
import io.ktor.server.request.* import io.ktor.server.request.*
@@ -42,236 +46,144 @@ fun Application.User(config: AppConfig) {
// 初始化 Redis 连接 // 初始化 Redis 连接
val redisClient: RedissonClient = setupRedis() val redisClient: RedissonClient = setupRedis()
routing { routing {
route("/api") { route("/api/user") {
route("/user") { post("/login") {
post("/login") { val loginRequest = call.receive<LoginRequest>()
val loginRequest = call.receive<LoginRequest>() val email = loginRequest.account
val email = loginRequest.account val password = loginRequest.password
val password = loginRequest.password // 查找用户
// 查找用户 val userId = UserDAO.getUserIdByEmail(email)
val userId = UserDAO.getUserIdByEmail(email) call.respond(
call.respond( if (userId == null) {
if (userId == null) { // 账号不存在
// 账号不存在 BaseResponse(status = false, message = "尚未注册", data = null)
BaseResponse(status = false, message = "尚未注册", data = null) } else {
} else { val userPassword = UserDAO.getPasswordById(userId)
val userPassword = UserDAO.getPasswordById(userId) // 验证密码
// 验证密码 if (password == userPassword) {
if (password == userPassword) { // 登录成功
// 登录成功
BaseResponse (
status = true, data = LoginResponse(
userId,
generateAccessToken(config, userId),
generateRefreshToken(config, userId)
)
)
} else {
// 账号密码不匹配
BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null)
}
}
)
}
post("/sendCode") {
val account = call.receive<CommonRequest>()
// 将验证码存入 Redis,设置过期时间为 10 分钟
val bucket: RBucket<String> = redisClient.getBucket("verificationCode:${account.str}")
// 生成随机验证码(6位数字)
val verificationCode = generateVerificationCode()
println("验证码:$verificationCode")
// 10分钟的过期时间
bucket.set(verificationCode, Duration.ofMinutes(10))
launch {
sendVerificationEmail(config, account.str, verificationCode)
}
call.respond(BaseResponse(status = true, message = "已发送验证码", data = null))
}
post("/register") {
// 可能返回的情况:1. 注册成功 -1. 账户已存在 -2. 验证码错误 -3. 验证码过期
val register = call.receive<RegisterRequest>()
val account = register.account
// 从 Redis 获取保存的验证码
val bucket: RBucket<String> = redisClient.getBucket("verificationCode:$account")
val storedCode = bucket.get()
call.respond(
if (storedCode == null) {
BaseResponse(status = false, message = "验证码过期,请重新获取", data = null)
} else if (storedCode != register.code) {
BaseResponse(status = false, message = "验证码错误,请检查或重新获取", data = null)
} else {
// 查找用户
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") {
val token = call.receive<RefreshTokenRequest>()
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 = UUID.fromString(decodedJWT.getClaim("user_id").asString())
// 生成新的access token和refresh token
BaseResponse( BaseResponse(
status = true, data = RefreshTokenResponse( status = true, data = LoginResponse(
userId,
generateAccessToken(config, userId), generateAccessToken(config, userId),
generateRefreshToken(config, userId) generateRefreshToken(config, userId)
) )
) )
BaseResponse(data = generateAccessToken(config, userId)) } else {
} catch (ex: Exception) { // 账号密码不匹配
BaseResponse(status = false, message = "token解析错误", data = null) BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null)
} }
)
}
authenticate {
get("/getAiList") {
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) }
if (userId != null) { post("/sendCode") {
val userInfo = UserDAO.getUserInfoByUserId(userId) val account = call.receive<CommonRequest>()
if (userInfo != null) { // 将验证码存入 Redis,设置过期时间为 10 分钟
val response = BaseResponse(data = userInfo) val bucket: RBucket<String> = redisClient.getBucket("verificationCode:${account.str}")
call.respond(response) // 生成随机验证码(6位数字)
val verificationCode = generateVerificationCode()
println("验证码:$verificationCode")
// 10分钟的过期时间
bucket.set(verificationCode, Duration.ofMinutes(10))
launch {
sendVerificationEmail(config, account.str, verificationCode)
}
call.respond(BaseResponse(status = true, message = "已发送验证码", data = null))
}
post("/register") {
// 可能返回的情况:1. 注册成功 -1. 账户已存在 -2. 验证码错误 -3. 验证码过期
val register = call.receive<RegisterRequest>()
val account = register.account
// 从 Redis 获取保存的验证码
val bucket: RBucket<String> = redisClient.getBucket("verificationCode:$account")
val storedCode = bucket.get()
call.respond(
if (storedCode == null) {
BaseResponse(status = false, message = "验证码过期,请重新获取", data = null)
} else if (storedCode != register.code) {
BaseResponse(status = false, message = "验证码错误,请检查或重新获取", data = null)
} else {
// 查找用户
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 { } else {
call.respond(BaseResponse(data = "查无此人")) BaseResponse(
status = true, message = "注册成功", data = LoginResponse(
id,
generateAccessToken(config, id),
generateRefreshToken(config, id)
)
)
} }
} else { } else {
call.respond(BaseResponse(data = "Token出错")) // 如果用户已存在,返回 0
BaseResponse(status = false, message = "账户已存在", data = null)
} }
} }
)
}
post("/refreshToken") {
val token = call.receive<RefreshTokenRequest>()
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 = UUID.fromString(decodedJWT.getClaim("user_id").asString())
// 生成新的access token和refresh token
BaseResponse(
status = true, data = RefreshTokenResponse(
generateAccessToken(config, userId),
generateRefreshToken(config, userId)
)
)
BaseResponse(data = generateAccessToken(config, userId))
} catch (ex: Exception) {
BaseResponse(status = false, message = "token解析错误", data = null)
}
)
}
authenticate {
get("/getAiList") {
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)
if (userId != null) {
val userInfo = UserDAO.getUserInfoByUserId(userId)
if (userInfo != null) {
val response = BaseResponse(data = userInfo)
call.respond(response)
} else {
call.respond(BaseResponse(data = "查无此人"))
}
} else {
call.respond(BaseResponse(data = "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: UUID): Token {
return generateToken(config, userId, 10 * 24 * 60 * 60, "refresh_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.toString())
.withClaim("token_type", tokenType)
.withExpiresAt(expiresAt)
.sign(Algorithm.HMAC256(config.jwtSecret))
return Token(token, expiresAt.time, DateFormat.getDateTimeInstance().format(expiresAt))
}
// 生成4位随机验证码
fun generateVerificationCode(): String {
return String.format("%04d", Random().nextInt(10000))
}
fun sendVerificationEmail(config: AppConfig, recipientEmail: String, verificationCode: String) {
// 设置邮件会话的属性
val properties = Properties().apply {
put("mail.smtp.host", config.smtpHost)
put("mail.smtp.port", config.smtpPort)
put("mail.smtp.auth", "true") // 启用身份验证
put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory") // 启用 SSL
put("mail.smtp.socketFactory.fallback", "false") // 禁用备用连接
}
// 创建会话
val session = Session.getInstance(properties, object : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication {
return PasswordAuthentication(config.smtpUser, config.smtpPassword)
}
})
try {
// Create email content with a more polished template
val message = MimeMessage(session).apply {
setFrom(InternetAddress(config.smtpUser))
setRecipient(Message.RecipientType.TO, InternetAddress(recipientEmail))
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 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>
<div style="background-color: #f1f1f1; padding: 15px; border-radius: 5px; font-size: 20px; text-align: center; color: #333;">
<strong style="color: #4CAF50;">$verificationCode</strong>
</div>
<p style="font-size: 16px; margin-top: 20px;">This code is valid for the next <strong>10 minutes</strong>. If you did not request this, please ignore this email.</p>
<p style="font-size: 16px; margin-top: 20px;">Please do not reply to this email. This inbox is not monitored.</p>
<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 BBIT-Lab Account Team</strong>
</p>
</div>
</body>
</html>
"""
setContent(htmlContent, "text/html")
}
// 发送邮件
Transport.send(message)
println("Verification email sent successfully to $recipientEmail")
} catch (e: MessagingException) {
e.printStackTrace()
println("Failed to send verification email.")
}
}
fun hashPassword(password: String): String {
val bytes = MessageDigest.getInstance("SHA-256").digest(password.toByteArray(UTF_8))
return bytes.joinToString("") { "%02x".format(it) }
}
@@ -12,8 +12,10 @@ import ink.snowflake.server.model.database.VideosTable.vATotalPeople
import ink.snowflake.server.model.database.VideosTable.vStartDateTime import ink.snowflake.server.model.database.VideosTable.vStartDateTime
import ink.snowflake.server.model.request.VideoAnalyticsRequest import ink.snowflake.server.model.request.VideoAnalyticsRequest
import ink.snowflake.server.model.response.* import ink.snowflake.server.model.response.*
import ink.snowflake.server.utils.CommandUtils.runCommand
import ink.snowflake.server.utils.MyUtils
import ink.snowflake.server.utils.MyUtils.getFriendlyActionName
import ink.snowflake.server.utils.WebSocketManager.broadcastMessage import ink.snowflake.server.utils.WebSocketManager.broadcastMessage
import ink.snowflake.server.utils.runCommand
import io.ktor.http.content.* import io.ktor.http.content.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
@@ -26,6 +28,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.toJavaLocalDateTime import kotlinx.datetime.toJavaLocalDateTime
import kotlinx.html.Entities
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -99,7 +102,7 @@ fun Application.VideoAnalytics() {
) )
println("-----------------" + command.joinToString(" ")) println("-----------------" + command.joinToString(" "))
runCommand(command) { runCommand(command) {
println(it) println(Entities.it)
} }
} }
// 获取已分析视频列表 // 获取已分析视频列表
@@ -254,13 +257,3 @@ suspend fun broadcastMessage(message: String) { // 封装的广播消息方法
} }
} }
} }
private fun getFriendlyActionName(name: String): String {
return if (name == "feed") {
"喂桑"
} else if (name == "disinfection") {
"消毒"
} else {
name
}
}
@@ -0,0 +1,67 @@
package ink.snowflake.server.utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.BufferedReader
import java.io.InputStreamReader
object CommandUtils {
fun runCommand(command: String): String {
println("command--$command")
return try {
val process = Runtime.getRuntime().exec(command)
// 等待命令执行完毕
process.waitFor()
// 读取输出流
val reader = BufferedReader(InputStreamReader(process.inputStream))
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
val output = StringBuilder()
// 读取标准输出
reader.use { r ->
var line: String? = r.readLine()
while (line != null) {
output.append(line).append("\n")
line = r.readLine()
}
}
// 读取错误输出
errorReader.use { er ->
var errorLine: String? = er.readLine()
while (errorLine != null) {
output.append("ERROR: ").append(errorLine).append("\n")
errorLine = er.readLine()
}
}
output.toString()
} catch (e: Exception) {
"Error running command: ${e.message}"
}
}
fun runCommand(command: List<String>, logCallback: (String) -> Unit): Process {
// 使用 ProcessBuilder 构建命令并启动
val process = ProcessBuilder(command)
.redirectErrorStream(true) // 合并标准输出和错误输出
.start()
// 创建一个 CoroutineScope 来管理协程
val scope = CoroutineScope(Dispatchers.IO)
// 使用协程读取标准输出和错误输出
scope.launch {
try {
val reader = BufferedReader(InputStreamReader(process.inputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
logCallback(line ?: "") // 将日志推送到回调
}
} catch (e: Exception) {
logCallback("Error reading output: ${e.message}")
}
}
return process
}
}
@@ -0,0 +1,78 @@
package ink.snowflake.server.utils
import java.util.Properties
import java.util.Random
import javax.mail.Authenticator
import javax.mail.Message
import javax.mail.MessagingException
import javax.mail.PasswordAuthentication
import javax.mail.Session
import javax.mail.Transport
import javax.mail.internet.InternetAddress
import javax.mail.internet.MimeMessage
object EmailUtils {
// 生成4位随机验证码
fun generateVerificationCode(): String {
return String.format("%04d", Random().nextInt(10000))
}
fun sendVerificationEmail(config: AppConfig, recipientEmail: String, verificationCode: String) {
// 设置邮件会话的属性
val properties = Properties().apply {
put("mail.smtp.host", config.smtpHost)
put("mail.smtp.port", config.smtpPort)
put("mail.smtp.auth", "true") // 启用身份验证
put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory") // 启用 SSL
put("mail.smtp.socketFactory.fallback", "false") // 禁用备用连接
}
// 创建会话
val session = Session.getInstance(properties, object : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication {
return PasswordAuthentication(config.smtpUser, config.smtpPassword)
}
})
try {
// Create email content with a more polished template
val message = MimeMessage(session).apply {
setFrom(InternetAddress(config.smtpUser))
setRecipient(Message.RecipientType.TO, InternetAddress(recipientEmail))
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 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>
<div style="background-color: #f1f1f1; padding: 15px; border-radius: 5px; font-size: 20px; text-align: center; color: #333;">
<strong style="color: #4CAF50;">$verificationCode</strong>
</div>
<p style="font-size: 16px; margin-top: 20px;">This code is valid for the next <strong>10 minutes</strong>. If you did not request this, please ignore this email.</p>
<p style="font-size: 16px; margin-top: 20px;">Please do not reply to this email. This inbox is not monitored.</p>
<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 BBIT-Lab Account Team</strong>
</p>
</div>
</body>
</html>
"""
setContent(htmlContent, "text/html")
}
// 发送邮件
Transport.send(message)
println("Verification email sent successfully to $recipientEmail")
} catch (e: MessagingException) {
e.printStackTrace()
println("Failed to send verification email.")
}
}
}
@@ -14,75 +14,26 @@ import java.text.SimpleDateFormat
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
object MyUtils {
fun getUserIdByToken(call: ApplicationCall) : UUID?{ fun formatDateToTargetString(date: Date, targetFormat: String): String {
// 通过token获取user_id val formatter = SimpleDateFormat(targetFormat) // 创建格式化器
return UUID.fromString(call.principal<JWTPrincipal>()?.payload?.getClaim("user_id")?.asString()) return formatter.format(date) // 格式化日期并返回字符串
}
fun formatDateToTargetString(date: Date, targetFormat: String): String {
val formatter = SimpleDateFormat(targetFormat) // 创建格式化器
return formatter.format(date) // 格式化日期并返回字符串
}
fun formatLocalDateTimeToString(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") // 创建格式化器
return localDateTime.toJavaLocalDateTime().format(formatter) // 转换并格式化
}
fun runCommand(command: String): String {
println("command--$command")
return try {
val process = Runtime.getRuntime().exec(command)
// 等待命令执行完毕
process.waitFor()
// 读取输出流
val reader = BufferedReader(InputStreamReader(process.inputStream))
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
val output = StringBuilder()
// 读取标准输出
reader.use { r ->
var line: String? = r.readLine()
while (line != null) {
output.append(line).append("\n")
line = r.readLine()
}
}
// 读取错误输出
errorReader.use { er ->
var errorLine: String? = er.readLine()
while (errorLine != null) {
output.append("ERROR: ").append(errorLine).append("\n")
errorLine = er.readLine()
}
}
output.toString()
} catch (e: Exception) {
"Error running command: ${e.message}"
} }
}
fun runCommand(command: List<String>, logCallback: (String) -> Unit): Process { fun formatLocalDateTimeToString(localDateTime: LocalDateTime): String {
// 使用 ProcessBuilder 构建命令并启动 val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") // 创建格式化器
val process = ProcessBuilder(command) return localDateTime.toJavaLocalDateTime().format(formatter) // 转换并格式化
.redirectErrorStream(true) // 合并标准输出和错误输出 }
.start()
// 创建一个 CoroutineScope 来管理协程 fun getFriendlyActionName(name: String): String {
val scope = CoroutineScope(Dispatchers.IO) return if (name == "feed") {
"喂桑"
// 使用协程读取标准输出和错误输出 } else if (name == "disinfection") {
scope.launch { "消毒"
try { } else {
val reader = BufferedReader(InputStreamReader(process.inputStream)) name
var line: String?
while (reader.readLine().also { line = it } != null) {
logCallback(line ?: "") // 将日志推送到回调
}
} catch (e: Exception) {
logCallback("Error reading output: ${e.message}")
} }
} }
return process
} }
@@ -0,0 +1,49 @@
package ink.snowflake.server.utils
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import ink.snowflake.server.model.response.Token
import io.ktor.server.application.ApplicationCall
import io.ktor.server.auth.jwt.JWTPrincipal
import io.ktor.server.auth.principal
import java.security.MessageDigest
import java.text.DateFormat
import java.util.Date
import java.util.UUID
import kotlin.text.Charsets.UTF_8
object TokenUtils {
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: UUID): Token {
return generateToken(config, userId, 10 * 24 * 60 * 60, "refresh_token")
}
fun getUserIdByToken(call: ApplicationCall) : UUID?{
// 通过token获取user_id
return UUID.fromString(call.principal<JWTPrincipal>()?.payload?.getClaim("user_id")?.asString())
}
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.toString())
.withClaim("token_type", tokenType)
.withExpiresAt(expiresAt)
.sign(Algorithm.HMAC256(config.jwtSecret))
return Token(token, expiresAt.time, DateFormat.getDateTimeInstance().format(expiresAt))
}
fun hashPassword(password: String): String {
val bytes = MessageDigest.getInstance("SHA-256").digest(password.toByteArray(UTF_8))
return bytes.joinToString("") { "%02x".format(it) }
}
}
@@ -1,46 +0,0 @@
var aiState = "等待分析任务中"
// 实时发送AI状态
webSocket("/handleState") { // WebSocket 路由
clients.add(this) // 添加当前连接的客户端
send(aiState) // 向客户端发送连接成功消息
try {
incoming.consumeEach { frame -> // 持续接收消息
when (frame) {
is Frame.Text -> {
aiState = frame.readText() // 更新状态
broadcastMessage(aiState) // 使用封装的方法广播消息
}
is Frame.Close -> {
println("Closed")
clients.remove(this)
close() // 确保关闭 WebSocket 连接
return@consumeEach
}
// 其他消息类型的处理
is Frame.Binary -> TODO() // 处理二进制消息
is Frame.Ping -> TODO() // 处理 Ping 消息
is Frame.Pong -> TODO() // 处理 Pong 消息
}
}
} catch (e: Exception) {
// 处理接收消息时的异常
close(CloseReason(CloseReason.Codes.NORMAL, "Client disconnected"))
e.printStackTrace()
} finally {
clients.remove(this) // 确保在连接关闭时移除客户端
}
}
// 拍照保存为图片 并且调用Python程序进行分析
//get("/takePhoto") {
// val camera = call.request.queryParameters["cameraId"]
// if (camera.isNullOrEmpty()) {
// call.respond(BaseResponse(status = false, message = "摄像头名称不能为空", data = null))
// return@get
// }
// stopHLSStream(camera)
// call.respond(BaseResponse(message = "摄像头流已停止", data = null))
//}
@@ -3,7 +3,7 @@ package ink.snowflake.server.utils.dao
import ink.snowflake.server.SERVER_PATH_OSS import ink.snowflake.server.SERVER_PATH_OSS
import ink.snowflake.server.model.request.ImageAnalyticsRequest import ink.snowflake.server.model.request.ImageAnalyticsRequest
import ink.snowflake.server.model.database.ScaImagesTable import ink.snowflake.server.model.database.ScaImagesTable
import ink.snowflake.server.utils.formatLocalDateTimeToString import ink.snowflake.server.utils.MyUtils.formatLocalDateTimeToString
import kotlinx.datetime.toKotlinLocalDateTime import kotlinx.datetime.toKotlinLocalDateTime
import org.jetbrains.exposed.v1.core.SortOrder import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.jdbc.insert import org.jetbrains.exposed.v1.jdbc.insert
@@ -7,7 +7,7 @@ import ink.snowflake.server.model.database.VideosTable.vName
import ink.snowflake.server.model.request.VideoAnalyticsRequest import ink.snowflake.server.model.request.VideoAnalyticsRequest
import ink.snowflake.server.model.request.VideoDetail import ink.snowflake.server.model.request.VideoDetail
import ink.snowflake.server.model.response.VideoListResponse import ink.snowflake.server.model.response.VideoListResponse
import ink.snowflake.server.utils.formatLocalDateTimeToString import ink.snowflake.server.utils.MyUtils.formatLocalDateTimeToString
import kotlinx.datetime.toKotlinLocalDateTime import kotlinx.datetime.toKotlinLocalDateTime
import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.SortOrder import org.jetbrains.exposed.v1.core.SortOrder