package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "net/http" "os" "slices" "strings" "src.opensuse.org/autogits/common" ) type Status struct { Context string `json:"context"` State string `json:"state"` TargetUrl string `json:"target_url"` } type StatusInput struct { State string `json:"state"` TargetUrl string `json:"target_url"` } func main() { configFile := flag.String("config", "", "status proxy config file") flag.Parse() if *configFile == "" { common.LogError("missing required argument config") return } config, err := ReadConfigFile(*configFile) if err != nil { common.LogError("Failed to read config file", err) return } mux := http.NewServeMux() mux.Handle("/repos/{owner}/{repo}/statuses/{sha}", ConfigMiddleWare(config)(http.HandlerFunc(StatusProxy))) common.LogInfo("server up and listening on :3000") err = http.ListenAndServe(":3000", mux) if err != nil { common.LogError("Server failed to start up", err) } } func StatusProxy(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPost { config, ok := r.Context().Value(configKey).(*Config) if !ok { common.LogError("Config missing from context") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } header := r.Header.Get("Authorization") if header == "" { http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return } token_arr := strings.Split(header, " ") if len(token_arr) != 2 { http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return } if !strings.EqualFold(token_arr[0], "Bearer") { http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return } token := token_arr[1] if !slices.Contains(config.Keys, token) { http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return } owner := r.PathValue("owner") repo := r.PathValue("repo") sha := r.PathValue("sha") if !ok { common.LogError("Failed to get config from context, is it set?") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } posturl := fmt.Sprintf("%s/repos/%s/%s/statuses/%s", config.ForgeEndpoint, owner, repo, sha) decoder := json.NewDecoder(r.Body) var statusinput StatusInput err := decoder.Decode(&statusinput) if err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } status := Status{ Context: "Build in obs", State: statusinput.State, TargetUrl: statusinput.TargetUrl, } status_payload, err := json.Marshal(status) if err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } client := &http.Client{} req, err := http.NewRequest("POST", posturl, bytes.NewBuffer(status_payload)) if err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } ForgeToken := os.Getenv("GITEA_TOKEN") if ForgeToken == "" { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) common.LogError("GITEA_TOKEN was not set, all requests will fail") return } req.Header.Add("Content-Type", "Content-Type") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ForgeToken)) resp, err := client.Do(req) if err != nil { common.LogError(fmt.Sprintf("Request to forge endpoint failed: %v", err)) http.Error(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway) return } defer resp.Body.Close() w.Header().Set("Content-Type", "application/json") w.WriteHeader(resp.StatusCode) /* the commented out section sets every key value from the headers, unsure if this leaks information from gitea for k, v := range resp.Header { for _, vv := range v { w.Header().Add(k, vv) } } */ _, err = io.Copy(w, resp.Body) if err != nil { common.LogError("Error copying response body: %v", err) } } else { http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }