From 26e931913e713fa99940c9745b58d262cae79342 Mon Sep 17 00:00:00 2001 From: ibrah-m <139663719+ibrah-m@users.noreply.github.com> Date: Sun, 6 Jul 2025 19:04:21 -0400 Subject: [PATCH 01/13] Create initial API skeleton --- src/api.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/api.go diff --git a/src/api.go b/src/api.go new file mode 100644 index 0000000..3661190 --- /dev/null +++ b/src/api.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "net/http" +) + +func login(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, world!") +} + +func refresh(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, world!") +} + +func enqueueJob(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, world!") +} + +func getJobStatus(w http.ResponseWriter, r *http.Request) { + id := r.PathValue("id") + fmt.Println(id) + fmt.Fprintf(w, "Hello, world!") +} + +func main() { + router := http.NewServeMux() + + router.HandleFunc("POST /auth/login", login) + router.HandleFunc("POST /auth/refresh", refresh) + router.HandleFunc("POST /jobs", enqueueJob) + router.HandleFunc("GET /jobs/{id}/status", getJobStatus) + + server := http.Server{ + Addr: ":3000", + Handler: router, + } + + server.ListenAndServe() +} From 455d4610fa13e6f5361630a6d891ce4910efd8db Mon Sep 17 00:00:00 2001 From: ibrah-m <139663719+ibrah-m@users.noreply.github.com> Date: Mon, 14 Jul 2025 15:42:59 -0400 Subject: [PATCH 02/13] Add supabase-go as a dependency --- src/go.mod | 7 +++++++ src/go.sum | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/go.mod b/src/go.mod index b0ad159..1178ba5 100644 --- a/src/go.mod +++ b/src/go.mod @@ -20,10 +20,17 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d // indirect + github.com/supabase-community/gotrue-go v1.2.0 // indirect + github.com/supabase-community/postgrest-go v0.0.11 // indirect + github.com/supabase-community/storage-go v0.7.0 // indirect + github.com/supabase-community/supabase-go v0.0.4 // indirect + github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/otel v1.36.0 // indirect diff --git a/src/go.sum b/src/go.sum index f400861..a5dd5ea 100644 --- a/src/go.sum +++ b/src/go.sum @@ -26,6 +26,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -44,6 +46,18 @@ github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d h1:LOrsumaZy615ai37h9RjUIygpSubX+F+6rDct1LIag0= +github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d/go.mod h1:nnIju6x3+OZSojtGQCQzu0h3kv4HdIZk+UWCnNxtSak= +github.com/supabase-community/gotrue-go v1.2.0 h1:Zm7T5q3qbuwPgC6xyomOBKrSb7X5dvmjDZEmNST7MoE= +github.com/supabase-community/gotrue-go v1.2.0/go.mod h1:86DXBiAUNcbCfgbeOPEh0PQxScLfowUbYgakETSFQOw= +github.com/supabase-community/postgrest-go v0.0.11 h1:717GTUMfLJxSBuAeEQG2MuW5Q62Id+YrDjvjprTSErg= +github.com/supabase-community/postgrest-go v0.0.11/go.mod h1:cw6LfzMyK42AOSBA1bQ/HZ381trIJyuui2GWhraW7Cc= +github.com/supabase-community/storage-go v0.7.0 h1:cJ8HLbbnL54H5rHPtHfiwtpRwcbDfA3in9HL/ucHnqA= +github.com/supabase-community/storage-go v0.7.0/go.mod h1:oBKcJf5rcUXy3Uj9eS5wR6mvpwbmvkjOtAA+4tGcdvQ= +github.com/supabase-community/supabase-go v0.0.4 h1:sxMenbq6N8a3z9ihNpN3lC2FL3E1YuTQsjX09VPRp+U= +github.com/supabase-community/supabase-go v0.0.4/go.mod h1:SSHsXoOlc+sq8XeXaf0D3gE2pwrq5bcUfzm0+08u/o8= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= From 6159d9ec0e07f31d87c163120541e5c8fb8f14b2 Mon Sep 17 00:00:00 2001 From: ibrah-m <139663719+ibrah-m@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:13:49 -0400 Subject: [PATCH 03/13] init scheduler and supervisor --- src/api.go | 142 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 123 insertions(+), 19 deletions(-) diff --git a/src/api.go b/src/api.go index 3661190..e2556c0 100644 --- a/src/api.go +++ b/src/api.go @@ -1,40 +1,144 @@ package main import ( + "context" "fmt" + "log" "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/redis/go-redis/v9" ) -func login(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, world!") +type App struct { + redisClient *redis.Client + scheduler *Scheduler + supervisor *Supervisor + httpServer *http.Server + wg sync.WaitGroup } -func refresh(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, world!") +func NewApp(redisAddr, gpuType string) *App { + client := redis.NewClient(&redis.Options{Addr: redisAddr}) + scheduler := NewScheduler(redisAddr) + + consumerID := fmt.Sprintf("worker_%d", os.Getpid()) + supervisor := NewSupervisor(redisAddr, consumerID, gpuType) + + mux := http.NewServeMux() + a := &App{ + redisClient: client, + scheduler: scheduler, + supervisor: supervisor, + httpServer: &http.Server{Addr: ":3000", Handler: mux}, + } + + mux.HandleFunc("/auth/login", a.login) + mux.HandleFunc("/auth/refresh", a.refresh) + mux.HandleFunc("/jobs", a.enqueueJob) + mux.HandleFunc("/jobs/status", a.getJobStatus) + + return a } -func enqueueJob(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, world!") +func (a *App) Start() error { + // Connect to redis + if err := a.redisClient.Ping(context.Background()).Err(); err != nil { + return fmt.Errorf("redis ping failed: %w", err) + } + + // Start Supervisor + if err := a.supervisor.Start(); err != nil { + return fmt.Errorf("supervisor start failed: %w", err) + } + + // Launch HTTP server + a.wg.Add(1) + go func() { + defer a.wg.Done() + log.Println("HTTP server listening on", a.httpServer.Addr) + if err := a.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("HTTP server error: %v", err) + } + }() + + return nil } -func getJobStatus(w http.ResponseWriter, r *http.Request) { - id := r.PathValue("id") - fmt.Println(id) - fmt.Fprintf(w, "Hello, world!") +func (a *App) Shutdown(ctx context.Context) error { + if err := a.httpServer.Shutdown(ctx); err != nil { + log.Printf("error shutting down HTTP server: %v", err) + } + + // Wait for ListenAndServe goroutine to finish + a.wg.Wait() + + a.supervisor.Stop() + + if err := a.scheduler.Close(); err != nil { + log.Printf("error closing scheduler: %v", err) + } + + if err := a.redisClient.Close(); err != nil { + log.Printf("error closing redis client: %v", err) + } + + return nil } func main() { - router := http.NewServeMux() + app := NewApp("localhost:6379", "AMD") + + if err := app.Start(); err != nil { + log.Fatalf("failed to start app: %v", err) + } + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + <-ctx.Done() + log.Println("shutdown signal received") + + shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + if err := app.Shutdown(shutdownCtx); err != nil { + log.Fatalf("shutdown error: %v", err) + } - router.HandleFunc("POST /auth/login", login) - router.HandleFunc("POST /auth/refresh", refresh) - router.HandleFunc("POST /jobs", enqueueJob) - router.HandleFunc("GET /jobs/{id}/status", getJobStatus) + log.Println("all services stopped cleanly") +} + +func (a *App) login(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + val, err := a.redisClient.Get(ctx, "some:key").Result() + if err != nil && err != redis.Nil { + http.Error(w, "redis error", http.StatusInternalServerError) + return + } + fmt.Fprintf(w, "login page; redis says: %q\n", val) +} - server := http.Server{ - Addr: ":3000", - Handler: router, +func (a *App) refresh(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, world!\n") +} + +func (a *App) enqueueJob(w http.ResponseWriter, r *http.Request) { + payload := map[string]interface{}{ + "task_id": 123, + "data": "test_data_123", } + if err := a.scheduler.Enqueue("jobType", payload); err != nil { + http.Error(w, "enqueue failed", http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, "enqueued") +} - server.ListenAndServe() +func (a *App) getJobStatus(w http.ResponseWriter, r *http.Request) { + id := r.URL.Query().Get("id") + fmt.Fprintln(w, "job id=", id) } From 771c81fef2deeb366b2fcfc573f7b09c9aa6ac62 Mon Sep 17 00:00:00 2001 From: blobcode Date: Sat, 2 Aug 2025 22:57:25 -0400 Subject: [PATCH 04/13] Clean up go.mod --- src/go.mod | 32 +----------------- src/go.sum | 99 +++--------------------------------------------------- 2 files changed, 5 insertions(+), 126 deletions(-) diff --git a/src/go.mod b/src/go.mod index 1178ba5..54b5958 100644 --- a/src/go.mod +++ b/src/go.mod @@ -2,39 +2,9 @@ module mist go 1.24.3 -require ( - github.com/docker/docker v28.2.2+incompatible - github.com/redis/go-redis/v9 v9.10.0 -) +require github.com/redis/go-redis/v9 v9.10.0 require ( - github.com/Microsoft/go-winio v0.4.14 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d // indirect - github.com/supabase-community/gotrue-go v1.2.0 // indirect - github.com/supabase-community/postgrest-go v0.0.11 // indirect - github.com/supabase-community/storage-go v0.7.0 // indirect - github.com/supabase-community/supabase-go v0.0.4 // indirect - github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.36.0 // indirect - go.opentelemetry.io/otel/metric v1.36.0 // indirect - go.opentelemetry.io/otel/trace v1.36.0 // indirect - golang.org/x/sys v0.33.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index a5dd5ea..010617d 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,101 +1,10 @@ -github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= -github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= -github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs= github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d h1:LOrsumaZy615ai37h9RjUIygpSubX+F+6rDct1LIag0= -github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d/go.mod h1:nnIju6x3+OZSojtGQCQzu0h3kv4HdIZk+UWCnNxtSak= -github.com/supabase-community/gotrue-go v1.2.0 h1:Zm7T5q3qbuwPgC6xyomOBKrSb7X5dvmjDZEmNST7MoE= -github.com/supabase-community/gotrue-go v1.2.0/go.mod h1:86DXBiAUNcbCfgbeOPEh0PQxScLfowUbYgakETSFQOw= -github.com/supabase-community/postgrest-go v0.0.11 h1:717GTUMfLJxSBuAeEQG2MuW5Q62Id+YrDjvjprTSErg= -github.com/supabase-community/postgrest-go v0.0.11/go.mod h1:cw6LfzMyK42AOSBA1bQ/HZ381trIJyuui2GWhraW7Cc= -github.com/supabase-community/storage-go v0.7.0 h1:cJ8HLbbnL54H5rHPtHfiwtpRwcbDfA3in9HL/ucHnqA= -github.com/supabase-community/storage-go v0.7.0/go.mod h1:oBKcJf5rcUXy3Uj9eS5wR6mvpwbmvkjOtAA+4tGcdvQ= -github.com/supabase-community/supabase-go v0.0.4 h1:sxMenbq6N8a3z9ihNpN3lC2FL3E1YuTQsjX09VPRp+U= -github.com/supabase-community/supabase-go v0.0.4/go.mod h1:SSHsXoOlc+sq8XeXaf0D3gE2pwrq5bcUfzm0+08u/o8= -github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= -github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= -go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= -go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= -go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= -go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= -go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 13d9db9a20c19c853d2b29a274ae61a830b50044 Mon Sep 17 00:00:00 2001 From: blobcode Date: Sat, 2 Aug 2025 23:04:45 -0400 Subject: [PATCH 05/13] Fix Supervisor Start --- src/api.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/api.go b/src/api.go index e2556c0..c1946a9 100644 --- a/src/api.go +++ b/src/api.go @@ -50,12 +50,7 @@ func (a *App) Start() error { if err := a.redisClient.Ping(context.Background()).Err(); err != nil { return fmt.Errorf("redis ping failed: %w", err) } - - // Start Supervisor - if err := a.supervisor.Start(); err != nil { - return fmt.Errorf("supervisor start failed: %w", err) - } - + // Launch HTTP server a.wg.Add(1) go func() { @@ -114,7 +109,7 @@ func main() { func (a *App) login(w http.ResponseWriter, r *http.Request) { ctx := r.Context() val, err := a.redisClient.Get(ctx, "some:key").Result() - if err != nil && err != redis.Nil { + if err != nil || err != redis.Nil { http.Error(w, "redis error", http.StatusInternalServerError) return } From bf7a869c041f7308a25e68464f7f0165536a8ec2 Mon Sep 17 00:00:00 2001 From: blobcode Date: Sat, 2 Aug 2025 23:14:57 -0400 Subject: [PATCH 06/13] add oauth2 server --- src/go.mod | 19 ++++++ src/go.sum | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) diff --git a/src/go.mod b/src/go.mod index 54b5958..cecc68a 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,6 +5,25 @@ go 1.24.3 require github.com/redis/go-redis/v9 v9.10.0 require ( + github.com/bytedance/gopkg v0.1.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/go-oauth2/oauth2/v4 v4.5.3 // indirect + github.com/go-session/session/v3 v3.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/tidwall/btree v1.8.0 // indirect + github.com/tidwall/buntdb v1.3.2 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/grect v0.1.4 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/rtred v0.1.2 // indirect + github.com/tidwall/rtree v1.10.0 // indirect + github.com/tidwall/tinyqueue v0.1.1 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/src/go.sum b/src/go.sum index 010617d..ec922fc 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,10 +1,199 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/gopkg v0.0.0-20221122125632-68358b8ecec6/go.mod h1:5FoAH5xUHHCMDvQPy1rnj8moqLkLHFaDVBjHhcFwEi0= +github.com/bytedance/gopkg v0.1.2 h1:8o2feYuxknDpN+O7kPwvSXfMEKfYvJYiA2K7aonoMEQ= +github.com/bytedance/gopkg v0.1.2/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/go-oauth2/oauth2/v4 v4.5.3 h1:lQt7O9KOnu/v4awe166FH7+p8tFUXQyR+no6nctAKU0= +github.com/go-oauth2/oauth2/v4 v4.5.3/go.mod h1:ryzb7zr8fdQBlciD0+tcnEWeOok5B0J8V/DniwYqQ2k= +github.com/go-session/session/v3 v3.2.1 h1:APQf5JFW84+bhbqRjEZO8J+IppSgT1jMQTFI/XVyIFY= +github.com/go-session/session/v3 v3.2.1/go.mod h1:RftEBbyuzqkNCAxIrCLJe+rfBqB/4G11qxq9KYKrx4M= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs= github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= +github.com/tidwall/btree v1.8.0 h1:kHHy8hSBauQUe0KPHMFLOt0olAj1nDnkHPJhr8+HFkM= +github.com/tidwall/btree v1.8.0/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= +github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI= +github.com/tidwall/buntdb v1.3.2 h1:qd+IpdEGs0pZci37G4jF51+fSKlkuUTMXuHhXL1AkKg= +github.com/tidwall/buntdb v1.3.2/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= +github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M= +github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= +github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= +github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= +github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao= +github.com/tidwall/rtree v1.10.0 h1:+EcI8fboEaW1L3/9oW/6AMoQ8HiEIHyR7bQOGnmz4Mg= +github.com/tidwall/rtree v1.10.0/go.mod h1:iDJQ9NBRtbfKkzZu02za+mIlaP+bjYPnunbSNidpbCQ= +github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ= +github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= +github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From c17ba16db9598b4e4305aa147691c2799c1e550a Mon Sep 17 00:00:00 2001 From: blobcode Date: Sat, 2 Aug 2025 23:24:38 -0400 Subject: [PATCH 07/13] remove supervisor bits --- src/api.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/api.go b/src/api.go index c1946a9..a5e63d5 100644 --- a/src/api.go +++ b/src/api.go @@ -17,7 +17,6 @@ import ( type App struct { redisClient *redis.Client scheduler *Scheduler - supervisor *Supervisor httpServer *http.Server wg sync.WaitGroup } @@ -27,13 +26,11 @@ func NewApp(redisAddr, gpuType string) *App { scheduler := NewScheduler(redisAddr) consumerID := fmt.Sprintf("worker_%d", os.Getpid()) - supervisor := NewSupervisor(redisAddr, consumerID, gpuType) mux := http.NewServeMux() a := &App{ redisClient: client, scheduler: scheduler, - supervisor: supervisor, httpServer: &http.Server{Addr: ":3000", Handler: mux}, } @@ -72,8 +69,6 @@ func (a *App) Shutdown(ctx context.Context) error { // Wait for ListenAndServe goroutine to finish a.wg.Wait() - a.supervisor.Stop() - if err := a.scheduler.Close(); err != nil { log.Printf("error closing scheduler: %v", err) } From 6815364ede6a7e5ca6710af98cbf8c7f105907bf Mon Sep 17 00:00:00 2001 From: blobcode Date: Sun, 3 Aug 2025 00:07:12 -0400 Subject: [PATCH 08/13] Create Basic Oauth Handler --- src/api.go | 50 +++++++++++++++++++++++++++++++++++++------------- src/go.mod | 6 ++++-- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/api.go b/src/api.go index a5e63d5..17fe2fe 100644 --- a/src/api.go +++ b/src/api.go @@ -11,6 +11,10 @@ import ( "syscall" "time" + "github.com/go-oauth2/oauth2/v4/manage" + "github.com/go-oauth2/oauth2/v4/models" + "github.com/go-oauth2/oauth2/v4/server" + "github.com/go-oauth2/oauth2/v4/store" "github.com/redis/go-redis/v9" ) @@ -18,6 +22,8 @@ type App struct { redisClient *redis.Client scheduler *Scheduler httpServer *http.Server + manager *manage.Manager + srv *server.Server wg sync.WaitGroup } @@ -25,17 +31,33 @@ func NewApp(redisAddr, gpuType string) *App { client := redis.NewClient(&redis.Options{Addr: redisAddr}) scheduler := NewScheduler(redisAddr) - consumerID := fmt.Sprintf("worker_%d", os.Getpid()) + manager := manage.NewDefaultManager() + // token memory store + manager.MustTokenStorage(store.NewMemoryTokenStore()) + + // client memory store + clientStore := store.NewClientStore() + clientStore.Set("000000", &models.Client{ + ID: "000000", + Secret: "999999", + Domain: "http://localhost:3000", // replace with environment domain + }) + manager.MapClientStorage(clientStore) + srv := CreateServer(manager) mux := http.NewServeMux() a := &App{ redisClient: client, scheduler: scheduler, + manager: manager, + srv: srv, httpServer: &http.Server{Addr: ":3000", Handler: mux}, } - mux.HandleFunc("/auth/login", a.login) - mux.HandleFunc("/auth/refresh", a.refresh) + // auth routes + mux.HandleFunc("/oauth/authorize", a.authorize) + mux.HandleFunc("/oauth/token", a.token) + mux.HandleFunc("/jobs", a.enqueueJob) mux.HandleFunc("/jobs/status", a.getJobStatus) @@ -47,7 +69,7 @@ func (a *App) Start() error { if err := a.redisClient.Ping(context.Background()).Err(); err != nil { return fmt.Errorf("redis ping failed: %w", err) } - + // Launch HTTP server a.wg.Add(1) go func() { @@ -101,18 +123,20 @@ func main() { log.Println("all services stopped cleanly") } -func (a *App) login(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - val, err := a.redisClient.Get(ctx, "some:key").Result() - if err != nil || err != redis.Nil { - http.Error(w, "redis error", http.StatusInternalServerError) - return +func (a *App) authorize(w http.ResponseWriter, r *http.Request) { + err := a.srv.HandleAuthorizeRequest(w, r) + if err != nil { + log.Printf("Authorize error: %v", err) + http.Error(w, err.Error(), http.StatusBadRequest) } - fmt.Fprintf(w, "login page; redis says: %q\n", val) } -func (a *App) refresh(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, world!\n") +func (a *App) token(w http.ResponseWriter, r *http.Request) { + err := a.srv.HandleTokenRequest(w, r) + if err != nil { + log.Printf("Token error: %v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + } } func (a *App) enqueueJob(w http.ResponseWriter, r *http.Request) { diff --git a/src/go.mod b/src/go.mod index cecc68a..7a9d3fc 100644 --- a/src/go.mod +++ b/src/go.mod @@ -2,13 +2,15 @@ module mist go 1.24.3 -require github.com/redis/go-redis/v9 v9.10.0 +require ( + github.com/go-oauth2/oauth2/v4 v4.5.3 + github.com/redis/go-redis/v9 v9.10.0 +) require ( github.com/bytedance/gopkg v0.1.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/go-oauth2/oauth2/v4 v4.5.3 // indirect github.com/go-session/session/v3 v3.2.1 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/protobuf v1.5.4 // indirect From 3e038df2f11326e655263f7303afb0dc6eca039d Mon Sep 17 00:00:00 2001 From: blobcode Date: Sun, 3 Aug 2025 01:10:20 -0400 Subject: [PATCH 09/13] Add User Registration --- src/api.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/auth.go | 33 +++++++++++++++++++ src/go.mod | 2 ++ src/go.sum | 4 +++ src/user.go | 71 ++++++++++++++++++++++++++++++++++++++++ src/util.go | 14 ++++++++ 6 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 src/auth.go create mode 100644 src/user.go diff --git a/src/api.go b/src/api.go index 17fe2fe..5b5aff0 100644 --- a/src/api.go +++ b/src/api.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "fmt" "log" "net/http" @@ -18,6 +19,16 @@ import ( "github.com/redis/go-redis/v9" ) +type LoginRequest struct { + Email string `json:"email"` + Password string `json:"password"` +} + +type RegisterRequest struct { + Email string `json:"email"` + Password string `json:"password"` +} + type App struct { redisClient *redis.Client scheduler *Scheduler @@ -32,10 +43,8 @@ func NewApp(redisAddr, gpuType string) *App { scheduler := NewScheduler(redisAddr) manager := manage.NewDefaultManager() - // token memory store manager.MustTokenStorage(store.NewMemoryTokenStore()) - // client memory store clientStore := store.NewClientStore() clientStore.Set("000000", &models.Client{ ID: "000000", @@ -55,9 +64,12 @@ func NewApp(redisAddr, gpuType string) *App { } // auth routes + mux.HandleFunc("/auth/register", a.register) + mux.HandleFunc("/auth/login", a.login) + mux.HandleFunc("/oauth/authorize", a.authorize) mux.HandleFunc("/oauth/token", a.token) - + mux.HandleFunc("/jobs", a.enqueueJob) mux.HandleFunc("/jobs/status", a.getJobStatus) @@ -123,6 +135,81 @@ func main() { log.Println("all services stopped cleanly") } +func (a *App) register(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + a.jsonResponse(w, http.StatusMethodNotAllowed, APIResponse{ + Success: false, + Error: "Method not allowed", + }) + return + } + + var req RegisterRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + a.jsonResponse(w, http.StatusBadRequest, APIResponse{ + Success: false, + Error: "Invalid JSON", + }) + return + } + + if req.Email == "" || req.Password == "" { + a.jsonResponse(w, http.StatusBadRequest, APIResponse{ + Success: false, + Error: "Username and password required", + }) + return + } + + // Check if user exists + if _, err := a.getUserByEmail(req.Email); err == nil { + a.jsonResponse(w, http.StatusConflict, APIResponse{ + Success: false, + Error: "Username already exists", + }) + return + } + + user, err := a.createUser(req.Email, req.Password) + if err != nil { + log.Printf("Failed to create user: %v", err) + a.jsonResponse(w, http.StatusInternalServerError, APIResponse{ + Success: false, + Error: "Failed to create user", + }) + return + } + + a.jsonResponse(w, http.StatusCreated, APIResponse{ + Success: true, + Data: map[string]string{ + "user_id": user.ID, + "email": user.Email, + }, + }) +} + +func (a *App) login(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + a.jsonResponse(w, http.StatusMethodNotAllowed, APIResponse{ + Success: false, + Error: "Method not allowed", + }) + return + } + + var req LoginRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + a.jsonResponse(w, http.StatusBadRequest, APIResponse{ + Success: false, + Error: "Invalid JSON", + }) + return + } + + // TODO: login methods +} + func (a *App) authorize(w http.ResponseWriter, r *http.Request) { err := a.srv.HandleAuthorizeRequest(w, r) if err != nil { diff --git a/src/auth.go b/src/auth.go new file mode 100644 index 0000000..03f7cc1 --- /dev/null +++ b/src/auth.go @@ -0,0 +1,33 @@ +package main + +import ( + "log" + "net/http" + + "github.com/go-oauth2/oauth2/v4/errors" + "github.com/go-oauth2/oauth2/v4/manage" + "github.com/go-oauth2/oauth2/v4/server" +) + +func CreateServer(manager *manage.Manager) *server.Server { + srv := server.NewDefaultServer(manager) + srv.SetAllowGetAccessRequest(true) + srv.SetClientInfoHandler(server.ClientFormHandler) + + srv.UserAuthorizationHandler = func(w http.ResponseWriter, r *http.Request) (userID string, err error) { + return "000000", nil + } + + // Error handler + srv.SetInternalErrorHandler(func(err error) (re *errors.Response) { + log.Println("Internal Error:", err.Error()) + return + }) + + // Response error handler + srv.SetResponseErrorHandler(func(re *errors.Response) { + log.Println("Response Error:", re.Error.Error()) + }) + + return srv +} diff --git a/src/go.mod b/src/go.mod index 7a9d3fc..736de3c 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,6 +5,8 @@ go 1.24.3 require ( github.com/go-oauth2/oauth2/v4 v4.5.3 github.com/redis/go-redis/v9 v9.10.0 + github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 + golang.org/x/crypto v0.40.0 ) require ( diff --git a/src/go.sum b/src/go.sum index ec922fc..5199873 100644 --- a/src/go.sum +++ b/src/go.sum @@ -79,6 +79,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI= +github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI= github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= github.com/tidwall/btree v1.8.0 h1:kHHy8hSBauQUe0KPHMFLOt0olAj1nDnkHPJhr8+HFkM= github.com/tidwall/btree v1.8.0/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= @@ -121,6 +123,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/src/user.go b/src/user.go new file mode 100644 index 0000000..dec0069 --- /dev/null +++ b/src/user.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "encoding/json" + "github.com/teris-io/shortid" + "golang.org/x/crypto/bcrypt" +) + +type User struct { + ID string `json:"id"` + Email string `json:"email"` + Password string `json:"password"` +} + +func (a *App) getUser(userID string) (*User, error) { + ctx := context.Background() + userData, err := a.redisClient.Get(ctx, "user:"+userID).Result() + if err != nil { + return nil, err + } + + var user User + if err := json.Unmarshal([]byte(userData), &user); err != nil { + return nil, err + } + + return &user, nil +} + +func (a *App) getUserByEmail(email string) (*User, error) { + ctx := context.Background() + userID, err := a.redisClient.Get(ctx, "email:"+email).Result() + if err != nil { + return nil, err + } + return a.getUser(userID) +} + +func (a *App) createUser(email, password string) (*User, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return nil, err + } + + userID, err := shortid.Generate() + if err != nil { + return nil, err + } + + user := &User{ + ID: userID, + Email: email, + Password: string(hashedPassword), + } + + userData, err := json.Marshal(user) + if err != nil { + return nil, err + } + + ctx := context.Background() + if err := a.redisClient.Set(ctx, "user:"+userID, userData, 0).Err(); err != nil { + return nil, err + } + if err := a.redisClient.Set(ctx, "email:"+email, userID, 0).Err(); err != nil { + return nil, err + } + + return user, nil +} diff --git a/src/util.go b/src/util.go index 9a2e05c..df844a1 100644 --- a/src/util.go +++ b/src/util.go @@ -1,7 +1,9 @@ package main import ( + "encoding/json" "fmt" + "net/http" "os" "time" ) @@ -13,6 +15,12 @@ const ( RetryDelay = 5 * time.Second ) +type APIResponse struct { + Success bool `json:"success"` + Data interface{} `json:"data,omitempty"` + Error string `json:"error,omitempty"` +} + type Job struct { ID string `json:"id"` Type string `json:"type"` @@ -25,3 +33,9 @@ type Job struct { func generateJobID() string { return fmt.Sprintf("job_%d_%d", time.Now().UnixNano(), os.Getpid()) } + +func (a *App) jsonResponse(w http.ResponseWriter, statusCode int, response APIResponse) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) + json.NewEncoder(w).Encode(response) +} From 20c1c98f08f6a12858d123836025e71a74ab83af Mon Sep 17 00:00:00 2001 From: blobcode Date: Wed, 6 Aug 2025 14:07:47 -0400 Subject: [PATCH 10/13] Add Oauth2 Authorization Code + Login Page --- src/api.go | 208 ++++++++++++++++++++++++++++++++++++++++++++++------ src/auth.go | 118 ++++++++++++++++++++++++----- src/user.go | 2 + src/util.go | 8 ++ 4 files changed, 294 insertions(+), 42 deletions(-) diff --git a/src/api.go b/src/api.go index 5b5aff0..9c1896c 100644 --- a/src/api.go +++ b/src/api.go @@ -6,8 +6,10 @@ import ( "fmt" "log" "net/http" + "net/http/httputil" "os" "os/signal" + "strings" "sync" "syscall" "time" @@ -17,6 +19,7 @@ import ( "github.com/go-oauth2/oauth2/v4/server" "github.com/go-oauth2/oauth2/v4/store" "github.com/redis/go-redis/v9" + "golang.org/x/crypto/bcrypt" ) type LoginRequest struct { @@ -43,16 +46,20 @@ func NewApp(redisAddr, gpuType string) *App { scheduler := NewScheduler(redisAddr) manager := manage.NewDefaultManager() - manager.MustTokenStorage(store.NewMemoryTokenStore()) + manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg) + manager.MustTokenStorage(store.NewMemoryTokenStore()) // TODO: move to redis? clientStore := store.NewClientStore() - clientStore.Set("000000", &models.Client{ - ID: "000000", - Secret: "999999", + clientStore.Set("client", &models.Client{ + ID: "client", + Secret: "secret", // replace this with actual secret Domain: "http://localhost:3000", // replace with environment domain }) manager.MapClientStorage(clientStore) - srv := CreateServer(manager) + + srv := server.NewDefaultServer(manager) + srv.SetAllowGetAccessRequest(true) + srv.SetClientInfoHandler(server.ClientFormHandler) mux := http.NewServeMux() a := &App{ @@ -73,6 +80,8 @@ func NewApp(redisAddr, gpuType string) *App { mux.HandleFunc("/jobs", a.enqueueJob) mux.HandleFunc("/jobs/status", a.getJobStatus) + srv.UserAuthorizationHandler = a.UserAuthorizationHandler + return a } @@ -156,7 +165,7 @@ func (a *App) register(w http.ResponseWriter, r *http.Request) { if req.Email == "" || req.Password == "" { a.jsonResponse(w, http.StatusBadRequest, APIResponse{ Success: false, - Error: "Username and password required", + Error: "Email and password required", }) return } @@ -165,7 +174,7 @@ func (a *App) register(w http.ResponseWriter, r *http.Request) { if _, err := a.getUserByEmail(req.Email); err == nil { a.jsonResponse(w, http.StatusConflict, APIResponse{ Success: false, - Error: "Username already exists", + Error: "Email already exists", }) return } @@ -189,7 +198,68 @@ func (a *App) register(w http.ResponseWriter, r *http.Request) { }) } +func (a *App) authorize(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + sessionID := r.Header.Get("Authorization") + if strings.HasPrefix(sessionID, "Session ") { + sessionID = strings.TrimPrefix(sessionID, "Session ") + } + if cookie, err := r.Cookie("session"); err == nil { + sessionID = cookie.Value + } + + log.Println(sessionID) + + if sessionID == "" { + log.Println("a") + a.redirectToLogin(w, r) + return + } + if _, err := a.getSession(sessionID); err != nil { + log.Println("b") + a.redirectToLogin(w, r) + return + } + + err := a.srv.HandleAuthorizeRequest(w, r) + if err != nil { + log.Printf("Authorize error: %v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + } + return + } + + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) +} + +func (a *App) token(w http.ResponseWriter, r *http.Request) { + err := a.srv.HandleTokenRequest(w, r) + if err != nil { + log.Printf("Token error: %v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + } +} + func (a *App) login(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + if sessionID := a.getSessionFromRequest(r); sessionID != "" { + if _, err := a.getSession(sessionID); err == nil { + // user is already logged in, redirect them + redirectURL := r.URL.Query().Get("redirect") + if redirectURL == "" { + redirectURL = "/" // default + } + + http.Redirect(w, r, redirectURL, http.StatusFound) + return + } + } + + + a.showLoginPage(w, r) + return + } + if r.Method != "POST" { a.jsonResponse(w, http.StatusMethodNotAllowed, APIResponse{ Success: false, @@ -198,32 +268,124 @@ func (a *App) login(w http.ResponseWriter, r *http.Request) { return } - var req LoginRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - a.jsonResponse(w, http.StatusBadRequest, APIResponse{ + // Check if this is a form submission or api + contentType := r.Header.Get("Content-Type") + isFormData := strings.Contains(contentType, "application/x-www-form-urlencoded") || contentType == "" + + var email, password string + var err error + + if isFormData { + email = r.FormValue("email") + password = r.FormValue("password") + if email == "" || password == "" { + a.showLoginPage(w, r) + return + } + } else { + var req LoginRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + a.jsonResponse(w, http.StatusBadRequest, APIResponse{ + Success: false, + Error: "Invalid JSON", + }) + return + } + email = req.Email + password = req.Password + } + + user, err := a.getUserByEmail(email) + if err != nil { + if isFormData { + a.showLoginPage(w, r) + return + } + a.jsonResponse(w, http.StatusUnauthorized, APIResponse{ Success: false, - Error: "Invalid JSON", + Error: "Invalid Email / Password", }) return } - // TODO: login methods -} - -func (a *App) authorize(w http.ResponseWriter, r *http.Request) { - err := a.srv.HandleAuthorizeRequest(w, r) + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) if err != nil { - log.Printf("Authorize error: %v", err) - http.Error(w, err.Error(), http.StatusBadRequest) + if isFormData { + a.showLoginPage(w, r) + return + } + a.jsonResponse(w, http.StatusUnauthorized, APIResponse{ + Success: false, + Error: "Invalid Email / Password", + }) + return } -} -func (a *App) token(w http.ResponseWriter, r *http.Request) { - err := a.srv.HandleTokenRequest(w, r) + sessionID, err := a.CreateSession(user.ID) if err != nil { - log.Printf("Token error: %v", err) - http.Error(w, err.Error(), http.StatusBadRequest) + if isFormData { + a.showLoginPage(w, r) + return + } + a.jsonResponse(w, http.StatusInternalServerError, APIResponse{ + Success: false, + Error: "Error Creating Session", + }) + return } + + if isFormData { + http.SetCookie(w, &http.Cookie{ + Name: "session", + Path: "/", + Value: sessionID, + HttpOnly: true, + Secure: false, // TODO: change in prod + SameSite: http.SameSiteLaxMode, + }) + + redirectURL := r.FormValue("redirect") + log.Println(redirectURL) + if redirectURL == "" { + redirectURL = "/" // Default redirect + } + http.Redirect(w, r, redirectURL, http.StatusFound) + return + } + + a.jsonResponse(w, http.StatusAccepted, APIResponse{ + Success: true, + Data: map[string]any{ + "session": sessionID, + }, + }) +} + +// TODO: move this to a file? +func (a *App) showLoginPage(w http.ResponseWriter, r *http.Request) { + redirectURL := r.URL.Query().Get("redirect") + + html := fmt.Sprintf(` + + + +
+ + +
+ + +
+
+ + +
+ +
+ +`, redirectURL) + w.Header().Set("Content-Type", "text/html") + fmt.Fprint(w, html) } func (a *App) enqueueJob(w http.ResponseWriter, r *http.Request) { diff --git a/src/auth.go b/src/auth.go index 03f7cc1..bd97dec 100644 --- a/src/auth.go +++ b/src/auth.go @@ -1,33 +1,113 @@ package main import ( + "context" + "fmt" "log" "net/http" + "strings" - "github.com/go-oauth2/oauth2/v4/errors" - "github.com/go-oauth2/oauth2/v4/manage" - "github.com/go-oauth2/oauth2/v4/server" + "github.com/teris-io/shortid" ) -func CreateServer(manager *manage.Manager) *server.Server { - srv := server.NewDefaultServer(manager) - srv.SetAllowGetAccessRequest(true) - srv.SetClientInfoHandler(server.ClientFormHandler) +// creates a user session in redis +func (a *App) CreateSession(uid string) (string, error) { + id, err := shortid.Generate() + if err != nil { + return "", err + } + + ctx := context.Background() + if err := a.redisClient.Set(ctx, "session:"+id, uid, 0).Err(); err != nil { + return "", err + } - srv.UserAuthorizationHandler = func(w http.ResponseWriter, r *http.Request) (userID string, err error) { - return "000000", nil + return id, nil +} + +func (a *App) getSession(session_id string) (string, error) { + ctx := context.Background() + uid, err := a.redisClient.Get(ctx, "session:"+session_id).Result() + if err != nil { + return "", err } - // Error handler - srv.SetInternalErrorHandler(func(err error) (re *errors.Response) { - log.Println("Internal Error:", err.Error()) - return - }) + return uid, nil +} + +func (a *App) getSessionFromRequest(r *http.Request) string { + authHeader := r.Header.Get("Authorization") + log.Println("test") + if strings.HasPrefix(authHeader, "Session ") { + return strings.TrimPrefix(authHeader, "Session ") + } else { + if cookie, err := r.Cookie("session"); err == nil { + return cookie.Value + } + } + + return "" +} - // Response error handler - srv.SetResponseErrorHandler(func(re *errors.Response) { - log.Println("Response Error:", re.Error.Error()) - }) +func (a *App) UserAuthorizationHandler(w http.ResponseWriter, r *http.Request) (string, error) { + sessionID := a.getSessionFromRequest(r) - return srv + if sessionID != "" { + uid, err := a.getSession(sessionID) + if err == nil { + log.Println("authorized " + uid) + return uid, nil + } + log.Printf("Session validation failed: %v", err) + } + + return "", fmt.Errorf("not authenticated") +} + +func (a *App) withAuth(handler http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + auth := r.Header.Get("Authorization") + + // Handle session-based auth + if strings.HasPrefix(auth, "Session ") { + sessionID := strings.TrimPrefix(auth, "Session ") + userID, err := a.getSession(sessionID) + if err != nil { + a.jsonResponse(w, http.StatusUnauthorized, APIResponse{ + Success: false, + Error: "Invalid session", + }) + return + } + + ctx := context.WithValue(r.Context(), "user_id", userID) + ctx = context.WithValue(ctx, "auth_type", "session") + handler(w, r.WithContext(ctx)) + return + } + + // Handle OAuth2 bearer tokens + if strings.HasPrefix(auth, "Bearer ") { + token := strings.TrimPrefix(auth, "Bearer ") + ti, err := a.manager.LoadAccessToken(context.Background(), token) + if err != nil { + a.jsonResponse(w, http.StatusUnauthorized, APIResponse{ + Success: false, + Error: "Invalid token", + }) + return + } + + ctx := context.WithValue(r.Context(), "user_id", ti.GetUserID()) + ctx = context.WithValue(ctx, "auth_type", "oauth2") + ctx = context.WithValue(ctx, "client_id", ti.GetClientID()) + handler(w, r.WithContext(ctx)) + return + } + + a.jsonResponse(w, http.StatusUnauthorized, APIResponse{ + Success: false, + Error: "Authentication required", + }) + } } diff --git a/src/user.go b/src/user.go index dec0069..b0aa671 100644 --- a/src/user.go +++ b/src/user.go @@ -63,6 +63,8 @@ func (a *App) createUser(email, password string) (*User, error) { if err := a.redisClient.Set(ctx, "user:"+userID, userData, 0).Err(); err != nil { return nil, err } + + // creates email index if err := a.redisClient.Set(ctx, "email:"+email, userID, 0).Err(); err != nil { return nil, err } diff --git a/src/util.go b/src/util.go index df844a1..0a2a3a3 100644 --- a/src/util.go +++ b/src/util.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "os" "time" ) @@ -39,3 +40,10 @@ func (a *App) jsonResponse(w http.ResponseWriter, statusCode int, response APIRe w.WriteHeader(statusCode) json.NewEncoder(w).Encode(response) } + +func (a *App) redirectToLogin(w http.ResponseWriter, r *http.Request) { + authorizeURL := "/oauth/authorize?" + r.URL.RawQuery + + loginURL := "/auth/login?redirect=" + url.QueryEscape(authorizeURL) + http.Redirect(w, r, loginURL, http.StatusFound) +} From e15432625b8e7ce60f554cb482accae6b34ac966 Mon Sep 17 00:00:00 2001 From: TheKCui0327 Date: Wed, 9 Jul 2025 18:31:58 -0700 Subject: [PATCH 11/13] Fix issue in integration test (#17) --- src/int_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/int_test.go b/src/int_test.go index d796dde..b211651 100644 --- a/src/int_test.go +++ b/src/int_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "sync" "os/signal" "syscall" "testing" @@ -34,7 +35,10 @@ func TestIntegration(t *testing.T) { signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // test jobs + var wg sync.WaitGroup + wg.Add(1) go func() { + defer wg.Done() jobTypes := []string{"a", "b", "c"} for i := 0; i < 10; i++ { jobType := jobTypes[i%len(jobTypes)] @@ -48,5 +52,7 @@ func TestIntegration(t *testing.T) { } } }() + + wg.Wait() supervisor.Stop() } From 804858f5698f2c3793c9854021528c995b6d32e0 Mon Sep 17 00:00:00 2001 From: Elwin Cheng <67119805+elwincheng@users.noreply.github.com> Date: Mon, 28 Jul 2025 20:31:06 -0400 Subject: [PATCH 12/13] Add google container-structure-test (#12) --- src/images/README.md | 30 ++++++- .../pytorch-cpu/pytorch-cpu_config.yaml | 12 +++ src/images/pytorch-cpu/run_test.sh | 2 + .../pytorch-cuda/pytorch-cuda_config.yaml | 12 +++ src/images/pytorch-cuda/run_test.sh | 2 + .../pytorch-rocm/pytorch-rocm_config.yaml | 11 +++ src/images/pytorch-rocm/run_test.sh | 2 + src/images/run_tests.sh | 13 +++ src/images/test/run_regression_tests.sh | 84 ------------------- 9 files changed, 82 insertions(+), 86 deletions(-) create mode 100644 src/images/pytorch-cpu/pytorch-cpu_config.yaml create mode 100644 src/images/pytorch-cpu/run_test.sh create mode 100644 src/images/pytorch-cuda/pytorch-cuda_config.yaml create mode 100644 src/images/pytorch-cuda/run_test.sh create mode 100644 src/images/pytorch-rocm/pytorch-rocm_config.yaml create mode 100644 src/images/pytorch-rocm/run_test.sh create mode 100755 src/images/run_tests.sh delete mode 100755 src/images/test/run_regression_tests.sh diff --git a/src/images/README.md b/src/images/README.md index d06f73f..6b91837 100644 --- a/src/images/README.md +++ b/src/images/README.md @@ -1,11 +1,37 @@ # README +### Prerequisites +- Nvidia runtime installed +- Docker engine running +- [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test) installed + ### Instructions to build and interact with container ``` docker build -t . docker run -it --rm --gpus all bash ``` -### Test +## Test +``` +sh run_tests.sh +``` +## Troubleshooting + +### Unknown or invalid runtime name: nvidia +- Add this do /etc/docker/daemon.json ``` -sh run_regression_tests.sh +{ + "default-runtime": "runc", + "runtimes": { + "nvidia": { + "path": "/usr/bin/nvidia-container-runtime", + "runtimeArgs": [] + } + } +} ``` + +- restart docker engine: sudo systemctl restart docker + + +### nvidia-smi: command not found +- download [nvidia container toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installing-the-nvidia-container-toolkit) diff --git a/src/images/pytorch-cpu/pytorch-cpu_config.yaml b/src/images/pytorch-cpu/pytorch-cpu_config.yaml new file mode 100644 index 0000000..6a5d0c4 --- /dev/null +++ b/src/images/pytorch-cpu/pytorch-cpu_config.yaml @@ -0,0 +1,12 @@ +schemaVersion: '2.0.0' + +commandTests: + - name: "Check Sudo" + command: "which" + args: ["sudo"] + exitCode: 1 + + - name: "CUDA availability check" + command: "python" + args: ["-c", "import torch; print(torch.cuda.is_available())"] + expectedOutput: ["False"] \ No newline at end of file diff --git a/src/images/pytorch-cpu/run_test.sh b/src/images/pytorch-cpu/run_test.sh new file mode 100644 index 0000000..3963fbd --- /dev/null +++ b/src/images/pytorch-cpu/run_test.sh @@ -0,0 +1,2 @@ +image=$1 +container-structure-test test --image $image --config ${image}_config.yaml \ No newline at end of file diff --git a/src/images/pytorch-cuda/pytorch-cuda_config.yaml b/src/images/pytorch-cuda/pytorch-cuda_config.yaml new file mode 100644 index 0000000..5ae3ac3 --- /dev/null +++ b/src/images/pytorch-cuda/pytorch-cuda_config.yaml @@ -0,0 +1,12 @@ +schemaVersion: '2.0.0' + +commandTests: + - name: "Check Sudo" + command: "which" + args: ["sudo"] + exitCode: 1 + + - name: "CUDA availability check" + command: "python" + args: ["-c", "import torch; print(torch.cuda.is_available())"] + expectedOutput: ["True"] \ No newline at end of file diff --git a/src/images/pytorch-cuda/run_test.sh b/src/images/pytorch-cuda/run_test.sh new file mode 100644 index 0000000..029c373 --- /dev/null +++ b/src/images/pytorch-cuda/run_test.sh @@ -0,0 +1,2 @@ +image=$1 +container-structure-test test --image $image --config ${image}_config.yaml --runtime nvidia \ No newline at end of file diff --git a/src/images/pytorch-rocm/pytorch-rocm_config.yaml b/src/images/pytorch-rocm/pytorch-rocm_config.yaml new file mode 100644 index 0000000..7cb0922 --- /dev/null +++ b/src/images/pytorch-rocm/pytorch-rocm_config.yaml @@ -0,0 +1,11 @@ +schemaVersion: '2.0.0' + +commandTests: + - name: "Check Sudo" + command: "whoami" + expectedOutput: ["guest"] + + - name: "CUDA availability check" + command: "python" + args: ["-c", "import torch; print(torch.cuda.is_available())"] + expectedOutput: ["False"] \ No newline at end of file diff --git a/src/images/pytorch-rocm/run_test.sh b/src/images/pytorch-rocm/run_test.sh new file mode 100644 index 0000000..3963fbd --- /dev/null +++ b/src/images/pytorch-rocm/run_test.sh @@ -0,0 +1,2 @@ +image=$1 +container-structure-test test --image $image --config ${image}_config.yaml \ No newline at end of file diff --git a/src/images/run_tests.sh b/src/images/run_tests.sh new file mode 100755 index 0000000..44ef9ed --- /dev/null +++ b/src/images/run_tests.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +images=("pytorch-cuda" "pytorch-rocm" "pytorch-cpu") +path=$(dirname $(readlink -f $0)) +for image in "${images[@]}"; do + cd $path/$image + docker build -t $image . +done + +for image in "${images[@]}"; do + cd $path/$image + sh ./run_test.sh $image +done \ No newline at end of file diff --git a/src/images/test/run_regression_tests.sh b/src/images/test/run_regression_tests.sh deleted file mode 100755 index 7c2f85d..0000000 --- a/src/images/test/run_regression_tests.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -function test_match() { - local gpu_option="" - - while getopts ":g" opt; do - case "$opt" in - g) - gpu_option="--gpus all" - shift $((OPTIND - 1)) - ;; - \?) - echo "Unknown option: -$OPTARG" - return 1 - ;; - esac - done - local test_name=$1 - local command=$2 - local input=$3 - local expected=$4 - - local actual=$(docker run -it --rm $gpu_option $image "$command" -c "$input" | grep -c "$expected") - echo "Running test for $test_name" - if [[ "$actual" == "1" ]]; then - echo "SUCCESS" - else - echo "FAIL" - fi -} - -function test_no_match() { - local gpu_option="" - - while getopts ":g" opt; do - case "$opt" in - g) - gpu_option="--gpus all" - shift $((OPTIND - 1)) - ;; - \?) - echo "Unknown option: -$OPTARG" - return 1 - ;; - esac - done - local test_name=$1 - local command=$2 - local input=$3 - local expected=$4 - - local actual=$(docker run -it --rm $gpu_option $image "$command" -c "$input" | grep -c "$expected") - echo "Running test for $test_name" - if [[ "$actual" == "0" ]]; then - echo "SUCCESS" - else - echo "FAIL" - fi -} - - -cd ../pytorch-cuda -docker build -t pytorch-cuda . -image=pytorch-cuda - -test_match -g "TEST CUDA" "python3" "import torch; print(torch.cuda.is_available())" "True" -test_match "TEST SUDO" "bash" "sudo whoami" "command not found" -test_match "TEST APT" "bash" "apt install" "Permission denied" - -cd ../pytorch-cpu -docker build -t pytorch-cpu . -image=pytorch-cpu - -test_match "TEST CUDA" "python3" "import torch; print(torch.cuda.is_available())" "False" -test_no_match "TEST SUDO" "cat" "/etc/sudoers" "guest" -test_match "TEST APT" "bash" "apt install" "Permission denied" - -cd ../pytorch-rocm -docker build -t pytorch-rocm . -image=pytorch-rocm - -test_match "TEST CUDA" "python3" "import torch; print(torch.cuda.is_available())" "False" -test_no_match "TEST SUDO" "cat" "/etc/sudoers" "guest" -test_match "TEST APT" "bash" "apt install" "Permission denied" From 82ec4d6cf2861be5961aafdac88859d81c73e3b3 Mon Sep 17 00:00:00 2001 From: Ibrahim <139663719+ibrah-m@users.noreply.github.com> Date: Mon, 18 Aug 2025 02:27:59 +0000 Subject: [PATCH 13/13] Create initial API skeleton (#16) * Create initial API skeleton * Add supabase-go as a dependency * init scheduler and supervisor * Clean up go.mod * Fix Supervisor Start --------- Co-authored-by: blobcode --- src/api.go | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api.go b/src/api.go index 9c1896c..69e600f 100644 --- a/src/api.go +++ b/src/api.go @@ -90,7 +90,6 @@ func (a *App) Start() error { if err := a.redisClient.Ping(context.Background()).Err(); err != nil { return fmt.Errorf("redis ping failed: %w", err) } - // Launch HTTP server a.wg.Add(1) go func() {