populate common params to commands

This commit is contained in:
Umputun
2018-09-05 02:13:57 -05:00
parent 84e7770359
commit 1b3904a307
13 changed files with 131 additions and 79 deletions
+11 -7
View File
@@ -15,12 +15,16 @@ import (
// BackupCommand set of flags and command for export
// ExportPath used as a separate element to leverage BACKUP_PATH. If ExportFile has a path (i.e. with /) BACKUP_PATH ignored.
type BackupCommand struct {
ExportPath string `short:"p" long:"path" env:"BACKUP_PATH" default:"./var/backup" description:"export path"`
ExportFile string `short:"f" long:"file" default:"userbackup-{{.SITE}}-{{.TS}}.gz" description:"file name"`
Site string `long:"site" env:"SITE" default:"remark" description:"site name"`
SharedSecret string `long:"secret" env:"SECRET" description:"shared secret key" required:"true"`
Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"`
URL string `long:"url" default:"http://127.0.0.1:8080" description:"base url"`
ExportPath string `short:"p" long:"path" env:"BACKUP_PATH" default:"./var/backup" description:"export path"`
ExportFile string `short:"f" long:"file" default:"userbackup-{{.SITE}}-{{.TS}}.gz" description:"file name"`
Site string `long:"site" env:"SITE" default:"remark" description:"site name"`
Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"`
CommonOpts
}
// SetCommon satisfies main.CommonOptionsCommander interface and sets common options
func (ec *BackupCommand) SetCommon(commonOpts CommonOpts) {
ec.CommonOpts = commonOpts
}
// Execute runs export with ExportCommand parameters, entry point for "export" command
@@ -40,7 +44,7 @@ func (ec *BackupCommand) Execute(args []string) error {
client := http.Client{}
ctx, cancel := context.WithTimeout(context.Background(), ec.Timeout)
defer cancel()
exportURL := fmt.Sprintf("%s/api/v1/admin/export?mode=file&site=%s&secret=%s", ec.URL, ec.Site, ec.SharedSecret)
exportURL := fmt.Sprintf("%s/api/v1/admin/export?mode=file&site=%s&secret=%s", ec.RemarkURL, ec.Site, ec.SharedSecret)
req, err := http.NewRequest(http.MethodGet, exportURL, nil)
if err != nil {
return errors.Wrapf(err, "can't make export request for %s", exportURL)
+8 -6
View File
@@ -22,9 +22,9 @@ func TestBackup_Execute(t *testing.T) {
defer ts.Close()
cmd := BackupCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--site=remark", "--path=/tmp",
"--file={{.SITE}}-test.export", "--url=" + ts.URL})
_, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file={{.SITE}}-test.export"})
require.Nil(t, err)
err = cmd.Execute(nil)
assert.NoError(t, err)
@@ -45,9 +45,10 @@ func TestBackup_ExecuteFailedStatus(t *testing.T) {
defer ts.Close()
cmd := BackupCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--site=remark", "--path=/tmp",
"--file={{.SITE}}-test.export", "--url=" + ts.URL})
_, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file={{.SITE}}-test.export"})
require.Nil(t, err)
err = cmd.Execute(nil)
assert.EqualError(t, err, `error response "400 Bad Request", some error`)
@@ -62,9 +63,10 @@ func TestBackup_ExecuteFailedWrite(t *testing.T) {
defer ts.Close()
cmd := BackupCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--site=remark", "--path=/tmp",
"--file=/tmp/no-such-dir/{{.SITE}}-test.export", "--url=" + ts.URL})
_, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file=/tmp/no-such-dir/{{.SITE}}-test.export"})
require.Nil(t, err)
err = cmd.Execute(nil)
assert.EqualError(t, err, `can't create backup file /tmp/no-such-dir/remark-test.export: open /tmp/no-such-dir/remark-test.export: no such file or directory`)
+6
View File
@@ -19,6 +19,12 @@ import (
// Revision sets from main
var Revision = "unknown"
// CommonOpts sets externally from main, shared across all commands
type CommonOpts struct {
RemarkURL string
SharedSecret string
}
// fileParser used to convert template strings like blah-{{.SITE}}-{{.YYYYMMDD}} the final format
type fileParser struct {
site string
+11 -7
View File
@@ -17,13 +17,16 @@ import (
// ImportCommand set of flags and command for import
type ImportCommand struct {
InputFile string `short:"f" long:"file" description:"input file name" required:"true"`
Provider string `short:"p" long:"provider" default:"disqus" choice:"disqus" choice:"wordpress" description:"import format"`
Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"`
SharedSecret string `long:"secret" env:"SECRET" description:"shared secret key" required:"true"`
InputFile string `short:"f" long:"file" description:"input file name" required:"true"`
Provider string `short:"p" long:"provider" default:"disqus" choice:"disqus" choice:"wordpress" description:"import format"`
Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"`
Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"`
CommonOpts
}
Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"`
URL string `long:"url" default:"http://127.0.0.1:8080" description:"migrator base url"`
// SetCommon satisfies main.CommonOptionsCommander interface and sets common options
func (ic *ImportCommand) SetCommon(commonOpts CommonOpts) {
ic.CommonOpts = commonOpts
}
// Execute runs import with ImportCommand parameters, entry point for "import" command
@@ -39,7 +42,8 @@ func (ic *ImportCommand) Execute(args []string) error {
client := http.Client{}
ctx, cancel := context.WithTimeout(context.Background(), ic.Timeout)
defer cancel()
importURL := fmt.Sprintf("%s/api/v1/admin/import?site=%s&provider=%s&secret=%s", ic.URL, ic.Site, ic.Provider, ic.SharedSecret)
importURL := fmt.Sprintf("%s/api/v1/admin/import?site=%s&provider=%s&secret=%s",
ic.RemarkURL, ic.Site, ic.Provider, ic.SharedSecret)
req, err := http.NewRequest(http.MethodPost, importURL, reader)
if err != nil {
return errors.Wrapf(err, "can't make import request for %s", importURL)
+15 -8
View File
@@ -30,15 +30,19 @@ func TestImport_Execute(t *testing.T) {
defer ts.Close()
cmd := ImportCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--site=remark", "--file=testdata/import.txt", "--url=" + ts.URL})
_, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt"})
require.Nil(t, err)
err = cmd.Execute(nil)
assert.NoError(t, err)
cmd = ImportCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p = flags.NewParser(&cmd, flags.Default)
_, err = p.ParseArgs([]string{"--secret=123456", "--site=remark", "--file=testdata/import.txt.gz", "--url=" + ts.URL})
_, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt.gz"})
require.Nil(t, err)
err = cmd.Execute(nil)
assert.NoError(t, err)
@@ -54,8 +58,9 @@ func TestImport_ExecuteFailed(t *testing.T) {
defer ts.Close()
cmd := ImportCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--site=remark", "--file=testdata/import-no.txt", "--url=" + ts.URL})
_, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import-no.txt"})
require.Nil(t, err)
err = cmd.Execute(nil)
t.Log(err)
@@ -63,9 +68,9 @@ func TestImport_ExecuteFailed(t *testing.T) {
assert.True(t, strings.Contains(err.Error(), "no such file or directory"))
cmd = ImportCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: "http://127.0.0.1:12345", SharedSecret: "123456"})
p = flags.NewParser(&cmd, flags.Default)
_, err = p.ParseArgs([]string{"--secret=123456", "--site=remark", "--file=testdata/import.txt",
"--url=http://127.0.0.1:12345"})
_, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt"})
require.Nil(t, err)
err = cmd.Execute(nil)
t.Log(err)
@@ -79,8 +84,9 @@ func TestImport_ExecuteFailed(t *testing.T) {
}))
defer ts2.Close()
cmd = ImportCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts2.URL, SharedSecret: "123456"})
p = flags.NewParser(&cmd, flags.Default)
_, err = p.ParseArgs([]string{"--secret=123456", "--site=remark", "--file=testdata/import.txt", "--url=" + ts2.URL})
_, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt"})
require.Nil(t, err)
err = cmd.Execute(nil)
t.Log(err)
@@ -102,9 +108,10 @@ func TestImport_ExecuteTimeout(t *testing.T) {
defer ts.Close()
cmd := ImportCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--site=remark", "--file=testdata/import.txt",
"--url=" + ts.URL, "--timeout=300ms"})
_, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt", "--timeout=300ms"})
require.Nil(t, err)
err = cmd.Execute(nil)
assert.NotNil(t, err)
+13 -10
View File
@@ -10,10 +10,14 @@ type RestoreCommand struct {
ImportPath string `short:"p" long:"path" env:"BACKUP_PATH" default:"./var/backup" description:"export path"`
ImportFile string `short:"f" long:"file" default:"userbackup-{{.SITE}}-{{.YYYYMMDD}}.gz" description:"file name" required:"true"`
Site string `long:"site" env:"SITE" default:"remark" description:"site name"`
SharedSecret string `long:"secret" env:"SECRET" description:"shared secret key" required:"true"`
Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"`
URL string `long:"url" default:"http://127.0.0.1:8081" description:"migrator base url"`
Site string `long:"site" env:"SITE" default:"remark" description:"site name"`
Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"`
CommonOpts
}
// SetCommon satisfies main.CommonOptionsCommander interface and sets common options
func (rc *RestoreCommand) SetCommon(commonOpts CommonOpts) {
rc.CommonOpts = commonOpts
}
// Execute runs import with RestoreCommand parameters, entry point for "restore" command
@@ -28,12 +32,11 @@ func (rc *RestoreCommand) Execute(args []string) error {
return err
}
importer := ImportCommand{
InputFile: fname,
Site: rc.Site,
Provider: "native",
SharedSecret: rc.SharedSecret,
Timeout: rc.Timeout,
URL: rc.URL,
InputFile: fname,
Site: rc.Site,
Provider: "native",
Timeout: rc.Timeout,
CommonOpts: rc.CommonOpts,
}
return importer.Execute(args)
}
+3 -2
View File
@@ -28,9 +28,10 @@ func TestRestore_Execute(t *testing.T) {
defer ts.Close()
cmd := RestoreCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"})
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--site=remark", "--path=testdata", "--file=import.txt",
"--url=" + ts.URL})
_, err := p.ParseArgs([]string{"--site=remark", "--path=testdata", "--file=import.txt"})
require.Nil(t, err)
err = cmd.Execute(nil)
assert.NoError(t, err)
+8 -4
View File
@@ -30,9 +30,6 @@ import (
// ServerCommand with command line flags and env
type ServerCommand struct {
RemarkURL string `long:"url" env:"REMARK_URL" required:"true" description:"url to remark"`
SharedSecret string `long:"secret" env:"SECRET" required:"true" description:"shared secret key"`
Store StoreGroup `group:"store" namespace:"store" env-namespace:"STORE"`
Avatar AvatarGroup `group:"avatar" namespace:"avatar" env-namespace:"AVATAR"`
Cache CacheGroup `group:"cache" namespace:"cache" env-namespace:"CACHE"`
@@ -64,6 +61,8 @@ type ServerCommand struct {
Yandex AuthGroup `group:"yandex" namespace:"yandex" env-namespace:"YANDEX" description:"Yandex OAuth"`
Dev bool `long:"dev" env:"DEV" description:"enable dev (local) oauth2"`
} `group:"auth" namespace:"auth" env-namespace:"AUTH"`
CommonOpts
}
// AuthGroup defines options group for auth params
@@ -131,9 +130,14 @@ type serverApp struct {
terminated chan struct{}
}
// SetCommon satisfies CommonOptionsCommander interface and sets common options
func (s *ServerCommand) SetCommon(commonOpts CommonOpts) {
s.CommonOpts = commonOpts
}
// Execute is the entry point for "server" command, called by flag parser
func (s *ServerCommand) Execute(args []string) error {
log.Print("[INFO] start remark42 server")
log.Printf("[INFO] start server on port %d", s.Port)
resetEnv("SECRET", "AUTH_GOOGLE_CSEC", "AUTH_GITHUB_CSEC", "AUTH_FACEBOOK_CSEC", "AUTH_YANDEX_CSEC")
ctx, cancel := context.WithCancel(context.Background())
+37 -26
View File
@@ -86,10 +86,12 @@ func TestServerApp_WithMongo(t *testing.T) {
}
opts := ServerCommand{}
opts.SetCommon(CommonOpts{RemarkURL: "https://demo.remark42.com", SharedSecret: "123456"})
// prepare options
p := flags.NewParser(&opts, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--dev-passwd=password", "--url=https://demo.remark42.com",
"--cache.type=mongo", "--store.type=mongo", "--avatar.type=mongo", "--mongo.url=" + mongoURL, "--mongo.db=test_remark", "--port=12345"})
_, err := p.ParseArgs([]string{"--dev-passwd=password", "--cache.type=mongo", "--store.type=mongo",
"--avatar.type=mongo", "--mongo.url=" + mongoURL, "--mongo.db=test_remark", "--port=12345"})
require.Nil(t, err)
opts.Auth.Github.CSEC, opts.Auth.Github.CID = "csec", "cid"
opts.BackupLocation = "/tmp"
@@ -131,11 +133,12 @@ func TestServerApp_WithMongo(t *testing.T) {
func TestServerApp_Failed(t *testing.T) {
opts := ServerCommand{}
opts.SetCommon(CommonOpts{RemarkURL: "https://demo.remark42.com", SharedSecret: "123456"})
p := flags.NewParser(&opts, flags.Default)
// RO bolt location
_, err := p.ParseArgs([]string{"--secret=123456", "--url=https://demo.remark42.com", "--backup=/tmp",
"--store.bolt.path=/dev/null"})
_, err := p.ParseArgs([]string{"--backup=/tmp", "--store.bolt.path=/dev/null"})
assert.Nil(t, err)
_, err = opts.newServerApp()
assert.EqualError(t, err, "can't initialize data store: failed to make boltdb for /dev/null/remark.db: "+
@@ -144,8 +147,9 @@ func TestServerApp_Failed(t *testing.T) {
// RO backup location
opts = ServerCommand{}
_, err = p.ParseArgs([]string{"--secret=123456", "--url=https://demo.remark42.com", "--store.bolt.path=/tmp",
"--backup=/dev/null/not-writable"})
opts.SetCommon(CommonOpts{RemarkURL: "https://demo.remark42.com", SharedSecret: "123456"})
_, err = p.ParseArgs([]string{"--store.bolt.path=/tmp", "--backup=/dev/null/not-writable"})
assert.Nil(t, err)
_, err = opts.newServerApp()
assert.EqualError(t, err, "can't check directory status for /dev/null/not-writable: stat /dev/null/not-writable: not a directory")
@@ -153,14 +157,18 @@ func TestServerApp_Failed(t *testing.T) {
// invalid url
opts = ServerCommand{}
_, err = p.ParseArgs([]string{"--secret=123456", "--url=demo.remark42.com", "--backup=/tmp", "----store.bolt.path=/tmp"})
opts.SetCommon(CommonOpts{RemarkURL: "demo.remark42.com", SharedSecret: "123456"})
_, err = p.ParseArgs([]string{"--backup=/tmp", "----store.bolt.path=/tmp"})
assert.Nil(t, err)
_, err = opts.newServerApp()
assert.EqualError(t, err, "invalid remark42 url demo.remark42.com")
t.Log(err)
opts = ServerCommand{}
_, err = p.ParseArgs([]string{"--secret=123456", "--url=https://demo.remark42.com", "--backup=/tmp", "--store.type=blah"})
opts.SetCommon(CommonOpts{RemarkURL: "https://demo.remark42.com", SharedSecret: "123456"})
_, err = p.ParseArgs([]string{"--backup=/tmp", "--store.type=blah"})
assert.NotNil(t, err, "blah is invalid type")
opts.Store.Type = "blah"
@@ -191,9 +199,10 @@ func TestServerApp_MainSignal(t *testing.T) {
st := time.Now()
s := ServerCommand{}
s.SetCommon(CommonOpts{RemarkURL: "https://demo.remark42.com", SharedSecret: "123456"})
p := flags.NewParser(&s, flags.Default)
args := []string{"test", "--secret=123456", "--store.bolt.path=/tmp/xyz", "--backup=/tmp", "--avatar.fs.path=/tmp",
"--port=18100", "--url=https://demo.remark42.com"}
args := []string{"test", "--store.bolt.path=/tmp/xyz", "--backup=/tmp", "--avatar.fs.path=/tmp", "--port=18100"}
_, err := p.ParseArgs(args)
require.Nil(t, err)
err = s.Execute(args)
@@ -202,25 +211,27 @@ func TestServerApp_MainSignal(t *testing.T) {
}
func prepServerApp(t *testing.T, duration time.Duration, fn func(o ServerCommand) ServerCommand) (*serverApp, context.Context) {
opts := ServerCommand{}
// prepare options
p := flags.NewParser(&opts, flags.Default)
_, err := p.ParseArgs([]string{"--secret=123456", "--dev-passwd=password", "--url=https://demo.remark42.com"})
require.Nil(t, err)
opts.Avatar.FS.Path, opts.Avatar.Type, opts.BackupLocation = "/tmp", "fs", "/tmp"
opts.Store.Bolt.Path = fmt.Sprintf("/tmp/%d", opts.Port)
opts.Store.Bolt.Timeout = 10 * time.Second
opts.Auth.Github.CSEC, opts.Auth.Github.CID = "csec", "cid"
opts.Auth.Google.CSEC, opts.Auth.Google.CID = "csec", "cid"
opts.Auth.Facebook.CSEC, opts.Auth.Facebook.CID = "csec", "cid"
opts.Auth.Yandex.CSEC, opts.Auth.Yandex.CID = "csec", "cid"
opts.BackupLocation = "/tmp"
opts = fn(opts)
cmd := ServerCommand{}
cmd.SetCommon(CommonOpts{RemarkURL: "https://demo.remark42.com", SharedSecret: "123456"})
os.Remove(opts.Store.Bolt.Path + "/remark.db")
// prepare options
p := flags.NewParser(&cmd, flags.Default)
_, err := p.ParseArgs([]string{"--dev-passwd=password"})
require.Nil(t, err)
cmd.Avatar.FS.Path, cmd.Avatar.Type, cmd.BackupLocation = "/tmp", "fs", "/tmp"
cmd.Store.Bolt.Path = fmt.Sprintf("/tmp/%d", cmd.Port)
cmd.Store.Bolt.Timeout = 10 * time.Second
cmd.Auth.Github.CSEC, cmd.Auth.Github.CID = "csec", "cid"
cmd.Auth.Google.CSEC, cmd.Auth.Google.CID = "csec", "cid"
cmd.Auth.Facebook.CSEC, cmd.Auth.Facebook.CID = "csec", "cid"
cmd.Auth.Yandex.CSEC, cmd.Auth.Yandex.CID = "csec", "cid"
cmd.BackupLocation = "/tmp"
cmd = fn(cmd)
os.Remove(cmd.Store.Bolt.Path + "/remark.db")
// create app
app, err := opts.newServerApp()
app, err := cmd.newServerApp()
require.Nil(t, err)
ctx, cancel := context.WithCancel(context.Background())
+12 -1
View File
@@ -18,11 +18,19 @@ type Opts struct {
BackupCmd cmd.BackupCommand `command:"backup"`
RestoreCmd cmd.RestoreCommand `command:"restore"`
RemarkURL string `long:"url" env:"REMARK_URL" required:"true" description:"url to remark"`
SharedSecret string `long:"secret" env:"SECRET" required:"true" description:"shared secret key"`
Dbg bool `long:"dbg" env:"DEBUG" description:"debug mode"`
}
var revision = "unknown"
type CommonOptionsCommander interface {
SetCommon(commonOpts cmd.CommonOpts)
Execute(args []string) error
}
func main() {
fmt.Printf("remark42 %s\n", revision)
cmd.Revision = revision
@@ -31,7 +39,10 @@ func main() {
p := flags.NewParser(&opts, flags.Default)
p.CommandHandler = func(command flags.Commander, args []string) error {
setupLog(opts.Dbg)
err := command.Execute(args)
commonOpts := cmd.CommonOpts{RemarkURL: opts.RemarkURL, SharedSecret: opts.SharedSecret}
c := command.(CommonOptionsCommander)
c.SetCommon(commonOpts)
err := c.Execute(args)
if err != nil {
log.Printf("[ERROR] failed with %+v", err)
}
-5
View File
@@ -70,11 +70,6 @@ type commentsWithInfo struct {
func (s *Rest) Run(port int) {
log.Printf("[INFO] activate rest server on port %d", port)
// TODO: restore admin info dbg
//if s.DataService != nil && len(s.DataService.AdminStore.Admins()) > 0 {
// log.Printf("[DEBUG] admins %+v", s.DataService.Admins)
//}
router := s.routes()
s.lock.Lock()
+4 -3
View File
@@ -50,14 +50,15 @@ func (a *Authenticator) Auth(reqAuth bool) func(http.Handler) http.Handler {
f := func(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// if secret key matches and site in request return admin
// if secret key matches for given site (from request) return admin user
if a.checkSecretKey(r) {
r = rest.SetUserInfo(r, adminUser)
h.ServeHTTP(w, r)
return
}
if a.basicDevUser(w, r) { // use dev user basic auth if enabled
// use dev user basic auth if enabled
if a.basicDevUser(w, r) {
r = rest.SetUserInfo(r, devUser)
h.ServeHTTP(w, r)
return
@@ -70,7 +71,7 @@ func (a *Authenticator) Auth(reqAuth bool) func(http.Handler) http.Handler {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// in anonymous mode just pass it to the next handler
// if !reqAuth just pass it to the next handler, used for information only, like logs
h.ServeHTTP(w, r)
return
}
+3
View File
@@ -1,5 +1,7 @@
package admin
import "log"
// Store defines interface returning admins info for given site
type Store interface {
Admins(siteID string) (ids []string)
@@ -14,6 +16,7 @@ type StaticStore struct {
// NewStaticStore makes StaticStore instance with given key
func NewStaticStore(admins []string, email string) *StaticStore {
log.Printf("[DEBUG] admin users %+v, email %s", admins, email)
return &StaticStore{admins: admins, email: email}
}