mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-01-04 06:38:43 +00:00
simplify gating logic
This commit is contained in:
parent
4aac0bc4d6
commit
3a64aa4cf2
15 changed files with 168 additions and 105 deletions
|
@ -400,7 +400,7 @@ func setupEvilGlobals(c *cli.Context, v store.Store) {
|
||||||
droneserver.Config.Services.Pubsub.Create(context.Background(), "topic/events")
|
droneserver.Config.Services.Pubsub.Create(context.Background(), "topic/events")
|
||||||
droneserver.Config.Services.Registries = registry.New(v)
|
droneserver.Config.Services.Registries = registry.New(v)
|
||||||
droneserver.Config.Services.Secrets = secrets.New(v)
|
droneserver.Config.Services.Secrets = secrets.New(v)
|
||||||
droneserver.Config.Services.Senders = sender.New(v)
|
droneserver.Config.Services.Senders = sender.New(v, v)
|
||||||
if endpoint := c.String("registry-service"); endpoint != "" {
|
if endpoint := c.String("registry-service"); endpoint != "" {
|
||||||
droneserver.Config.Services.Registries = registry.NewRemote(endpoint)
|
droneserver.Config.Services.Registries = registry.NewRemote(endpoint)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ package model
|
||||||
type ConfigStore interface {
|
type ConfigStore interface {
|
||||||
ConfigLoad(int64) (*Config, error)
|
ConfigLoad(int64) (*Config, error)
|
||||||
ConfigFind(*Repo, string) (*Config, error)
|
ConfigFind(*Repo, string) (*Config, error)
|
||||||
ConfigUpdate(*Config) error
|
ConfigFindApproved(*Config) (bool, error)
|
||||||
ConfigInsert(*Config) error
|
ConfigCreate(*Config) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config represents a pipeline configuration.
|
// Config represents a pipeline configuration.
|
||||||
|
@ -14,5 +14,4 @@ type Config struct {
|
||||||
RepoID int64 `json:"-" meddler:"config_repo_id"`
|
RepoID int64 `json:"-" meddler:"config_repo_id"`
|
||||||
Data string `json:"data" meddler:"config_data"`
|
Data string `json:"data" meddler:"config_data"`
|
||||||
Hash string `json:"hash" meddler:"config_hash"`
|
Hash string `json:"hash" meddler:"config_hash"`
|
||||||
Approved bool `json:"approved" meddler:"config_approved"`
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,23 @@ import (
|
||||||
|
|
||||||
type builtin struct {
|
type builtin struct {
|
||||||
store model.SenderStore
|
store model.SenderStore
|
||||||
|
conf model.ConfigStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new local gating service.
|
// New returns a new local gating service.
|
||||||
func New(store model.SenderStore) model.SenderService {
|
func New(store model.SenderStore, conf model.ConfigStore) model.SenderService {
|
||||||
return &builtin{store}
|
return &builtin{store, conf}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builtin) SenderAllowed(user *model.User, repo *model.Repo, build *model.Build, conf *model.Config) (bool, error) {
|
func (b *builtin) SenderAllowed(user *model.User, repo *model.Repo, build *model.Build, conf *model.Config) (bool, error) {
|
||||||
if !conf.Approved {
|
if build.Event == model.EventPull && build.Sender != user.Login {
|
||||||
|
// check to see if the configuration has already been used in an
|
||||||
|
// existing build. If yes it is considered approved.
|
||||||
|
if ok, _ := b.conf.ConfigFindApproved(conf); ok {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
// else check to see if the configuration is sent from a user
|
||||||
|
// account that is a repositroy approver themselves.
|
||||||
sender, err := b.store.SenderFind(repo, build.Sender)
|
sender, err := b.store.SenderFind(repo, build.Sender)
|
||||||
if err != nil || sender.Block {
|
if err != nil || sender.Block {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|
|
@ -180,10 +180,6 @@ func PostApproval(c *gin.Context) {
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !conf.Approved {
|
|
||||||
conf.Approved = true
|
|
||||||
Config.Storage.Config.ConfigUpdate(conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
netrc, err := remote_.Netrc(user, repo)
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -404,10 +400,6 @@ func PostBuild(c *gin.Context) {
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !conf.Approved {
|
|
||||||
conf.Approved = true
|
|
||||||
Config.Storage.Config.ConfigUpdate(conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
netrc, err := remote_.Netrc(user, repo)
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -145,24 +145,14 @@ func PostHook(c *gin.Context) {
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Data: string(confb),
|
Data: string(confb),
|
||||||
Hash: sha,
|
Hash: sha,
|
||||||
Approved: false,
|
|
||||||
}
|
}
|
||||||
if user.Login == repo.Owner || build.Event != model.EventPull || repo.IsGated == false {
|
err = Config.Storage.Config.ConfigCreate(conf)
|
||||||
conf.Approved = true
|
|
||||||
}
|
|
||||||
err = Config.Storage.Config.ConfigInsert(conf)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failure to persist config for %s. %s", repo.FullName, err)
|
logrus.Errorf("failure to persist config for %s. %s", repo.FullName, err)
|
||||||
c.AbortWithError(500, err)
|
c.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !conf.Approved {
|
|
||||||
if user.Login == repo.Owner || build.Event != model.EventPull || repo.IsGated == false {
|
|
||||||
conf.Approved = true
|
|
||||||
Config.Storage.Config.ConfigUpdate(conf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
build.ConfigID = conf.ID
|
build.ConfigID = conf.ID
|
||||||
|
|
||||||
netrc, err := remote_.Netrc(user, repo)
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
package datastore
|
package datastore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
gosql "database/sql"
|
||||||
|
|
||||||
"github.com/drone/drone/model"
|
"github.com/drone/drone/model"
|
||||||
"github.com/drone/drone/store/datastore/sql"
|
"github.com/drone/drone/store/datastore/sql"
|
||||||
"github.com/russross/meddler"
|
"github.com/russross/meddler"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *datastore) ConfigLoad(id int64) (*model.Config, error) {
|
func (db *datastore) ConfigLoad(id int64) (*model.Config, error) {
|
||||||
stmt := sql.Lookup(db.driver, "config-find-repo-id")
|
stmt := sql.Lookup(db.driver, "config-find-id")
|
||||||
conf := new(model.Config)
|
conf := new(model.Config)
|
||||||
err := meddler.QueryRow(db, conf, stmt, id)
|
err := meddler.QueryRow(db, conf, stmt, id)
|
||||||
return conf, err
|
return conf, err
|
||||||
|
@ -20,10 +22,18 @@ func (db *datastore) ConfigFind(repo *model.Repo, hash string) (*model.Config, e
|
||||||
return conf, err
|
return conf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *datastore) ConfigUpdate(config *model.Config) error {
|
func (db *datastore) ConfigFindApproved(config *model.Config) (bool, error) {
|
||||||
return meddler.Update(db, "config", config)
|
var dest int64
|
||||||
|
stmt := sql.Lookup(db.driver, "config-find-approved")
|
||||||
|
err := db.DB.QueryRow(stmt, config.RepoID, config.ID).Scan(&dest)
|
||||||
|
if err == gosql.ErrNoRows {
|
||||||
|
return false, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *datastore) ConfigInsert(config *model.Config) error {
|
func (db *datastore) ConfigCreate(config *model.Config) error {
|
||||||
return meddler.Insert(db, "config", config)
|
return meddler.Insert(db, "config", config)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,11 @@ func TestConfig(t *testing.T) {
|
||||||
hash = "8d8647c9aa90d893bfb79dddbe901f03e258588121e5202632f8ae5738590b26"
|
hash = "8d8647c9aa90d893bfb79dddbe901f03e258588121e5202632f8ae5738590b26"
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := s.ConfigInsert(
|
if err := s.ConfigCreate(
|
||||||
&model.Config{
|
&model.Config{
|
||||||
RepoID: 2,
|
RepoID: 2,
|
||||||
Data: data,
|
Data: data,
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Approved: false,
|
|
||||||
},
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
t.Errorf("Unexpected error: insert config: %s", err)
|
t.Errorf("Unexpected error: insert config: %s", err)
|
||||||
|
@ -47,60 +46,102 @@ func TestConfig(t *testing.T) {
|
||||||
if got, want := config.Hash, hash; got != want {
|
if got, want := config.Hash, hash; got != want {
|
||||||
t.Errorf("Want config hash %s, got %s", want, got)
|
t.Errorf("Want config hash %s, got %s", want, got)
|
||||||
}
|
}
|
||||||
if got, want := config.Approved, false; got != want {
|
|
||||||
t.Errorf("Want config approved %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Approved = true
|
loaded, err := s.ConfigLoad(config.ID)
|
||||||
err = s.ConfigUpdate(config)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Want config updated, got error %q", err)
|
t.Errorf("Want config by id, got error %q", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if got, want := loaded.ID, config.ID; got != want {
|
||||||
updated, err := s.ConfigFind(&model.Repo{ID: 2}, hash)
|
t.Errorf("Want config by id %d, got %d", want, got)
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Want config find, got error %q", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if got, want := updated.Approved, true; got != want {
|
|
||||||
t.Errorf("Want config approved updated %v, got %v", want, got)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
func TestConfigApproved(t *testing.T) {
|
||||||
// func TestConfigIndexes(t *testing.T) {
|
s := newTest()
|
||||||
// s := newTest()
|
defer func() {
|
||||||
// defer func() {
|
s.Exec("delete from config")
|
||||||
// s.Exec("delete from config")
|
s.Exec("delete from builds")
|
||||||
// s.Close()
|
s.Close()
|
||||||
// }()
|
}()
|
||||||
//
|
|
||||||
// if err := s.FileCreate(
|
var (
|
||||||
// &model.File{
|
data = "pipeline: [ { image: golang, commands: [ go build, go test ] } ]"
|
||||||
// BuildID: 1,
|
hash = "8d8647c9aa90d893bfb79dddbe901f03e258588121e5202632f8ae5738590b26"
|
||||||
// ProcID: 1,
|
conf = &model.Config{
|
||||||
// Name: "hello.txt",
|
RepoID: 1,
|
||||||
// Size: 11,
|
Data: data,
|
||||||
// Mime: "text/plain",
|
Hash: hash,
|
||||||
// },
|
}
|
||||||
// bytes.NewBufferString("hello world"),
|
)
|
||||||
// ); err != nil {
|
|
||||||
// t.Errorf("Unexpected error: insert file: %s", err)
|
if err := s.ConfigCreate(conf); err != nil {
|
||||||
// return
|
t.Errorf("Unexpected error: insert config: %s", err)
|
||||||
// }
|
return
|
||||||
//
|
}
|
||||||
// // fail due to duplicate file name
|
|
||||||
// if err := s.FileCreate(
|
s.CreateBuild(&model.Build{
|
||||||
// &model.File{
|
RepoID: 1,
|
||||||
// BuildID: 1,
|
ConfigID: conf.ID,
|
||||||
// ProcID: 1,
|
Status: model.StatusBlocked,
|
||||||
// Name: "hello.txt",
|
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
|
||||||
// Mime: "text/plain",
|
})
|
||||||
// Size: 11,
|
s.CreateBuild(&model.Build{
|
||||||
// },
|
RepoID: 1,
|
||||||
// bytes.NewBufferString("hello world"),
|
ConfigID: conf.ID,
|
||||||
// ); err == nil {
|
Status: model.StatusPending,
|
||||||
// t.Errorf("Unexpected error: dupliate pid")
|
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
|
||||||
// }
|
})
|
||||||
// }
|
|
||||||
|
if ok, _ := s.ConfigFindApproved(conf); ok == true {
|
||||||
|
t.Errorf("Want config not approved, when blocked or pending")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.CreateBuild(&model.Build{
|
||||||
|
RepoID: 1,
|
||||||
|
ConfigID: conf.ID,
|
||||||
|
Status: model.StatusRunning,
|
||||||
|
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
|
||||||
|
})
|
||||||
|
|
||||||
|
if ok, _ := s.ConfigFindApproved(conf); ok == false {
|
||||||
|
t.Errorf("Want config approved, when running.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigIndexes(t *testing.T) {
|
||||||
|
s := newTest()
|
||||||
|
defer func() {
|
||||||
|
s.Exec("delete from config")
|
||||||
|
s.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var (
|
||||||
|
data = "pipeline: [ { image: golang, commands: [ go build, go test ] } ]"
|
||||||
|
hash = "8d8647c9aa90d893bfb79dddbe901f03e258588121e5202632f8ae5738590b26"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := s.ConfigCreate(
|
||||||
|
&model.Config{
|
||||||
|
RepoID: 2,
|
||||||
|
Data: data,
|
||||||
|
Hash: hash,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("Unexpected error: insert config: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fail due to duplicate sha
|
||||||
|
if err := s.ConfigCreate(
|
||||||
|
&model.Config{
|
||||||
|
RepoID: 2,
|
||||||
|
Data: data,
|
||||||
|
Hash: hash,
|
||||||
|
},
|
||||||
|
); err == nil {
|
||||||
|
t.Errorf("Unexpected error: dupliate sha")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ CREATE TABLE config (
|
||||||
,config_repo_id INTEGER
|
,config_repo_id INTEGER
|
||||||
,config_hash VARCHAR(250)
|
,config_hash VARCHAR(250)
|
||||||
,config_data MEDIUMBLOB
|
,config_data MEDIUMBLOB
|
||||||
,config_approved BOOLEAN
|
|
||||||
|
|
||||||
,UNIQUE(config_hash, config_repo_id)
|
,UNIQUE(config_hash, config_repo_id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,6 @@ CREATE TABLE config (
|
||||||
,config_repo_id INTEGER
|
,config_repo_id INTEGER
|
||||||
,config_hash VARCHAR(250)
|
,config_hash VARCHAR(250)
|
||||||
,config_data BYTEA
|
,config_data BYTEA
|
||||||
,config_approved BOOLEAN
|
|
||||||
|
|
||||||
,UNIQUE(config_hash, config_repo_id)
|
,UNIQUE(config_hash, config_repo_id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,6 @@ CREATE TABLE config (
|
||||||
,config_repo_id INTEGER
|
,config_repo_id INTEGER
|
||||||
,config_hash TEXT
|
,config_hash TEXT
|
||||||
,config_data BLOB
|
,config_data BLOB
|
||||||
,config_approved BOOLEAN
|
|
||||||
|
|
||||||
,UNIQUE(config_hash, config_repo_id)
|
,UNIQUE(config_hash, config_repo_id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,6 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_id = $1
|
WHERE config_id = $1
|
||||||
|
|
||||||
|
@ -16,7 +15,14 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_repo_id = $1
|
WHERE config_repo_id = $1
|
||||||
AND config_hash = $2
|
AND config_hash = $2
|
||||||
|
|
||||||
|
-- name: config-find-approved
|
||||||
|
|
||||||
|
SELECT build_id FROM builds
|
||||||
|
WHERE build_repo_id = $1
|
||||||
|
AND build_config_id = $2
|
||||||
|
AND build_status NOT IN ('blocked', 'pending')
|
||||||
|
LIMIT 1
|
||||||
|
|
|
@ -8,6 +8,7 @@ func Lookup(name string) string {
|
||||||
var index = map[string]string{
|
var index = map[string]string{
|
||||||
"config-find-id": configFindId,
|
"config-find-id": configFindId,
|
||||||
"config-find-repo-hash": configFindRepoHash,
|
"config-find-repo-hash": configFindRepoHash,
|
||||||
|
"config-find-approved": configFindApproved,
|
||||||
"count-users": countUsers,
|
"count-users": countUsers,
|
||||||
"count-repos": countRepos,
|
"count-repos": countRepos,
|
||||||
"count-builds": countBuilds,
|
"count-builds": countBuilds,
|
||||||
|
@ -41,7 +42,6 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_id = $1
|
WHERE config_id = $1
|
||||||
`
|
`
|
||||||
|
@ -52,12 +52,19 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_repo_id = $1
|
WHERE config_repo_id = $1
|
||||||
AND config_hash = $2
|
AND config_hash = $2
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var configFindApproved = `
|
||||||
|
SELECT build_id FROM builds
|
||||||
|
WHERE build_repo_id = $1
|
||||||
|
AND build_config_id = $2
|
||||||
|
AND build_status NOT IN ('blocked', 'pending')
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
var countUsers = `
|
var countUsers = `
|
||||||
SELECT reltuples
|
SELECT reltuples
|
||||||
FROM pg_class WHERE relname = 'users';
|
FROM pg_class WHERE relname = 'users';
|
||||||
|
|
|
@ -5,7 +5,6 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_id = ?
|
WHERE config_id = ?
|
||||||
|
|
||||||
|
@ -16,7 +15,14 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_repo_id = ?
|
WHERE config_repo_id = ?
|
||||||
AND config_hash = ?
|
AND config_hash = ?
|
||||||
|
|
||||||
|
-- name: config-find-approved
|
||||||
|
|
||||||
|
SELECT build_id FROM builds
|
||||||
|
WHERE build_repo_id = ?
|
||||||
|
AND build_config_id = ?
|
||||||
|
AND build_status NOT IN ('blocked', 'pending')
|
||||||
|
LIMIT 1
|
||||||
|
|
|
@ -8,6 +8,7 @@ func Lookup(name string) string {
|
||||||
var index = map[string]string{
|
var index = map[string]string{
|
||||||
"config-find-id": configFindId,
|
"config-find-id": configFindId,
|
||||||
"config-find-repo-hash": configFindRepoHash,
|
"config-find-repo-hash": configFindRepoHash,
|
||||||
|
"config-find-approved": configFindApproved,
|
||||||
"count-users": countUsers,
|
"count-users": countUsers,
|
||||||
"count-repos": countRepos,
|
"count-repos": countRepos,
|
||||||
"count-builds": countBuilds,
|
"count-builds": countBuilds,
|
||||||
|
@ -41,7 +42,6 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_id = ?
|
WHERE config_id = ?
|
||||||
`
|
`
|
||||||
|
@ -52,12 +52,19 @@ SELECT
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_approved
|
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_repo_id = ?
|
WHERE config_repo_id = ?
|
||||||
AND config_hash = ?
|
AND config_hash = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var configFindApproved = `
|
||||||
|
SELECT build_id FROM builds
|
||||||
|
WHERE build_repo_id = ?
|
||||||
|
AND build_config_id = ?
|
||||||
|
AND build_status NOT IN ('blocked', 'pending')
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
var countUsers = `
|
var countUsers = `
|
||||||
SELECT count(1)
|
SELECT count(1)
|
||||||
FROM users
|
FROM users
|
||||||
|
|
|
@ -94,8 +94,8 @@ type Store interface {
|
||||||
|
|
||||||
ConfigLoad(int64) (*model.Config, error)
|
ConfigLoad(int64) (*model.Config, error)
|
||||||
ConfigFind(*model.Repo, string) (*model.Config, error)
|
ConfigFind(*model.Repo, string) (*model.Config, error)
|
||||||
ConfigUpdate(*model.Config) error
|
ConfigFindApproved(*model.Config) (bool, error)
|
||||||
ConfigInsert(*model.Config) error
|
ConfigCreate(*model.Config) error
|
||||||
|
|
||||||
SenderFind(*model.Repo, string) (*model.Sender, error)
|
SenderFind(*model.Repo, string) (*model.Sender, error)
|
||||||
SenderList(*model.Repo) ([]*model.Sender, error)
|
SenderList(*model.Repo) ([]*model.Sender, error)
|
||||||
|
|
Loading…
Reference in a new issue