Compare commits
1 Commits
master
...
feature/wi
Author | SHA1 | Date |
---|---|---|
![]() |
8011636a53 |
|
@ -1,42 +0,0 @@
|
||||||
// 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)),
|
|
||||||
}
|
|
||||||
}
|
|
16
cmd/run.go
16
cmd/run.go
|
@ -17,7 +17,8 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.com/jonasfranz/staletea/web"
|
"gitea.com/jonasfranz/staletea/config"
|
||||||
|
"gitea.com/jonasfranz/staletea/jobs"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,14 +26,13 @@ import (
|
||||||
var CmdRun = cli.Command{
|
var CmdRun = cli.Command{
|
||||||
Action: run,
|
Action: run,
|
||||||
Name: "run",
|
Name: "run",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{},
|
||||||
cli.StringFlag{
|
|
||||||
Name: "address",
|
|
||||||
Value: ":3030",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(ctx *cli.Context) error {
|
func run(ctx *cli.Context) error {
|
||||||
return web.StartServer(ctx.String("address"))
|
println("Startup!")
|
||||||
|
if err := config.SetupConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return jobs.StartCronJobs()
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,56 +41,40 @@ func (k Key) setDefault(defaultValue interface{}) {
|
||||||
|
|
||||||
// This block contains all config keys
|
// This block contains all config keys
|
||||||
const (
|
const (
|
||||||
DaysUntilStale Key = "daysUntilStale"
|
GiteaURL Key = "gitea_url"
|
||||||
DaysUntilClose Key = "daysUntilClose"
|
BotAccessToken Key = "gitea_bot_access_token"
|
||||||
LabelBlacklist Key = "onlyLabels"
|
|
||||||
LabelWhitelist Key = "exemptLabels"
|
|
||||||
StaleLabel Key = "staleLabel"
|
|
||||||
|
|
||||||
MarkComment Key = "markComment"
|
SessionSecret Key = "session_secret"
|
||||||
UnmarkComment Key = "unmarkComment"
|
DatabaseDriver Key = "database_driver"
|
||||||
CloseComment Key = "closeComment"
|
DatabaseURI Key = "database_uri"
|
||||||
|
|
||||||
TypeBlacklist Key = "only"
|
RepositoryScanInterval Key = "repository_scan_interval"
|
||||||
|
IssueScanInterval Key = "issue_scan_interval"
|
||||||
|
|
||||||
BaseURL Key = "host"
|
RepositoryConfigFileName Key = "repository_config_file_name"
|
||||||
|
|
||||||
GiteaURL Key = "gitea.url"
|
|
||||||
GiteaClientID Key = "gitea.client_id"
|
|
||||||
GiteaClientSecret Key = "gitea.client_secret"
|
|
||||||
|
|
||||||
SessionSecret Key = "session_secret"
|
|
||||||
DatabasePath Key = "./database_path"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetupConfig fills all default values, reads the config and/or writes the default config.
|
// SetupConfig fills all default values, reads the config and/or writes the default config.
|
||||||
func SetupConfig() error {
|
func SetupConfig() error {
|
||||||
DaysUntilStale.setDefault(60)
|
|
||||||
DaysUntilClose.setDefault(7)
|
|
||||||
LabelBlacklist.setDefault([]string{})
|
|
||||||
LabelWhitelist.setDefault([]string{})
|
|
||||||
StaleLabel.setDefault("wontfix")
|
|
||||||
|
|
||||||
MarkComment.setDefault(`This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed in %d days if no further activity occurs. Thank you
|
|
||||||
for your contributions.`)
|
|
||||||
UnmarkComment.setDefault("")
|
|
||||||
CloseComment.setDefault("This issue was closed automatically since it was marked as stale and had no recent activity.")
|
|
||||||
TypeBlacklist.setDefault([]string{})
|
|
||||||
BaseURL.setDefault("http://localhost")
|
|
||||||
GiteaURL.setDefault("https://gitea.com")
|
GiteaURL.setDefault("https://gitea.com")
|
||||||
GiteaClientID.setDefault("")
|
BotAccessToken.setDefault("YOUR_BOT_TOKEN")
|
||||||
GiteaClientSecret.setDefault("")
|
|
||||||
|
|
||||||
secret, err := utils.NewSecret(32)
|
secret, err := utils.NewSecret(32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
SessionSecret.setDefault(secret)
|
SessionSecret.setDefault(secret)
|
||||||
DatabasePath.setDefault("staletea.db")
|
DatabaseDriver.setDefault("sqlite3")
|
||||||
|
DatabaseURI.setDefault("staletea.db")
|
||||||
|
|
||||||
|
RepositoryScanInterval.setDefault("@every 30s")
|
||||||
|
IssueScanInterval.setDefault("@every 2m")
|
||||||
|
|
||||||
|
RepositoryConfigFileName.setDefault(".stale.yml")
|
||||||
|
|
||||||
viper.SetConfigName("config")
|
viper.SetConfigName("config")
|
||||||
viper.SetConfigType("yml")
|
viper.SetConfigType("yml")
|
||||||
|
viper.AutomaticEnv()
|
||||||
viper.AddConfigPath(".")
|
viper.AddConfigPath(".")
|
||||||
if err := viper.SafeWriteConfigAs("config.yml"); err != nil {
|
if err := viper.SafeWriteConfigAs("config.yml"); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -12,6 +12,7 @@ require (
|
||||||
github.com/lib/pq v1.1.0 // indirect
|
github.com/lib/pq v1.1.0 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.10.0
|
github.com/mattn/go-sqlite3 v1.10.0
|
||||||
github.com/pkg/errors v0.8.1 // indirect
|
github.com/pkg/errors v0.8.1 // indirect
|
||||||
|
github.com/robfig/cron v1.2.0
|
||||||
github.com/spf13/viper v1.4.0
|
github.com/spf13/viper v1.4.0
|
||||||
github.com/urfave/cli v1.20.0
|
github.com/urfave/cli v1.20.0
|
||||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect
|
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect
|
||||||
|
@ -19,4 +20,5 @@ require (
|
||||||
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0
|
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0
|
||||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect
|
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect
|
||||||
golang.org/x/text v0.3.2 // indirect
|
golang.org/x/text v0.3.2 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -137,6 +137,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||||
|
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||||
|
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// 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)
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// 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)
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// staletea
|
// staletea
|
||||||
// Copyright (C) 2019 Jonas Franz
|
// Copyright (C) 2020 Jonas Franz
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -14,26 +14,15 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package models
|
package loader
|
||||||
|
|
||||||
import (
|
type Repository struct {
|
||||||
"code.gitea.io/sdk/gitea"
|
ID int64
|
||||||
"context"
|
Owner string
|
||||||
"gitea.com/jonasfranz/staletea/auth"
|
Name string
|
||||||
"gitea.com/jonasfranz/staletea/config"
|
ConfigBranch string
|
||||||
"golang.org/x/oauth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// User represents a signed in oauth2 gitea user saved in a session
|
|
||||||
type User struct {
|
|
||||||
ID int64
|
|
||||||
Username string
|
|
||||||
Token *oauth2.Token
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GiteaClient will return a gitea client with authentication of the user
|
type RepositoryConfig struct {
|
||||||
func (user *User) GiteaClient() *gitea.Client {
|
Enabled bool `yaml:"enabled"`
|
||||||
client := gitea.NewClient(config.GiteaURL.Get().(string), "")
|
|
||||||
client.SetHTTPClient(auth.Config().Client(context.Background(), user.Token))
|
|
||||||
return client
|
|
||||||
}
|
}
|
|
@ -30,7 +30,7 @@ var x *xorm.Engine
|
||||||
// SetupDatabase will init a sqlite3 database and sync all models
|
// SetupDatabase will init a sqlite3 database and sync all models
|
||||||
func SetupDatabase() {
|
func SetupDatabase() {
|
||||||
var err error
|
var err error
|
||||||
x, err = xorm.NewEngine("sqlite3", config.DatabasePath.Get().(string))
|
x, err = xorm.NewEngine("sqlite3", config.DatabaseURI.Get().(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,24 +16,36 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "github.com/go-xorm/xorm"
|
import (
|
||||||
|
"gitea.com/jonasfranz/staletea/loader"
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
// Repository represents a Gitea repository indexed at the local database
|
// Repository represents a Gitea repository indexed at the local database
|
||||||
type Repository struct {
|
type Repository struct {
|
||||||
ID int64 `xorm:"pk"`
|
ID int64 `xorm:"pk"`
|
||||||
UserID int64 `xorm:"index"`
|
Config *loader.RepositoryConfig `xorm:"json"`
|
||||||
Owner string `xorm:"unique(owner_name)"`
|
|
||||||
Name string `xorm:"unique(owner_name)"`
|
|
||||||
|
|
||||||
Activated bool
|
Activated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindRepositoriesByUserID returns all repos of an user
|
func FindAllRepositories() ([]*Repository, error) {
|
||||||
func FindRepositoriesByUserID(userID int64) ([]*Repository, error) {
|
return findAllRepositories(x)
|
||||||
return findRepositoriesByUserID(x, userID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findRepositoriesByUserID(e *xorm.Engine, userID int64) ([]*Repository, error) {
|
func findAllRepositories(e *xorm.Engine) ([]*Repository, error) {
|
||||||
repos := make([]*Repository, 0)
|
repos := make([]*Repository, 0)
|
||||||
return repos, e.Where("user_id = ?", userID).Find(&repos)
|
if err := e.Find(&repos); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRepositories(repos []*Repository) error {
|
||||||
|
return createRepositories(x, repos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRepositories(e *xorm.Engine, repos []*Repository) error {
|
||||||
|
_, err := e.Insert(&repos)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
83
web/auth.go
83
web/auth.go
|
@ -1,83 +0,0 @@
|
||||||
// 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))
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
// 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) {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
// 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