Compare commits
2 Commits
Author | SHA256 | Date | |
---|---|---|---|
e5d07f0ce6
|
|||
df9478a920
|
46
gitea_status_proxy/config.go
Normal file
46
gitea_status_proxy/config.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/tailscale/hujson"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ForgeEndpoint string `json:"forge_url"`
|
||||
Keys []string `json:"keys"`
|
||||
}
|
||||
|
||||
type contextKey string
|
||||
|
||||
const configKey contextKey = "config"
|
||||
|
||||
func ReadConfig(reader io.Reader) (*Config, error) {
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading config data: %w", err)
|
||||
}
|
||||
config := Config{}
|
||||
data, err = hujson.Standardize(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse json: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("error parsing json to api keys and target url: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func ReadConfigFile(filename string) (*Config, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot open config file for reading. err: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return ReadConfig(file)
|
||||
}
|
15
gitea_status_proxy/handlers.go
Normal file
15
gitea_status_proxy/handlers.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ConfigMiddleWare(cfg *Config) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(r.Context(), configKey, cfg)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
169
gitea_status_proxy/main.go
Normal file
169
gitea_status_proxy/main.go
Normal file
@@ -0,0 +1,169 @@
|
||||
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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user