Compare commits
No commits in common. 'feature/without-web' and 'master' have entirely different histories.
feature/wi
...
master
16 changed files with 312 additions and 274 deletions
@ -0,0 +1,42 @@ |
|||||||
|
// staletea |
||||||
|
// Copyright (C) 2019 Jonas Franz |
||||||
|
// |
||||||
|
// This program is free software: you can redistribute it and/or modify |
||||||
|
// it under the terms of the GNU General Public License as published by |
||||||
|
// the Free Software Foundation, either version 3 of the License, or |
||||||
|
// (at your option) any later version. |
||||||
|
// |
||||||
|
// This program is distributed in the hope that it will be useful, |
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
// GNU General Public License for more details. |
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License |
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
package auth |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"gitea.com/jonasfranz/staletea/config" |
||||||
|
"golang.org/x/oauth2" |
||||||
|
) |
||||||
|
|
||||||
|
func endpoint() oauth2.Endpoint { |
||||||
|
// Endpoint is Google's OAuth 2.0 endpoint. |
||||||
|
return oauth2.Endpoint{ |
||||||
|
AuthURL: fmt.Sprintf("%s/login/oauth/authorize", config.GiteaURL.Get().(string)), |
||||||
|
TokenURL: fmt.Sprintf("%s/login/oauth/access_token", config.GiteaURL.Get().(string)), |
||||||
|
AuthStyle: oauth2.AuthStyleInParams, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Config returns an oauth2 config |
||||||
|
func Config() *oauth2.Config { |
||||||
|
return &oauth2.Config{ |
||||||
|
ClientID: config.GiteaClientID.Get().(string), |
||||||
|
ClientSecret: config.GiteaClientSecret.Get().(string), |
||||||
|
Endpoint: endpoint(), |
||||||
|
RedirectURL: fmt.Sprintf("%s/callback", config.BaseURL.Get().(string)), |
||||||
|
} |
||||||
|
} |
@ -1,21 +0,0 @@ |
|||||||
// staletea |
|
||||||
// Copyright (C) 2020 Jonas Franz |
|
||||||
// |
|
||||||
// This program is free software: you can redistribute it and/or modify |
|
||||||
// it under the terms of the GNU General Public License as published by |
|
||||||
// the Free Software Foundation, either version 3 of the License, or |
|
||||||
// (at your option) any later version. |
|
||||||
// |
|
||||||
// This program is distributed in the hope that it will be useful, |
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
// GNU General Public License for more details. |
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License |
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
|
|
||||||
package jobs |
|
||||||
|
|
||||||
func syncIssues() error { |
|
||||||
return nil |
|
||||||
} |
|
@ -1,51 +0,0 @@ |
|||||||
// staletea |
|
||||||
// Copyright (C) 2020 Jonas Franz |
|
||||||
// |
|
||||||
// This program is free software: you can redistribute it and/or modify |
|
||||||
// it under the terms of the GNU General Public License as published by |
|
||||||
// the Free Software Foundation, either version 3 of the License, or |
|
||||||
// (at your option) any later version. |
|
||||||
// |
|
||||||
// This program is distributed in the hope that it will be useful, |
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
// GNU General Public License for more details. |
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License |
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
|
|
||||||
package jobs |
|
||||||
|
|
||||||
import "github.com/robfig/cron" |
|
||||||
|
|
||||||
import "gitea.com/jonasfranz/staletea/config" |
|
||||||
|
|
||||||
type Job struct { |
|
||||||
Interval config.Key |
|
||||||
Action func() error |
|
||||||
} |
|
||||||
|
|
||||||
var registeredCronJobs []Job = []Job{ |
|
||||||
{Interval: config.RepositoryScanInterval, Action: syncRepositories}, |
|
||||||
{Interval: config.IssueScanInterval, Action: syncIssues}, |
|
||||||
} |
|
||||||
|
|
||||||
func handleError(unsafeFunc func() error) func() { |
|
||||||
return func() { |
|
||||||
err := unsafeFunc() |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func StartCronJobs() error { |
|
||||||
c := cron.New() |
|
||||||
for _, job := range registeredCronJobs { |
|
||||||
if err := c.AddFunc(job.Interval.Get().(string), handleError(job.Action)); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
c.Run() |
|
||||||
return nil |
|
||||||
} |
|
@ -1,56 +0,0 @@ |
|||||||
// staletea |
|
||||||
// Copyright (C) 2020 Jonas Franz |
|
||||||
// |
|
||||||
// This program is free software: you can redistribute it and/or modify |
|
||||||
// it under the terms of the GNU General Public License as published by |
|
||||||
// the Free Software Foundation, either version 3 of the License, or |
|
||||||
// (at your option) any later version. |
|
||||||
// |
|
||||||
// This program is distributed in the hope that it will be useful, |
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
// GNU General Public License for more details. |
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License |
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
|
|
||||||
package jobs |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"gitea.com/jonasfranz/staletea/config" |
|
||||||
"gitea.com/jonasfranz/staletea/loader" |
|
||||||
"gitea.com/jonasfranz/staletea/models" |
|
||||||
) |
|
||||||
|
|
||||||
func syncRepositories() error { |
|
||||||
savedRepos, err := models.FindAllRepositories() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
savedRepoIDs := make(map[int64]*models.Repository) |
|
||||||
for _, savedRepo := range savedRepos { |
|
||||||
savedRepoIDs[savedRepo.ID] = savedRepo |
|
||||||
} |
|
||||||
l := loader.NewGiteaLoader(config.GiteaURL.Get().(string), config.BotAccessToken.Get().(string)) |
|
||||||
loadedRepos, err := l.LoadRepositories() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
newRepos := make([]*models.Repository, 0) |
|
||||||
for _, loadedRepo := range loadedRepos { |
|
||||||
if _, ok := savedRepoIDs[loadedRepo.ID]; !ok { |
|
||||||
repoConfig, err := l.DownloadConfig(loadedRepo) |
|
||||||
if err != nil { |
|
||||||
fmt.Printf("cannot download config for repo %s/%s: %v\n", loadedRepo.Owner, loadedRepo.Name, err) |
|
||||||
continue |
|
||||||
} |
|
||||||
newRepos = append(newRepos, &models.Repository{ |
|
||||||
ID: loadedRepo.ID, |
|
||||||
Config: repoConfig, |
|
||||||
Activated: true, |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
return models.CreateRepositories(newRepos) |
|
||||||
} |
|
@ -1,63 +0,0 @@ |
|||||||
// staletea |
|
||||||
// Copyright (C) 2020 Jonas Franz |
|
||||||
// |
|
||||||
// This program is free software: you can redistribute it and/or modify |
|
||||||
// it under the terms of the GNU General Public License as published by |
|
||||||
// the Free Software Foundation, either version 3 of the License, or |
|
||||||
// (at your option) any later version. |
|
||||||
// |
|
||||||
// This program is distributed in the hope that it will be useful, |
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
// GNU General Public License for more details. |
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License |
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
|
|
||||||
package loader |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"code.gitea.io/sdk/gitea" |
|
||||||
"gitea.com/jonasfranz/staletea/config" |
|
||||||
"gopkg.in/yaml.v2" |
|
||||||
) |
|
||||||
|
|
||||||
func NewGiteaLoader(url, token string) Loader { |
|
||||||
return &GiteaLoader{ |
|
||||||
client: gitea.NewClient(url, token), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
type GiteaLoader struct { |
|
||||||
client *gitea.Client |
|
||||||
} |
|
||||||
|
|
||||||
func (l *GiteaLoader) LoadRepositories() ([]*Repository, error) { |
|
||||||
repos, err := l.client.ListMyRepos() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
convertedRepos := make([]*Repository, len(repos)) |
|
||||||
for index, repo := range repos { |
|
||||||
convertedRepos[index] = &Repository{ |
|
||||||
ID: repo.ID, |
|
||||||
Owner: repo.Owner.UserName, |
|
||||||
Name: repo.Name, |
|
||||||
ConfigBranch: repo.DefaultBranch, |
|
||||||
} |
|
||||||
} |
|
||||||
return convertedRepos, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (l *GiteaLoader) DownloadConfig(repo *Repository) (*RepositoryConfig, error) { |
|
||||||
file, err := l.client.GetFile(repo.Owner, repo.Name, repo.ConfigBranch, config.RepositoryConfigFileName.Get().(string)) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
repoConfig := new(RepositoryConfig) |
|
||||||
if err := yaml.NewDecoder(bytes.NewReader(file)).Decode(repoConfig); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return repoConfig, nil |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
// staletea |
|
||||||
// Copyright (C) 2020 Jonas Franz |
|
||||||
// |
|
||||||
// This program is free software: you can redistribute it and/or modify |
|
||||||
// it under the terms of the GNU General Public License as published by |
|
||||||
// the Free Software Foundation, either version 3 of the License, or |
|
||||||
// (at your option) any later version. |
|
||||||
// |
|
||||||
// This program is distributed in the hope that it will be useful, |
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
// GNU General Public License for more details. |
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License |
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
|
|
||||||
package loader |
|
||||||
|
|
||||||
type Loader interface { |
|
||||||
LoadRepositories() ([]*Repository, error) |
|
||||||
DownloadConfig(repo *Repository) (*RepositoryConfig, error) |
|
||||||
} |
|
@ -0,0 +1,83 @@ |
|||||||
|
// staletea |
||||||
|
// Copyright (C) 2019 Jonas Franz |
||||||
|
// |
||||||
|
// This program is free software: you can redistribute it and/or modify |
||||||
|
// it under the terms of the GNU General Public License as published by |
||||||
|
// the Free Software Foundation, either version 3 of the License, or |
||||||
|
// (at your option) any later version. |
||||||
|
// |
||||||
|
// This program is distributed in the hope that it will be useful, |
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
// GNU General Public License for more details. |
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License |
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
package web |
||||||
|
|
||||||
|
import ( |
||||||
|
"code.gitea.io/sdk/gitea" |
||||||
|
"context" |
||||||
|
"gitea.com/jonasfranz/staletea/auth" |
||||||
|
"gitea.com/jonasfranz/staletea/config" |
||||||
|
"gitea.com/jonasfranz/staletea/models" |
||||||
|
"gitea.com/jonasfranz/staletea/utils" |
||||||
|
"github.com/gin-contrib/sessions" |
||||||
|
"github.com/gin-gonic/gin" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
func handleLogin(ctx *gin.Context) { |
||||||
|
session := sessions.Default(ctx) |
||||||
|
state, err := utils.NewSecret(32) |
||||||
|
if err != nil { |
||||||
|
_ = ctx.AbortWithError(500, err) |
||||||
|
return |
||||||
|
} |
||||||
|
session.Set("state", state) |
||||||
|
if err := session.Save(); err != nil { |
||||||
|
_ = ctx.AbortWithError(http.StatusInternalServerError, err) |
||||||
|
return |
||||||
|
} |
||||||
|
redirectURL := auth.Config().AuthCodeURL(state) |
||||||
|
ctx.Redirect(http.StatusTemporaryRedirect, redirectURL) |
||||||
|
} |
||||||
|
|
||||||
|
func handleCallback(ctx *gin.Context) { |
||||||
|
session := sessions.Default(ctx) |
||||||
|
state := ctx.Query("state") |
||||||
|
savedState := session.Get("state") |
||||||
|
if savedState == nil { |
||||||
|
ctx.String(http.StatusUnauthorized, "Invalid state") |
||||||
|
return |
||||||
|
} |
||||||
|
session.Delete("state") |
||||||
|
if parsedState, ok := savedState.(string); !ok || parsedState != state { |
||||||
|
ctx.String(http.StatusUnauthorized, "Invalid state") |
||||||
|
return |
||||||
|
} |
||||||
|
token, err := auth.Config().Exchange(ctx, ctx.Query("code")) |
||||||
|
if err != nil { |
||||||
|
_ = ctx.AbortWithError(http.StatusUnauthorized, err) |
||||||
|
return |
||||||
|
} |
||||||
|
client := gitea.NewClient(config.GiteaURL.Get().(string), "") |
||||||
|
client.SetHTTPClient(auth.Config().Client(context.Background(), token)) |
||||||
|
user, err := client.GetMyUserInfo() |
||||||
|
if err != nil { |
||||||
|
_ = ctx.AbortWithError(http.StatusUnauthorized, err) |
||||||
|
return |
||||||
|
} |
||||||
|
storedUser := &models.User{ |
||||||
|
ID: user.ID, |
||||||
|
Username: user.UserName, |
||||||
|
Token: token, |
||||||
|
} |
||||||
|
session.Set("user", storedUser) |
||||||
|
if err := session.Save(); err != nil { |
||||||
|
_ = ctx.AbortWithError(http.StatusInternalServerError, err) |
||||||
|
return |
||||||
|
} |
||||||
|
ctx.Redirect(http.StatusTemporaryRedirect, config.BaseURL.Get().(string)) |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
// staletea |
||||||
|
// Copyright (C) 2019 Jonas Franz |
||||||
|
// |
||||||
|
// This program is free software: you can redistribute it and/or modify |
||||||
|
// it under the terms of the GNU General Public License as published by |
||||||
|
// the Free Software Foundation, either version 3 of the License, or |
||||||
|
// (at your option) any later version. |
||||||
|
// |
||||||
|
// This program is distributed in the hope that it will be useful, |
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
// GNU General Public License for more details. |
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License |
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
package web |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"gitea.com/jonasfranz/staletea/config" |
||||||
|
"gitea.com/jonasfranz/staletea/models" |
||||||
|
"github.com/gin-contrib/sessions" |
||||||
|
"github.com/gin-gonic/gin" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
func showDashboard(ctx *gin.Context) { |
||||||
|
session := sessions.Default(ctx) |
||||||
|
user, ok := session.Get("user").(*models.User) |
||||||
|
if !ok || user == nil { |
||||||
|
ctx.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/login", config.BaseURL.Get().(string))) |
||||||
|
return |
||||||
|
} |
||||||
|
activatedRepos, err := models.FindRepositoriesByUserID(user.ID) |
||||||
|
if err != nil { |
||||||
|
_ = ctx.AbortWithError(500, err) |
||||||
|
return |
||||||
|
} |
||||||
|
remoteRepos, err := user.GiteaClient().ListMyRepos() |
||||||
|
if err != nil { |
||||||
|
_ = ctx.AbortWithError(500, err) |
||||||
|
return |
||||||
|
} |
||||||
|
combinedRepos := make(map[int64]*models.Repository, len(activatedRepos)) |
||||||
|
|
||||||
|
for _, repo := range activatedRepos { |
||||||
|
combinedRepos[repo.ID] = repo |
||||||
|
} |
||||||
|
|
||||||
|
for _, repo := range remoteRepos { |
||||||
|
combinedRepos[repo.ID] = &models.Repository{ |
||||||
|
ID: repo.ID, |
||||||
|
UserID: user.ID, |
||||||
|
Owner: repo.Owner.UserName, |
||||||
|
Name: repo.Name, |
||||||
|
Activated: false, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
data := map[string]interface{}{ |
||||||
|
"repos": combinedRepos, |
||||||
|
"user": user, |
||||||
|
} |
||||||
|
|
||||||
|
ctx.HTML(200, "dashboard.tmpl", data) |
||||||
|
} |
||||||
|
|
||||||
|
func handleActivate(ctx *gin.Context) { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
// staletea |
||||||
|
// Copyright (C) 2019 Jonas Franz |
||||||
|
// |
||||||
|
// This program is free software: you can redistribute it and/or modify |
||||||
|
// it under the terms of the GNU General Public License as published by |
||||||
|
// the Free Software Foundation, either version 3 of the License, or |
||||||
|
// (at your option) any later version. |
||||||
|
// |
||||||
|
// This program is distributed in the hope that it will be useful, |
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
// GNU General Public License for more details. |
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License |
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
package web |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/gob" |
||||||
|
"gitea.com/jonasfranz/staletea/config" |
||||||
|
"gitea.com/jonasfranz/staletea/models" |
||||||
|
"github.com/gin-contrib/sessions" |
||||||
|
"github.com/gin-contrib/sessions/cookie" |
||||||
|
"github.com/gin-gonic/gin" |
||||||
|
) |
||||||
|
|
||||||
|
// StartServer will start the webserver at the given addresses |
||||||
|
func StartServer(addr ...string) error { |
||||||
|
r := gin.Default() |
||||||
|
gob.Register(new(models.User)) |
||||||
|
store := cookie.NewStore([]byte(config.SessionSecret.Get().(string))) |
||||||
|
r.Use(sessions.Sessions("sessions", store)) |
||||||
|
r.LoadHTMLFiles("templates/dashboard.tmpl") |
||||||
|
setupRoutes(r) |
||||||
|
return r.Run(addr...) |
||||||
|
} |
||||||
|
|
||||||
|
func setupRoutes(r *gin.Engine) { |
||||||
|
r.GET("/", showDashboard) |
||||||
|
r.POST("/", handleActivate) |
||||||
|
r.GET("/login", handleLogin) |
||||||
|
r.GET("/callback", handleCallback) |
||||||
|
} |
Loading…
Reference in new issue