package cc.unitmesh.server.workflow import cc.unitmesh.server.auth.AuthService import cc.unitmesh.server.workflow.engine.WorkflowEngine import cc.unitmesh.server.workflow.models.* import io.github.oshai.kotlinlogging.KotlinLogging import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.server.sse.* import io.ktor.sse.ServerSentEvent import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json private val logger = KotlinLogging.logger {} /** * 发送信号请求 */ @Serializable data class SendSignalRequest( val signalName: String, val data: Map = emptyMap() ) /** * 工作流路由配置 */ fun Route.workflowRoutes( workflowEngine: WorkflowEngine, authService: AuthService ) { val json = Json { encodeDefaults = true; ignoreUnknownKeys = true } route("/api/workflows") { // 启动工作流 post("/start") { val token = call.request.headers["Authorization"]?.removePrefix("Bearer ") val username = token?.let { authService.validateToken(it) } if (username == null) { call.respond(HttpStatusCode.Unauthorized, mapOf("error" to "Unauthorized")) return@post } try { val request = call.receive() val response = workflowEngine.startWorkflow(request.copy(userId = username)) call.respond(HttpStatusCode.Created, response) } catch (e: Exception) { logger.error(e) { "Failed to start workflow" } call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message)) } } // 获取工作流状态 get("/{workflowId}") { val token = call.request.headers["Authorization"]?.removePrefix("Bearer ") val username = token?.let { authService.validateToken(it) } if (username == null) { call.respond(HttpStatusCode.Unauthorized, mapOf("error" to "Unauthorized")) return@get } val workflowId = call.parameters["workflowId"] ?: return@get call.respond( HttpStatusCode.BadRequest, mapOf("error" to "Missing workflowId") ) val metadata = workflowEngine.queryMetadata(workflowId) if (metadata == null) { call.respond(HttpStatusCode.NotFound, mapOf("error" to "Workflow not found")) return@get } // 检查权限 if (metadata.ownerId != username) { call.respond(HttpStatusCode.Forbidden, mapOf("error" to "Access denied")) return@get } call.respond(metadata) } // 查询工作流状态 get("/{workflowId}/query") { val token = call.request.headers["Authorization"]?.removePrefix("Bearer ") val username = token?.let { authService.validateToken(it) } if (username == null) { call.respond(HttpStatusCode.Unauthorized, mapOf("error" to "Unauthorized")) return@get } val workflowId = call.parameters["workflowId"] ?: return@get call.respond( HttpStatusCode.BadRequest, mapOf("error" to "Missing workflowId") ) val state = workflowEngine.queryState(workflowId) if (state == null) { call.respond(HttpStatusCode.NotFound, mapOf("error" to "Workflow state not found")) return@get } call.respond(state) } // 发送信号 post("/{workflowId}/signal") { val token = call.request.headers["Authorization"]?.removePrefix("Bearer ") val username = token?.let { authService.validateToken(it) } if (username == null) { call.respond(HttpStatusCode.Unauthorized, mapOf("error" to "Unauthorized")) return@post } val workflowId = call.parameters["workflowId"] ?: return@post call.respond( HttpStatusCode.BadRequest, mapOf("error" to "Missing workflowId") ) try { val request = call.receive() workflowEngine.sendSignal( workflowId = workflowId, signalName = request.signalName, signalData = request.data ) call.respond(HttpStatusCode.OK, mapOf("success" to true)) } catch (e: Exception) { logger.error(e) { "Failed to send signal to workflow $workflowId" } call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message)) } } // 取消工作流 post("/{workflowId}/cancel") { val token = call.request.headers["Authorization"]?.removePrefix("Bearer ") val username = token?.let { authService.validateToken(it) } if (username == null) { call.respond(HttpStatusCode.Unauthorized, mapOf("error" to "Unauthorized")) return@post } val workflowId = call.parameters["workflowId"] ?: return@post call.respond( HttpStatusCode.BadRequest, mapOf("error" to "Missing workflowId") ) val metadata = workflowEngine.queryMetadata(workflowId) if (metadata == null) { call.respond(HttpStatusCode.NotFound, mapOf("error" to "Workflow not found")) return@post } // 检查权限 if (metadata.ownerId != username) { call.respond(HttpStatusCode.Forbidden, mapOf("error" to "Access denied")) return@post } workflowEngine.cancelWorkflow(workflowId) call.respond(HttpStatusCode.OK, mapOf("success" to true, "message" to "Cancellation requested")) } // 订阅工作流事件(SSE) route("/{workflowId}/events", HttpMethod.Get) { sse { val token = call.request.headers["Authorization"]?.removePrefix("Bearer ") val username = token?.let { authService.validateToken(it) } if (username == null) { send(ServerSentEvent( data = """{"error": "Unauthorized"}""", event = "error" )) return@sse } val workflowId = call.parameters["workflowId"] if (workflowId == null) { send(ServerSentEvent( data = """{"error": "Missing workflowId"}""", event = "error" )) return@sse } logger.info { "User $username subscribing to workflow $workflowId events" } try { workflowEngine.subscribeToEvents(workflowId).collect { event -> send(ServerSentEvent( data = json.encodeToString(event), event = event.eventType, id = event.id )) } } catch (e: Exception) { logger.error(e) { "Error in SSE stream for workflow $workflowId" } send(ServerSentEvent( data = """{"error": "${e.message}"}""", event = "error" )) } } } } }