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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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