package api import ( structs "code.gitea.io/gitea/modules/structs" gitea "code.gitea.io/sdk/gitea" "encoding/json" "fmt" "git.lumeweb.com/LumeWeb/gitea-github-proxy/config" "github.com/google/go-github/v59/github" "github.com/gorilla/mux" "go.uber.org/zap" "net/http" "strconv" ) type restApi struct { config *config.Config logger *zap.Logger } func newRestApi(cfg *config.Config, logger *zap.Logger) *restApi { return &restApi{config: cfg, logger: logger} } func (r restApi) handlerGetPullRequestFiles(w http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) owner := vars["owner"] repo := vars["repo"] pullNumber := vars["pull_number"] client := r.getClientOrError(w) if client == nil { return } parsedPullNumber, err := strconv.ParseInt(pullNumber, 10, 64) if err != nil { http.Error(w, "Failed to parse pull number", http.StatusBadRequest) r.logger.Error("Failed to parse pull number", zap.Error(err)) return } files, r2, err := client.ListPullRequestFiles(owner, repo, parsedPullNumber, gitea.ListPullRequestFilesOptions{ ListOptions: r.getPagingOptions(request), }) if err != nil { http.Error(w, "Failed to get pull request files", http.StatusInternalServerError) r.logger.Error("Failed to get pull request files", zap.Error(err)) return } githubFiles := make([]*github.CommitFile, len(files)) for i, file := range files { f := structs.ChangedFile(*file) githubFiles[i] = convertCommitFile(&f) } r.sendPagingHeaders(w, r2) r.respond(w, http.StatusOK, githubFiles) } func (r restApi) getPagingOptions(request *http.Request) gitea.ListOptions { page, _ := strconv.Atoi(request.URL.Query().Get("page")) perPage, _ := strconv.Atoi(request.URL.Query().Get("per_page")) return gitea.ListOptions{ Page: page, PageSize: perPage, } } func (r restApi) sendPagingHeaders(w http.ResponseWriter, apiResponse *gitea.Response) { links := []string{} baseURL := fmt.Sprintf("https://%s", r.config.Domain) joinStrings := func(elements []string, separator string) string { if len(elements) == 0 { return "" } result := elements[0] for i := 1; i < len(elements); i++ { result += separator + elements[i] } return result } joinLinks := func(links []string) string { return fmt.Sprintf("%s", joinStrings(links, ", ")) } if apiResponse.FirstPage > 0 { links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="first"`, baseURL, apiResponse.FirstPage)) } if apiResponse.PrevPage > 0 { links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="prev"`, baseURL, apiResponse.PrevPage)) } if apiResponse.NextPage > 0 { links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="next"`, baseURL, apiResponse.NextPage)) } if apiResponse.LastPage > 0 { links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="last"`, baseURL, apiResponse.LastPage)) } if len(links) > 0 { w.Header().Add("Link", fmt.Sprintf("%s", joinLinks(links))) } } func (r restApi) respond(w http.ResponseWriter, status int, data interface{}) { jsonData, err := json.Marshal(data) if err != nil { http.Error(w, "Failed to marshal response", http.StatusInternalServerError) r.logger.Error("Failed to marshal response", zap.Error(err)) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if data != nil { _, _ = w.Write(jsonData) } } func (r restApi) getClientOrError(w http.ResponseWriter) *gitea.Client { client, err := getClient(ClientParams{ Config: r.config, }) if err != nil { http.Error(w, "Failed to get Gitea client", http.StatusInternalServerError) r.logger.Error("Failed to get Gitea client", zap.Error(err)) return nil } return client } func setupRestRoutes(params RouteParams) { logger := params.Logger cfg := params.Config r := params.R restApi := newRestApi(cfg, logger) setupRoutes := func(r *mux.Router) { r.HandleFunc("/repos/{owner}/{repo}/pulls/{pull_number}/files", restApi.handlerGetPullRequestFiles).Methods("GET") } restRouter := r.PathPrefix("/api").Subrouter() restRouter.Use(githubRestVerifyMiddleware(params.Db)) restRouter.Use(githubRestRequireAuthMiddleware(params.Config)) setupRoutes(restRouter) setupRoutes(restRouter.PathPrefix("/v3").Subrouter()) }