2022-03-23 00:39:53 +00:00
|
|
|
package gitea
|
|
|
|
|
|
|
|
import (
|
|
|
|
"code.gitea.io/sdk/gitea"
|
2022-03-25 01:01:46 +00:00
|
|
|
"code.mrmelon54.xyz/sean/melon-tools/utils"
|
|
|
|
"context"
|
|
|
|
_ "embed"
|
|
|
|
"encoding/gob"
|
2022-03-23 00:39:53 +00:00
|
|
|
"fmt"
|
2022-03-25 01:01:46 +00:00
|
|
|
"github.com/google/uuid"
|
2022-03-23 00:39:53 +00:00
|
|
|
"github.com/gorilla/mux"
|
2022-03-25 01:01:46 +00:00
|
|
|
"golang.org/x/oauth2"
|
|
|
|
"html/template"
|
2022-03-24 21:07:44 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2022-03-26 11:43:55 +00:00
|
|
|
"strings"
|
2022-03-23 00:39:53 +00:00
|
|
|
)
|
|
|
|
|
2022-03-25 01:01:46 +00:00
|
|
|
//go:embed pages/index.go.html
|
|
|
|
var indexTemplate string
|
|
|
|
|
2022-03-23 00:39:53 +00:00
|
|
|
type Module struct {
|
2022-03-25 01:01:46 +00:00
|
|
|
sessionWrapper func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request)
|
2022-03-28 21:22:34 +01:00
|
|
|
oauthClient *oauth2.Config
|
2022-03-23 00:39:53 +00:00
|
|
|
}
|
|
|
|
|
2022-03-25 01:01:46 +00:00
|
|
|
type giteaKeyType int
|
|
|
|
|
|
|
|
const (
|
2022-03-26 00:26:20 +00:00
|
|
|
KeyOauthClient = giteaKeyType(iota)
|
|
|
|
KeyUser
|
|
|
|
KeyState
|
|
|
|
KeyAccessToken
|
|
|
|
KeyRefreshToken
|
2022-03-25 01:01:46 +00:00
|
|
|
)
|
2022-03-24 21:07:44 +00:00
|
|
|
|
2022-03-23 00:39:53 +00:00
|
|
|
func New() *Module {
|
2022-03-25 01:01:46 +00:00
|
|
|
gob.Register(new(giteaKeyType))
|
2022-03-23 00:39:53 +00:00
|
|
|
return &Module{}
|
|
|
|
}
|
|
|
|
|
2022-03-28 21:22:34 +01:00
|
|
|
func (m *Module) GetName() string { return "Gitea" }
|
|
|
|
func (m *Module) GetEndpoint() string { return "/gitea" }
|
|
|
|
|
|
|
|
func (m *Module) SetupModule(router *mux.Router, f func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request)) {
|
|
|
|
m.sessionWrapper = f
|
2022-03-25 01:01:46 +00:00
|
|
|
m.oauthClient = &oauth2.Config{
|
|
|
|
ClientID: os.Getenv("GITEA_CLIENT_ID"),
|
|
|
|
ClientSecret: os.Getenv("GITEA_CLIENT_SECRET"),
|
|
|
|
Scopes: []string{"openid"},
|
|
|
|
Endpoint: oauth2.Endpoint{
|
|
|
|
AuthURL: os.Getenv("GITEA_AUTHORIZE_URL"),
|
|
|
|
TokenURL: os.Getenv("GITEA_TOKEN_URL"),
|
|
|
|
},
|
|
|
|
RedirectURL: os.Getenv("GITEA_REDIRECT_URL"),
|
|
|
|
}
|
2022-03-24 21:07:44 +00:00
|
|
|
router.HandleFunc("/", m.getClient(m.homepage))
|
2022-03-25 01:01:46 +00:00
|
|
|
router.HandleFunc("/login", m.sessionWrapper(m.loginPage))
|
2022-03-24 21:07:44 +00:00
|
|
|
}
|
|
|
|
|
2022-03-25 01:01:46 +00:00
|
|
|
func (m *Module) getClient(cb func(http.ResponseWriter, *http.Request, *utils.State, *gitea.Client)) func(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
return m.sessionWrapper(func(rw http.ResponseWriter, req *http.Request, state *utils.State) {
|
2022-03-26 00:26:20 +00:00
|
|
|
if v, ok := utils.GetStateValue[*gitea.Client](state, KeyOauthClient); ok {
|
2022-03-25 01:01:46 +00:00
|
|
|
cb(rw, req, state, v)
|
2022-03-24 21:07:44 +00:00
|
|
|
return
|
|
|
|
}
|
2022-03-25 01:01:46 +00:00
|
|
|
http.Redirect(rw, req, "/gitea/login", http.StatusTemporaryRedirect)
|
2022-03-24 21:07:44 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-25 01:01:46 +00:00
|
|
|
func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *utils.State, giteaClient *gitea.Client) {
|
2022-03-24 21:07:44 +00:00
|
|
|
myUser, _, err := giteaClient.GetMyUserInfo()
|
2022-03-23 00:39:53 +00:00
|
|
|
if err != nil {
|
2022-03-26 00:26:20 +00:00
|
|
|
state.Del(KeyOauthClient)
|
2022-03-24 21:07:44 +00:00
|
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
orgs, _, err := giteaClient.ListMyOrgs(gitea.ListOrgsOptions{})
|
|
|
|
if err != nil {
|
|
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
2022-03-23 00:39:53 +00:00
|
|
|
return
|
|
|
|
}
|
2022-03-25 01:01:46 +00:00
|
|
|
orgSimple := make([]struct{ Name string }, len(orgs))
|
|
|
|
for i, j := range orgs {
|
|
|
|
orgSimple[i] = struct{ Name string }{j.UserName}
|
|
|
|
}
|
2022-03-26 00:26:20 +00:00
|
|
|
selOrg := ""
|
|
|
|
repoSimple := make([]struct{ Name string }, 0)
|
|
|
|
selRepo := ""
|
2022-03-26 11:43:55 +00:00
|
|
|
selModule := ""
|
|
|
|
selCommitTime := ""
|
|
|
|
selCommitHash := ""
|
2022-03-26 00:26:20 +00:00
|
|
|
|
|
|
|
q := req.URL.Query()
|
|
|
|
if q.Has("org") {
|
|
|
|
selOrg = q.Get("org")
|
|
|
|
repos, _, err := giteaClient.ListOrgRepos(selOrg, gitea.ListOrgReposOptions{ListOptions: gitea.ListOptions{Page: 0, PageSize: 100}})
|
|
|
|
if err != nil {
|
|
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
repoSimple = make([]struct{ Name string }, len(repos))
|
|
|
|
for i, j := range repos {
|
|
|
|
repoSimple[i] = struct{ Name string }{Name: j.Name}
|
|
|
|
}
|
|
|
|
|
|
|
|
if q.Has("repo") {
|
|
|
|
selRepo = q.Get("repo")
|
2022-03-26 11:43:55 +00:00
|
|
|
repo, _, err := giteaClient.GetRepo(selOrg, selRepo)
|
2022-03-26 00:26:20 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2022-03-26 11:43:55 +00:00
|
|
|
refs, _, err := giteaClient.GetRepoRefs(selOrg, selRepo, "heads/"+repo.DefaultBranch)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(refs) == 1 {
|
|
|
|
ref := refs[0]
|
|
|
|
commit, _, err := giteaClient.GetSingleCommit(selOrg, selRepo, ref.Object.SHA)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
selCommitTime = commit.CommitMeta.Created.Format("20060102150405")
|
|
|
|
selCommitHash = commit.CommitMeta.SHA[:12]
|
|
|
|
goMod, _, err := giteaClient.GetFile(selOrg, selRepo, ref.Object.SHA, "go.mod")
|
|
|
|
if err != nil {
|
|
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
goModStr := string(goMod)
|
|
|
|
goModIdx := strings.Index(goModStr, "\n")
|
|
|
|
goModLine := goModStr[:goModIdx]
|
|
|
|
goModSpace := strings.Index(goModLine, " ")
|
|
|
|
selModule = goModLine[goModSpace+1:]
|
2022-03-26 00:26:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-03-25 01:01:46 +00:00
|
|
|
|
|
|
|
tmp, err := template.New("homepage").Parse(indexTemplate)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Template parse error:", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = tmp.Execute(rw, struct {
|
2022-03-26 11:43:55 +00:00
|
|
|
Username string
|
|
|
|
Orgs []struct{ Name string }
|
|
|
|
Repos []struct{ Name string }
|
|
|
|
SelOrg string
|
|
|
|
ShowOrg bool
|
|
|
|
SelModule string
|
|
|
|
ShowRepo bool
|
|
|
|
CommitTime string
|
|
|
|
CommitHash string
|
2022-03-25 01:01:46 +00:00
|
|
|
}{
|
2022-03-26 11:43:55 +00:00
|
|
|
Username: myUser.UserName,
|
|
|
|
Orgs: orgSimple,
|
|
|
|
Repos: repoSimple,
|
|
|
|
SelOrg: selOrg,
|
|
|
|
SelModule: selModule,
|
|
|
|
ShowOrg: selOrg != "",
|
|
|
|
ShowRepo: selModule != "",
|
|
|
|
CommitTime: selCommitTime,
|
|
|
|
CommitHash: selCommitHash,
|
2022-03-25 01:01:46 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Template execute error:", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Module) loginPage(rw http.ResponseWriter, req *http.Request, state *utils.State) {
|
2022-03-26 00:26:20 +00:00
|
|
|
if myUser, ok := utils.GetStateValue[*string](state, KeyUser); ok {
|
2022-03-25 01:01:46 +00:00
|
|
|
if myUser != nil {
|
|
|
|
http.Redirect(rw, req, "/gitea", http.StatusTemporaryRedirect)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-26 00:26:20 +00:00
|
|
|
if flowState, ok := utils.GetStateValue[uuid.UUID](state, KeyState); ok {
|
2022-03-25 01:01:46 +00:00
|
|
|
q := req.URL.Query()
|
|
|
|
if q.Has("code") && q.Has("state") {
|
|
|
|
if q.Get("state") == flowState.String() {
|
|
|
|
exchange, err := m.oauthClient.Exchange(context.Background(), q.Get("code"))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Exchange token error:", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c, err := gitea.NewClient(os.Getenv("GITEA_SERVER"), gitea.SetToken(exchange.AccessToken))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Create client error:", err)
|
|
|
|
return
|
|
|
|
}
|
2022-03-26 00:26:20 +00:00
|
|
|
state.Put(KeyOauthClient, c)
|
|
|
|
state.Put(KeyAccessToken, exchange.AccessToken)
|
|
|
|
state.Put(KeyRefreshToken, exchange.RefreshToken)
|
2022-03-25 01:01:46 +00:00
|
|
|
http.Redirect(rw, req, "/gitea", http.StatusTemporaryRedirect)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Error(rw, "OAuth flow state doesn't match\n", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
flowState := uuid.New()
|
2022-03-26 00:26:20 +00:00
|
|
|
state.Put(KeyState, flowState)
|
2022-03-25 01:01:46 +00:00
|
|
|
|
|
|
|
http.Redirect(rw, req, m.oauthClient.AuthCodeURL(flowState.String(), oauth2.AccessTypeOffline), http.StatusTemporaryRedirect)
|
2022-03-23 00:39:53 +00:00
|
|
|
}
|
|
|
|
|
2022-03-24 21:07:44 +00:00
|
|
|
func (m *Module) fetchRepos(giteaClient *gitea.Client) {
|
|
|
|
repos, _, err := giteaClient.ListOrgRepos("snow", gitea.ListOrgReposOptions{ListOptions: gitea.ListOptions{Page: 0, PageSize: 100}})
|
2022-03-23 00:39:53 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, myRepo := range repos {
|
2022-03-24 21:07:44 +00:00
|
|
|
refs, _, err := giteaClient.GetRepoRefs("snow", myRepo.Name, "heads")
|
2022-03-23 00:39:53 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Println(len(refs))
|
|
|
|
for _, myRef := range refs {
|
|
|
|
if myRef.Ref == "refs/heads/"+myRepo.DefaultBranch {
|
|
|
|
fmt.Println(myRef.Ref)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Println("Can't find default branch")
|
|
|
|
}
|
|
|
|
}
|