diff --git a/ktor/src/main/kotlin/ink/snowflake/server/controller/Chat.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/Chat.kt index 4b4f769..2bda520 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/controller/Chat.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/Chat.kt @@ -5,9 +5,9 @@ import ink.snowflake.server.gson 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.TokenUtils.getUserIdByToken import ink.snowflake.server.utils.WebSocketManager import ink.snowflake.server.utils.dao.ChatRecordsDao -import ink.snowflake.server.utils.getUserIdByToken import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.client.request.* diff --git a/ktor/src/main/kotlin/ink/snowflake/server/controller/RemoteDebug.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/RemoteDebug.kt index 9ad8b56..e8bd241 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/controller/RemoteDebug.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/RemoteDebug.kt @@ -4,7 +4,7 @@ import com.google.gson.Gson import ink.snowflake.server.SERVER_PATH_FRP import ink.snowflake.server.model.request.DevicesInfoRequest 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.engine.cio.CIO import io.ktor.client.plugins.auth.Auth diff --git a/ktor/src/main/kotlin/ink/snowflake/server/controller/User.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/User.kt index d54eb4e..f57a3ae 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/controller/User.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/User.kt @@ -8,9 +8,13 @@ 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.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.UserDAO -import ink.snowflake.server.utils.getUserIdByToken import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.request.* @@ -42,236 +46,144 @@ fun Application.User(config: AppConfig) { // 初始化 Redis 连接 val redisClient: RedissonClient = setupRedis() routing { - route("/api") { - route("/user") { - post("/login") { - val loginRequest = call.receive() - val email = loginRequest.account - val password = loginRequest.password - // 查找用户 - val userId = UserDAO.getUserIdByEmail(email) - call.respond( - 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) - } - } - ) - } - post("/sendCode") { - val account = call.receive() - // 将验证码存入 Redis,设置过期时间为 10 分钟 - val bucket: RBucket = 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() - val account = register.account - // 从 Redis 获取保存的验证码 - val bucket: RBucket = 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() - 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 + route("/api/user") { + post("/login") { + val loginRequest = call.receive() + val email = loginRequest.account + val password = loginRequest.password + // 查找用户 + val userId = UserDAO.getUserIdByEmail(email) + call.respond( + if (userId == null) { + // 账号不存在 + BaseResponse(status = false, message = "尚未注册", data = null) + } else { + val userPassword = UserDAO.getPasswordById(userId) + // 验证密码 + if (password == userPassword) { + // 登录成功 BaseResponse( - status = true, data = RefreshTokenResponse( + status = true, data = LoginResponse( + userId, generateAccessToken(config, userId), generateRefreshToken(config, userId) ) ) - BaseResponse(data = generateAccessToken(config, userId)) - } catch (ex: Exception) { - BaseResponse(status = false, message = "token解析错误", data = null) + } else { + // 账号密码不匹配 + 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) { - val userInfo = UserDAO.getUserInfoByUserId(userId) - if (userInfo != null) { - val response = BaseResponse(data = userInfo) - call.respond(response) + ) + } + post("/sendCode") { + val account = call.receive() + // 将验证码存入 Redis,设置过期时间为 10 分钟 + val bucket: RBucket = 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() + val account = register.account + // 从 Redis 获取保存的验证码 + val bucket: RBucket = 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 { - call.respond(BaseResponse(data = "查无此人")) + BaseResponse( + status = true, message = "注册成功", data = LoginResponse( + id, + generateAccessToken(config, id), + generateRefreshToken(config, id) + ) + ) } } else { - call.respond(BaseResponse(data = "Token出错")) + // 如果用户已存在,返回 0 + BaseResponse(status = false, message = "账户已存在", data = null) } } + ) + } + post("/refreshToken") { + 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 = 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 = """ - - -
-

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:

- -
- $verificationCode -
- -

This code is valid for the next 10 minutes. If you did not request this, please ignore this email.

- -

Please do not reply to this email. This inbox is not monitored.

-
-

- Best regards
- The BBIT-Lab Account Team -

-
- - - """ - 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) } -} diff --git a/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalytics.kt b/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalytics.kt index 0478d54..ff217aa 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalytics.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/controller/VideoAnalytics.kt @@ -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.request.VideoAnalyticsRequest 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.runCommand import io.ktor.http.content.* import io.ktor.server.application.* import io.ktor.server.auth.* @@ -26,6 +28,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.datetime.LocalDateTime import kotlinx.datetime.toJavaLocalDateTime +import kotlinx.html.Entities import java.io.File import java.io.IOException import java.time.format.DateTimeFormatter @@ -99,7 +102,7 @@ fun Application.VideoAnalytics() { ) println("-----------------" + command.joinToString(" ")) 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 - } -} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/CommandUtils.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/CommandUtils.kt new file mode 100644 index 0000000..4fc0359 --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/CommandUtils.kt @@ -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, 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 + } +} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/EmailUtils.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/EmailUtils.kt new file mode 100644 index 0000000..b9f8cce --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/EmailUtils.kt @@ -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 = """ + + +
+

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:

+ +
+ $verificationCode +
+ +

This code is valid for the next 10 minutes. If you did not request this, please ignore this email.

+ +

Please do not reply to this email. This inbox is not monitored.

+
+

+ Best regards
+ The BBIT-Lab Account Team +

+
+ + + """ + 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.") + } + } + +} \ No newline at end of file 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 cf08949..4c93e27 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/utils/MyUtils.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/MyUtils.kt @@ -14,75 +14,26 @@ import java.text.SimpleDateFormat import java.time.format.DateTimeFormatter import java.util.* +object MyUtils { -fun getUserIdByToken(call: ApplicationCall) : UUID?{ - // 通过token获取user_id - return UUID.fromString(call.principal()?.payload?.getClaim("user_id")?.asString()) -} - -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 formatDateToTargetString(date: Date, targetFormat: String): String { + val formatter = SimpleDateFormat(targetFormat) // 创建格式化器 + return formatter.format(date) // 格式化日期并返回字符串 } -} -fun runCommand(command: List, logCallback: (String) -> Unit): Process { - // 使用 ProcessBuilder 构建命令并启动 - val process = ProcessBuilder(command) - .redirectErrorStream(true) // 合并标准输出和错误输出 - .start() + fun formatLocalDateTimeToString(localDateTime: LocalDateTime): String { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") // 创建格式化器 + return localDateTime.toJavaLocalDateTime().format(formatter) // 转换并格式化 + } - // 创建一个 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}") + fun getFriendlyActionName(name: String): String { + return if (name == "feed") { + "喂桑" + } else if (name == "disinfection") { + "消毒" + } else { + name } } - return process } diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/TokenUtils.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/TokenUtils.kt new file mode 100644 index 0000000..6c52ea6 --- /dev/null +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/TokenUtils.kt @@ -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()?.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) } + } + +} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/bin b/ktor/src/main/kotlin/ink/snowflake/server/utils/bin deleted file mode 100644 index e65eb11..0000000 --- a/ktor/src/main/kotlin/ink/snowflake/server/utils/bin +++ /dev/null @@ -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)) -//} \ No newline at end of file diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ImageDao.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ImageDao.kt index e9f8efc..aa7f6e8 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ImageDao.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/ImageDao.kt @@ -3,7 +3,7 @@ package ink.snowflake.server.utils.dao import ink.snowflake.server.SERVER_PATH_OSS import ink.snowflake.server.model.request.ImageAnalyticsRequest 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 org.jetbrains.exposed.v1.core.SortOrder import org.jetbrains.exposed.v1.jdbc.insert diff --git a/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/VideoDao.kt b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/VideoDao.kt index a1c8716..c05e6f0 100644 --- a/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/VideoDao.kt +++ b/ktor/src/main/kotlin/ink/snowflake/server/utils/dao/VideoDao.kt @@ -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.VideoDetail 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 org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.SortOrder