276 lines
7.4 KiB
Go
276 lines
7.4 KiB
Go
package api
|
|
|
|
import (
|
|
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 {
|
|
githubFiles[i] = convertCommitFile(file)
|
|
}
|
|
|
|
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 (r restApi) handlerGetTree(w http.ResponseWriter, request *http.Request) {
|
|
vars := mux.Vars(request)
|
|
owner := vars["owner"]
|
|
repo := vars["repo"]
|
|
treeSha := vars["tree_sha"]
|
|
recursive := false
|
|
|
|
if request.URL.Query().Has("recursive") {
|
|
recursive = true
|
|
}
|
|
|
|
client := r.getClientOrError(w)
|
|
|
|
if client == nil {
|
|
return
|
|
}
|
|
|
|
tree, r2, err := client.GetTrees(owner, repo, treeSha, recursive)
|
|
if err != nil {
|
|
http.Error(w, "Failed to get tree", http.StatusInternalServerError)
|
|
r.logger.Error("Failed to get tree", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
treeResponse := convertGitTree(tree)
|
|
|
|
r.sendPagingHeaders(w, r2)
|
|
r.respond(w, http.StatusOK, treeResponse)
|
|
}
|
|
|
|
func (r restApi) handlerCreateIssueComment(w http.ResponseWriter, request *http.Request) {
|
|
vars := mux.Vars(request)
|
|
owner := vars["owner"]
|
|
repo := vars["repo"]
|
|
issueNumber := vars["issue_number"]
|
|
|
|
client := r.getClientOrError(w)
|
|
|
|
if client == nil {
|
|
return
|
|
}
|
|
|
|
ghComment := github.IssueComment{}
|
|
|
|
if err := r.parseJsonBody(request, &ghComment); err != nil {
|
|
http.Error(w, "Failed to parse request body", http.StatusBadRequest)
|
|
r.logger.Error("Failed to parse request body", zap.Error(err))
|
|
}
|
|
|
|
issueNumberInt, err := strconv.Atoi(issueNumber)
|
|
if err != nil {
|
|
http.Error(w, "Failed to parse issue number", http.StatusBadRequest)
|
|
r.logger.Error("Failed to parse issue number", zap.Error(err))
|
|
return
|
|
}
|
|
comment := gitea.CreateIssueCommentOption{
|
|
Body: ghComment.GetBody(),
|
|
}
|
|
|
|
commentResponse, _, err := client.CreateIssueComment(owner, repo, int64(issueNumberInt), comment)
|
|
if err != nil {
|
|
http.Error(w, "Failed to create comment", http.StatusInternalServerError)
|
|
r.logger.Error("Failed to create comment", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
r.respond(w, http.StatusCreated, commentResponse)
|
|
}
|
|
|
|
func (r restApi) handlerUpdateIssueComment(w http.ResponseWriter, request *http.Request) {
|
|
vars := mux.Vars(request)
|
|
owner := vars["owner"]
|
|
repo := vars["repo"]
|
|
commentID := vars["comment_id"]
|
|
|
|
client := r.getClientOrError(w)
|
|
|
|
if client == nil {
|
|
return
|
|
}
|
|
|
|
ghComment := github.IssueComment{}
|
|
|
|
if err := r.parseJsonBody(request, &ghComment); err != nil {
|
|
http.Error(w, "Failed to parse request body", http.StatusBadRequest)
|
|
r.logger.Error("Failed to parse request body", zap.Error(err))
|
|
}
|
|
|
|
commentIDInt, err := strconv.Atoi(commentID)
|
|
if err != nil {
|
|
http.Error(w, "Failed to parse comment ID", http.StatusBadRequest)
|
|
r.logger.Error("Failed to parse comment ID", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
comment := gitea.EditIssueCommentOption{
|
|
Body: request.FormValue("body"),
|
|
}
|
|
|
|
commentResponse, _, err := client.EditIssueComment(owner, repo, int64(commentIDInt), comment)
|
|
if err != nil {
|
|
http.Error(w, "Failed to update comment", http.StatusInternalServerError)
|
|
r.logger.Error("Failed to update comment", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
r.respond(w, http.StatusOK, commentResponse)
|
|
}
|
|
|
|
func (r restApi) parseJsonBody(request *http.Request, obj interface{}) error {
|
|
if obj == nil {
|
|
obj = make(map[string]interface{})
|
|
}
|
|
|
|
decoder := json.NewDecoder(request.Body)
|
|
return decoder.Decode(obj)
|
|
}
|
|
|
|
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")
|
|
r.HandleFunc("/repos/{owner}/{repo}/git/trees/{tree_sha}", restApi.handlerGetTree).Methods("GET")
|
|
r.HandleFunc("/repos/{owner}/{repo}/issues/{issue_number}/comments", restApi.handlerCreateIssueComment).Methods("POST")
|
|
r.HandleFunc("/repos/{owner}/{repo}/issues/{issue_number}/comments/{comment_id}", restApi.handlerUpdateIssueComment).Methods("PATCH")
|
|
}
|
|
|
|
restRouter := r.PathPrefix("/api").Subrouter()
|
|
restRouter.Use(githubRestVerifyMiddleware(params.Db))
|
|
restRouter.Use(githubRestRequireAuthMiddleware(params.Config))
|
|
|
|
setupRoutes(restRouter)
|
|
setupRoutes(restRouter.PathPrefix("/v3").Subrouter())
|
|
}
|