Skip to content

Commit

Permalink
fix up git cmd.go as well, fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
pickledish committed Dec 14, 2023
1 parent 22d4a2b commit 0b63d66
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 152 deletions.
3 changes: 1 addition & 2 deletions cmd/api/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ import (

"cbnr/util"

"github.com/spf13/cobra"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/jwtauth/v5"
"github.com/spf13/cobra"
)

var ApiCmd = &cobra.Command{
Expand Down
213 changes: 63 additions & 150 deletions cmd/git/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,214 +4,127 @@ import (
"context"
"fmt"
"log"
"math/rand"
"net/http"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"text/template"
"time"

"cbnr/util"

"github.com/spf13/cobra"

"github.com/asim/git-http-backend/server"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/jwtauth/v5"

"github.com/asim/git-http-backend/server"

"github.com/carlmjohnson/requests"
"github.com/spf13/cobra"
)

type Storage struct {
Name string `json:"storage_name"`
Token string `json:"storage_token"`
}

func GitInitMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(out http.ResponseWriter, req *http.Request) {

// Only need to init the repository at the start of the "git push", which
// always begins with this GET to /reponame/info/refs, so if this request
// is not that, it must not be the start of a push
if !(req.Method == "GET" && strings.HasSuffix(req.URL.Path, "/info/refs")) {
next.ServeHTTP(out, req)
return
}

repoName := strings.Split(req.URL.Path, "/")[1]
repoPath := "/tmp/" + repoName

log.Printf("git init bare %s", repoPath)

cmd := exec.Command("git", "init", repoPath)
stdout, err := cmd.Output()

if err != nil {
log.Printf(err.Error())
return
}

// Print the output
log.Printf(string(stdout))

next.ServeHTTP(out, req)
})
}

// gets the site ID from the URL path, then makes a query to supabase (using
// the JWT gotten from BasicAuthJwtVerifier) to make sure the user actually owns
// that site ID and can push there, then uses the storage name and pass to create
// a post-receive hook in the repo created by GitInitMiddleware
func RenderPostReceiveHookMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(out http.ResponseWriter, req *http.Request) {

// Only need to init the repository at the start of the "git push", which
// always begins with this GET to /reponame/info/refs, so if this request
// is not that, it must not be the start of a push
if !(req.Method == "GET" && strings.HasSuffix(req.URL.Path, "/info/refs")) {
next.ServeHTTP(out, req)
return
}

token := req.Header.Get("Authorization")

repoName := strings.Split(req.URL.Path, "/")[1]
var GitCmd = &cobra.Command{
Use: "git",
Short: "git - handles administrative tasks regarding bunny and supabase",
Run: func(cmd *cobra.Command, args []string) {

var storage []Storage
log.Printf("[INFO] Starting up...")

err := requests.
URL("https://ewwccbgjnulfgcvfrsvj.supabase.co").
Path("/rest/v1/site").
Param("select", "storage_name,storage_token").
Param("id", fmt.Sprintf("eq.%s", repoName)).
Header("apikey", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImV3d2NjYmdqbnVsZmdjdmZyc3ZqIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTM1ODE2ODUsImV4cCI6MjAwOTE1NzY4NX0.gI3YdNSC5GMkda2D2QPRMvnBdaMOS2ynfFKxis5-WKs").
Header("Authorization", token).
ToJSON(&storage).
Fetch(req.Context())
// ------------------------------------------------------------------------

if err != nil {
http.Error(out, fmt.Sprintf("Error occurred querying sites from supabase, dying: %v", err), http.StatusInternalServerError)
return
}
log.Printf("[INFO] Seeding randomness for generating IDs...")

if len(storage) == 0 {
http.Error(out, fmt.Sprintf("No result rows from supabase for site ID %v (possibly RLS unauthorized?)", repoName), http.StatusUnauthorized)
return
}
rand.Seed(time.Now().UnixNano())

if len(storage) > 1 {
http.Error(out, fmt.Sprintf("Too many rows from supabase, what do I do: %v", storage), http.StatusInternalServerError)
return
}
// ------------------------------------------------------------------------

log.Printf("site ID found for repo %s, storage %s", repoName, storage[0])
log.Printf("[INFO] Registering int handlers for graceful shutdown...")

hookValues := HookValues{
SiteId: repoName,
StorageHost: "storage.bunnycdn.com",
StorageName: storage[0].Name,
StorageToken: storage[0].Token,
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

hookPath := fmt.Sprintf("/tmp/%s/.git/hooks/post-receive", repoName)
// ------------------------------------------------------------------------

tpl, err := template.New("naaaaame").Parse(HookTemplate)
if err != nil {
http.Error(out, fmt.Sprintf("Unable to render post-receive hook template: %v", err), http.StatusInternalServerError)
return
}
log.Printf("[INFO] Getting configs from environment...")

file, err := os.OpenFile(hookPath, os.O_RDWR|os.O_CREATE, 0777)
if err != nil {
http.Error(out, fmt.Sprintf("Could not create and open post-receive hook file: %v", err), http.StatusInternalServerError)
return
configNames := []string{
"HTTP_LISTENER_PORT",
"PERMISSIVE_MODE",
"JWT_SECRET",
"SUPABASE_URL",
"SUPABASE_ANON_KEY",
}

defer file.Close()

err = tpl.Execute(file, hookValues)
config, err := util.GetEnvConfigs(configNames)
if err != nil {
http.Error(out, fmt.Sprintf("Could not render template into hook file: %v", err), http.StatusInternalServerError)
return
log.Fatalf("[ERROR] Could not parse configs from environment: %v", err)
}

next.ServeHTTP(out, req)
return
})
}
// ------------------------------------------------------------------------

var GitCmd = &cobra.Command{
Use: "git",
Short: "git - handles administrative tasks regarding bunny and supabase",
Run: func(cmd *cobra.Command, args []string) {
log.Printf("[INFO] Setting up middlewares...")

log.Printf("STARTING")
// as soon as supabase supports RS256 / asymmetric JWT encryption, get
// this out of here and replace with the public key just for validation
// https://github.com/orgs/supabase/discussions/4059
jwtSecret := jwtauth.New("HS256", []byte(config["JWT_SECRET"]), nil)

// set up channel to handle graceful shutdown
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
// ------------------------------------------------------------------------

httpPort, err := util.GetEnvConfig("HTTP_LISTENER_PORT")
if err != nil {
log.Fatalf("Unable to get http listener port from environment: %v", err)
}
log.Printf("[INFO] Setting up custom git-middlewarer client...")

permissiveStr, err := util.GetEnvConfig("PERMISSIVE_MODE")
if err != nil {
log.Fatalf("Unable to get permissive mode status from environment: %v", err)
mid := Middlewarer{
SupabaseUrl: config["SUPABASE_URL"],
SupabaseAnonKey: config["SUPABASE_ANON_KEY"],
}

permissive := permissiveStr == "true"

jwtSecretStr, err := util.GetEnvConfig("JWT_SECRET")
if err != nil {
log.Fatalf("Unable to get JWT secret token from environment: %v", err)
}
// ------------------------------------------------------------------------

// as soon as supabase supports RS256 / asymmetric JWT encryption, get this
// out of here and replace with the public key just for validation
// https://github.com/orgs/supabase/discussions/4059
jwtSecret := jwtauth.New("HS256", []byte(jwtSecretStr), nil)
log.Printf("[INFO] Registering middlewares and handlers...")

r := chi.NewRouter()

r.Use(middleware.Recoverer)
r.Use(middleware.Logger)
//r.Use(middleware.AllowContentType("application/json"))
r.Use(middleware.Timeout(time.Second))
r.Use(middleware.Timeout(60 * time.Second))
r.Use(util.BasicAuthJwtVerifier(jwtSecret))
r.Use(util.CheckJwtMiddleware(permissive, true))
r.Use(GitInitMiddleware)
r.Use(RenderPostReceiveHookMiddleware)
r.Use(util.CheckJwtMiddleware((config["PERMISSIVE_MODE"] == "true"), true))
r.Use(mid.CreateGitInitMiddleware())
r.Use(mid.CreateGitHookMiddleware())

r.Get("/*", server.Handler())
r.Post("/*", server.Handler())

log.Printf("MIDDLEWARES SET UP, WILL LISTEN ON %v...", httpPort)
// ------------------------------------------------------------------------

log.Printf("[INFO] Trying to listen %v...", config["HTTP_LISTENER_PORT"])

primary := http.Server{
Addr: fmt.Sprintf(":%v", config["HTTP_LISTENER_PORT"]),
Handler: r,
}

go func() {
err := primary.ListenAndServe()
if err != nil {
log.Fatalf("[ERROR] Primary server could not start: %v", err)
}
}()

server := http.Server{Addr: fmt.Sprintf(":%v", httpPort), Handler: r}
go server.ListenAndServe()
// ------------------------------------------------------------------------

log.Printf("HTTP SERVER STARTED IN GOROUTINE, waiting to die...")
log.Printf("[INFO] Listening! Main thread now waiting for interrupt...")

// block here until we get some sort of interrupt or kill
<-done

log.Printf("GOT SIGNAL TO DIE, cleaning up...")
log.Printf("[INFO] Got signal to die, cleaning up...")

ctx := context.Background()
ctxTimeout, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel()

err = server.Shutdown(ctxTimeout)
err = primary.Shutdown(ctxTimeout)
if err != nil {
log.Fatalf("Could not cleanly shut down http server: %v", err)
log.Fatalf("[ERROR] Could not cleanly shut down primary server: %v", err)
}

log.Printf("ALL DONE, GOODBYE")
log.Printf("[INFO] ALL DONE, GOODBYE")
},
}
Loading

0 comments on commit 0b63d66

Please sign in to comment.