mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-06 00:16:49 +08:00
feat: [wip] - Implement html node
This commit is contained in:
@@ -165,7 +165,7 @@ func (tm *DAG) Render(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx := mq.SetHeaders(r.Context(), map[string]string{consts.AwaitResponseKey: "true", "request_type": "render"})
|
ctx := mq.SetHeaders(r.Context(), map[string]string{consts.AwaitResponseKey: "true", "request_type": "render"})
|
||||||
ctx = context.WithValue(ctx, "query_params", r.URL.Query())
|
ctx = context.WithValue(ctx, "query_params", r.URL.Query())
|
||||||
rs := tm.Process(ctx, nil)
|
rs := tm.Process(ctx, nil)
|
||||||
content, err := jsonparser.GetString(rs.Payload, "content")
|
content, err := jsonparser.GetString(rs.Payload, "html_content")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Failed to read request body", http.StatusBadRequest)
|
http.Error(w, "Failed to read request body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/oarkflow/mq"
|
"github.com/oarkflow/mq"
|
||||||
"github.com/oarkflow/mq/consts"
|
"github.com/oarkflow/mq/consts"
|
||||||
|
"github.com/oarkflow/mq/jsonparser"
|
||||||
"github.com/oarkflow/mq/storage"
|
"github.com/oarkflow/mq/storage"
|
||||||
"github.com/oarkflow/mq/storage/memory"
|
"github.com/oarkflow/mq/storage/memory"
|
||||||
)
|
)
|
||||||
@@ -24,7 +25,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Ctx context.Context
|
Ctx context.Context `json:"-"`
|
||||||
Data json.RawMessage
|
Data json.RawMessage
|
||||||
Error error
|
Error error
|
||||||
Status TaskStatus
|
Status TaskStatus
|
||||||
@@ -188,6 +189,59 @@ func (tm *DAG) ProcessTask(ctx context.Context, payload []byte) Result {
|
|||||||
return <-resultCh
|
return <-resultCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tm *DAG) render(w http.ResponseWriter, request *http.Request) {
|
||||||
|
ctx, data, err := parse(request)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result := tm.ProcessTask(ctx, data)
|
||||||
|
if contentType, ok := result.Ctx.Value(consts.ContentType).(string); ok && contentType == consts.TypeHtml {
|
||||||
|
w.Header().Set(consts.ContentType, consts.TypeHtml)
|
||||||
|
data, err := jsonparser.GetString(result.Data, "content")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write([]byte(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *DAG) submit(ctx context.Context, payload []byte) Result {
|
||||||
|
var taskID string
|
||||||
|
userCtx := UserContext(ctx)
|
||||||
|
if val := userCtx.Get("task_id"); val != "" {
|
||||||
|
taskID = val
|
||||||
|
} else {
|
||||||
|
taskID = mq.NewID()
|
||||||
|
}
|
||||||
|
ctx = context.WithValue(ctx, "task_id", taskID)
|
||||||
|
userContext := UserContext(ctx)
|
||||||
|
next := userContext.Get("next")
|
||||||
|
manager, ok := tm.taskManager.Get(taskID)
|
||||||
|
resultCh := make(chan Result, 1)
|
||||||
|
if !ok {
|
||||||
|
manager = NewTaskManager(tm, resultCh)
|
||||||
|
tm.taskManager.Set(taskID, manager)
|
||||||
|
} else {
|
||||||
|
manager.resultCh = resultCh
|
||||||
|
}
|
||||||
|
if next == "true" {
|
||||||
|
nodes, err := tm.GetNextNodes(manager.currentNode)
|
||||||
|
if err != nil {
|
||||||
|
return Result{Error: err}
|
||||||
|
}
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
ctx = context.WithValue(ctx, "initial_node", nodes[0].ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstNode, err := tm.parseInitialNode(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return Result{Error: err}
|
||||||
|
}
|
||||||
|
manager.ProcessTask(ctx, taskID, firstNode, payload)
|
||||||
|
return <-resultCh
|
||||||
|
}
|
||||||
|
|
||||||
func (tm *DAG) formHandler(w http.ResponseWriter, r *http.Request) {
|
func (tm *DAG) formHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
http.ServeFile(w, r, "webroot/form.html")
|
http.ServeFile(w, r, "webroot/form.html")
|
||||||
@@ -221,25 +275,35 @@ func (tm *DAG) taskStatusHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, `{"message": "Invalid TaskID"}`, http.StatusNotFound)
|
http.Error(w, `{"message": "Invalid TaskID"}`, http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set(consts.ContentType, consts.TypeJson)
|
||||||
json.NewEncoder(w).Encode(manager.taskStates)
|
json.NewEncoder(w).Encode(manager.taskStates)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *DAG) Start(addr string) {
|
func (tm *DAG) Start(addr string) {
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, request *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, data, err := parse(request)
|
if r.Method == "GET" {
|
||||||
|
tm.render(w, r)
|
||||||
|
} else if r.Method == "POST" {
|
||||||
|
ctx, data, err := parse(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result := tm.ProcessTask(ctx, data)
|
result := tm.submit(ctx, data)
|
||||||
|
if result.Ctx == nil {
|
||||||
|
fmt.Println("Ctrl not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
if contentType, ok := result.Ctx.Value(consts.ContentType).(string); ok && contentType == consts.TypeHtml {
|
if contentType, ok := result.Ctx.Value(consts.ContentType).(string); ok && contentType == consts.TypeHtml {
|
||||||
w.Header().Set(consts.ContentType, consts.TypeHtml)
|
w.Header().Set(consts.ContentType, consts.TypeHtml)
|
||||||
w.Write(result.Data)
|
data, err := jsonparser.GetString(result.Data, "content")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write([]byte(data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
http.HandleFunc("/form", tm.formHandler)
|
|
||||||
http.HandleFunc("/result", tm.resultHandler)
|
|
||||||
http.HandleFunc("/task-result", tm.taskStatusHandler)
|
http.HandleFunc("/task-result", tm.taskStatusHandler)
|
||||||
http.ListenAndServe(addr, nil)
|
http.ListenAndServe(addr, nil)
|
||||||
}
|
}
|
||||||
|
@@ -200,6 +200,7 @@ func (tm *TaskManager) aggregateResults(parentNode string, taskID string) {
|
|||||||
} else if state.targetResults.Size() == 1 {
|
} else if state.targetResults.Size() == 1 {
|
||||||
state.Result = state.targetResults.Values()[0]
|
state.Result = state.targetResults.Values()[0]
|
||||||
}
|
}
|
||||||
|
tm.resultCh <- state.Result
|
||||||
tm.processFinalResult(taskID, state)
|
tm.processFinalResult(taskID, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -116,7 +116,7 @@ type Final struct {
|
|||||||
|
|
||||||
func (e *Final) ProcessTask(ctx context.Context, task *mq.Task) mq.Result {
|
func (e *Final) ProcessTask(ctx context.Context, task *mq.Task) mq.Result {
|
||||||
rs := map[string]any{
|
rs := map[string]any{
|
||||||
"content": `<strong>Processed successfully!</strong>`,
|
"html_content": `<strong>Processed successfully!</strong>`,
|
||||||
}
|
}
|
||||||
bt, _ := json.Marshal(rs)
|
bt, _ := json.Marshal(rs)
|
||||||
return mq.Result{Payload: bt, Ctx: ctx}
|
return mq.Result{Payload: bt, Ctx: ctx}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/oarkflow/jet"
|
"github.com/oarkflow/jet"
|
||||||
|
|
||||||
@@ -12,45 +13,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Form(ctx context.Context, payload json.RawMessage) v2.Result {
|
func Form(ctx context.Context, payload json.RawMessage) v2.Result {
|
||||||
template := `
|
bt, err := os.ReadFile("webroot/form.html")
|
||||||
<!DOCTYPE html>
|
if err != nil {
|
||||||
<html lang="en">
|
return v2.Result{Error: err, Ctx: ctx}
|
||||||
<head>
|
}
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>User Data Form</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Enter Your Information</h1>
|
|
||||||
<form action="/?task_id={{task_id}}&next=true" method="POST">
|
|
||||||
<label for="email">Email:</label><br>
|
|
||||||
<input type="email" id="email" name="email" value="s.baniya.np@gmail.com" required><br><br>
|
|
||||||
|
|
||||||
<label for="age">Age:</label><br>
|
|
||||||
<input type="number" id="age" name="age" value="18" required><br><br>
|
|
||||||
|
|
||||||
<label for="gender">Gender:</label><br>
|
|
||||||
<select id="gender" name="gender" required>
|
|
||||||
<option value="male">Male</option>
|
|
||||||
<option value="female">Female</option>
|
|
||||||
<option value="other">Other</option>
|
|
||||||
</select><br><br>
|
|
||||||
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
`
|
|
||||||
parser := jet.NewWithMemory(jet.WithDelims("{{", "}}"))
|
parser := jet.NewWithMemory(jet.WithDelims("{{", "}}"))
|
||||||
rs, err := parser.ParseTemplate(template, map[string]any{
|
rs, err := parser.ParseTemplate(string(bt), map[string]any{
|
||||||
"task_id": ctx.Value("task_id"),
|
"task_id": ctx.Value("task_id"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return v2.Result{Error: err, Ctx: ctx}
|
return v2.Result{Error: err, Ctx: ctx}
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, consts.ContentType, consts.TypeHtml)
|
ctx = context.WithValue(ctx, consts.ContentType, consts.TypeHtml)
|
||||||
return v2.Result{Data: []byte(rs), Ctx: ctx}
|
data := map[string]any{
|
||||||
|
"content": rs,
|
||||||
|
}
|
||||||
|
bt, _ = json.Marshal(data)
|
||||||
|
return v2.Result{Data: bt, Ctx: ctx}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NodeA(ctx context.Context, payload json.RawMessage) v2.Result {
|
func NodeA(ctx context.Context, payload json.RawMessage) v2.Result {
|
||||||
@@ -84,18 +63,26 @@ func NodeC(ctx context.Context, payload json.RawMessage) v2.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Result(ctx context.Context, payload json.RawMessage) v2.Result {
|
func Result(ctx context.Context, payload json.RawMessage) v2.Result {
|
||||||
|
bt, err := os.ReadFile("webroot/result.html")
|
||||||
|
if err != nil {
|
||||||
|
return v2.Result{Error: err, Ctx: ctx}
|
||||||
|
}
|
||||||
var data map[string]any
|
var data map[string]any
|
||||||
if err := json.Unmarshal(payload, &data); err != nil {
|
if err := json.Unmarshal(payload, &data); err != nil {
|
||||||
return v2.Result{Error: err, Ctx: ctx}
|
return v2.Result{Error: err, Ctx: ctx}
|
||||||
}
|
}
|
||||||
if templateFile, ok := data["html_content"].(string); ok {
|
if bt != nil {
|
||||||
parser := jet.NewWithMemory(jet.WithDelims("{{", "}}"))
|
parser := jet.NewWithMemory(jet.WithDelims("{{", "}}"))
|
||||||
rs, err := parser.ParseTemplate(templateFile, data)
|
rs, err := parser.ParseTemplate(string(bt), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return v2.Result{Error: err, Ctx: ctx}
|
return v2.Result{Error: err, Ctx: ctx}
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, consts.ContentType, consts.TypeHtml)
|
ctx = context.WithValue(ctx, consts.ContentType, consts.TypeHtml)
|
||||||
return v2.Result{Data: []byte(rs), Ctx: ctx}
|
data := map[string]any{
|
||||||
|
"content": rs,
|
||||||
|
}
|
||||||
|
bt, _ := json.Marshal(data)
|
||||||
|
return v2.Result{Data: bt, Ctx: ctx}
|
||||||
}
|
}
|
||||||
return v2.Result{Data: payload, Ctx: ctx}
|
return v2.Result{Data: payload, Ctx: ctx}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Enter Your Information</h1>
|
<h1>Enter Your Information</h1>
|
||||||
<form action="/form" method="POST">
|
<form action="/?task_id={{task_id}}&next=true" method="POST">
|
||||||
<label for="email">Email:</label><br>
|
<label for="email">Email:</label><br>
|
||||||
<input type="email" id="email" name="email" value="s.baniya.np@gmail.com" required><br><br>
|
<input type="email" id="email" name="email" value="s.baniya.np@gmail.com" required><br><br>
|
||||||
|
|
||||||
|
@@ -94,7 +94,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the task result
|
// Fetch the task result
|
||||||
const taskID = new URLSearchParams(window.location.search).get('taskID'); // Get taskID from URL
|
const taskID = new URLSearchParams(window.location.search).get('task_id'); // Get taskID from URL
|
||||||
if (taskID) {
|
if (taskID) {
|
||||||
fetch(`/task-result?taskID=${taskID}`)
|
fetch(`/task-result?taskID=${taskID}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
@@ -111,13 +111,13 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Task ID</th>
|
<th>Task ID</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Timestamp</th>
|
<th>UpdatedAt</th>
|
||||||
<th>Result Data</th>
|
<th>Result Data</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>${taskID}</td>
|
<td>${taskID}</td>
|
||||||
<td class="${getStatusClass(data.Result.Status)}">${data.Result.Status}</td>
|
<td class="${getStatusClass(data.Result.Status)}">${data.Result.Status}</td>
|
||||||
<td>${formatDate(data.Result.Timestamp)}</td>
|
<td>${formatDate(data.Result.UpdatedAt)}</td>
|
||||||
<td><pre>${JSON.stringify(data.Result.Result.Data, null, 2)}</pre></td>
|
<td><pre>${JSON.stringify(data.Result.Result.Data, null, 2)}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Node ID</th>
|
<th>Node ID</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Timestamp</th>
|
<th>UpdatedAt</th>
|
||||||
<th>Node Result Data</th>
|
<th>Node Result Data</th>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>${node.NodeID}</td>
|
<td>${node.NodeID}</td>
|
||||||
<td class="${getStatusClass(node.Status)}">${node.Status}</td>
|
<td class="${getStatusClass(node.Status)}">${node.Status}</td>
|
||||||
<td>${formatDate(node.Timestamp)}</td>
|
<td>${formatDate(node.UpdatedAt)}</td>
|
||||||
<td><pre>${JSON.stringify(node.Result.Data, null, 2)}</pre></td>
|
<td><pre>${JSON.stringify(node.Result.Data, null, 2)}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
|
Reference in New Issue
Block a user