Initial commit — Director app (API + UI)
This commit is contained in:
119
api/main.go
Normal file
119
api/main.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"director/api/db"
|
||||
"director/api/handlers"
|
||||
"director/api/pubsub"
|
||||
"director/api/ws"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dsn := os.Getenv("DATABASE_URL")
|
||||
if dsn == "" {
|
||||
dsn = "postgres://director:director_local_2026@127.0.0.1:5432/director?sslmode=disable"
|
||||
}
|
||||
|
||||
redisAddr := os.Getenv("REDIS_URL")
|
||||
if redisAddr == "" {
|
||||
redisAddr = "127.0.0.1:6379"
|
||||
}
|
||||
|
||||
database, err := db.Connect(dsn)
|
||||
if err != nil {
|
||||
log.Fatalf("database connection failed: %v", err)
|
||||
}
|
||||
defer database.Close()
|
||||
|
||||
hub := ws.NewHub()
|
||||
go hub.Run()
|
||||
|
||||
// Redis pub/sub — bridges external events (OpenClaw agents) to WebSocket clients
|
||||
ps := pubsub.New(redisAddr, hub)
|
||||
if err := ps.Ping(context.Background()); err != nil {
|
||||
log.Fatalf("redis connection failed: %v", err)
|
||||
}
|
||||
defer ps.Close()
|
||||
go ps.Subscribe(context.Background())
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
// CORS for local dev
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
|
||||
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
})
|
||||
|
||||
api := r.Group("/api")
|
||||
{
|
||||
h := handlers.New(database, ps)
|
||||
|
||||
// Plans
|
||||
api.GET("/plans", h.ListPlans)
|
||||
api.POST("/plans", h.CreatePlan)
|
||||
api.GET("/plans/:id", h.GetPlan)
|
||||
api.PATCH("/plans/:id", h.UpdatePlan)
|
||||
|
||||
// Tasks
|
||||
api.GET("/plans/:id/tasks", h.ListTasks)
|
||||
api.POST("/plans/:id/tasks", h.CreateTask)
|
||||
api.PATCH("/tasks/:id", h.UpdateTask)
|
||||
|
||||
// Agents
|
||||
api.GET("/agents", h.ListAgents)
|
||||
api.PATCH("/agents/:id", h.UpdateAgent)
|
||||
|
||||
// Director Queue
|
||||
api.GET("/queue", h.ListQueue)
|
||||
api.PATCH("/queue/:id", h.UpdateQueueItem)
|
||||
|
||||
// Pending Questions
|
||||
api.GET("/questions", h.ListQuestions)
|
||||
api.POST("/questions", h.CreateQuestion)
|
||||
api.PATCH("/questions/:id", h.AnswerQuestion)
|
||||
|
||||
// Messages
|
||||
api.GET("/plans/:id/messages", h.ListMessages)
|
||||
api.POST("/plans/:id/messages", h.CreateMessage)
|
||||
api.PATCH("/messages/:id", h.UpdateMessage)
|
||||
|
||||
// System / Settings
|
||||
api.GET("/system/claude-mode", h.GetClaudeMode)
|
||||
api.POST("/system/claude-mode", h.SetClaudeMode)
|
||||
}
|
||||
|
||||
// WebSocket
|
||||
r.GET("/ws", func(c *gin.Context) {
|
||||
ws.HandleWebSocket(hub, c.Writer, c.Request)
|
||||
})
|
||||
|
||||
// Health
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
if err := ps.Ping(c.Request.Context()); err != nil {
|
||||
c.JSON(500, gin.H{"status": "error", "redis": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"status": "ok"})
|
||||
})
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8090"
|
||||
}
|
||||
|
||||
log.Printf("Director API starting on :%s", port)
|
||||
if err := r.Run(":" + port); err != nil {
|
||||
log.Fatalf("server failed: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user