Files
spyda/cmd/spyda/main.go
2021-01-30 15:06:58 +10:00

241 lines
6.5 KiB
Go

package main
import (
"expvar"
"fmt"
"net/http"
"net/http/pprof"
"os"
"path"
"strings"
"time"
log "github.com/sirupsen/logrus"
flag "github.com/spf13/pflag"
profiler "github.com/wblakecaldwell/profiler"
"git.mills.io/prologic/spyda"
"git.mills.io/prologic/spyda/internal"
)
var (
bind string
debug bool
version bool
// Basic options
name string
description string
data string
store string
theme string
baseURL string
// Pod Oeprator
adminUser string
adminPass string
adminName string
adminEmail string
// Limits
resultsPerpage int
// Secrets
apiSigningKey string
cookieSecret string
magiclinkSecret string
// Email Setitngs
smtpHost string
smtpPort int
smtpUser string
smtpPass string
smtpFrom string
// Timeouts
sessionExpiry time.Duration
sessionCacheTTL time.Duration
apiSessionTime time.Duration
)
func init() {
flag.BoolVarP(&debug, "debug", "D", false, "enable debug logging")
flag.StringVarP(&bind, "bind", "b", "0.0.0.0:8000", "[int]:<port> to bind to")
flag.BoolVarP(&version, "version", "v", false, "display version information")
// Basic options
flag.StringVarP(&name, "name", "n", internal.DefaultName, "set the pod's name")
flag.StringVarP(&description, "description", "m", internal.DefaultMetaDescription, "set the pod's description")
flag.StringVarP(&data, "data", "d", internal.DefaultData, "data directory")
flag.StringVarP(&store, "store", "s", internal.DefaultStore, "store to use")
flag.StringVarP(&theme, "theme", "t", internal.DefaultTheme, "set the default theme")
flag.StringVarP(&baseURL, "base-url", "u", internal.DefaultBaseURL, "base url to use")
// Administration
flag.StringVarP(&adminName, "admin-name", "N", internal.DefaultAdminName, "default admin user name")
flag.StringVarP(&adminEmail, "admin-email", "E", internal.DefaultAdminEmail, "default admin user email")
flag.StringVarP(&adminUser, "admin-user", "A", internal.DefaultAdminUser, "default admin user to use")
flag.StringVarP(&adminPass, "admin-pass", "P", internal.DefaultAdminName, "default admin user name")
// Limits
flag.IntVarP(
&resultsPerPage, "results-per-page", "T", internal.DefaultResultsPerPage,
"maximum results per page to display",
)
// Secrets
flag.StringVar(
&apiSigningKey, "api-signing-key", internal.DefaultAPISigningKey,
"secret to use for signing api tokens",
)
flag.StringVar(
&cookieSecret, "cookie-secret", internal.DefaultCookieSecret,
"cookie secret to use secure sessions",
)
flag.StringVar(
&magiclinkSecret, "magiclink-secret", internal.DefaultMagicLinkSecret,
"magiclink secret to use for password reset tokens",
)
// Email Setitngs
flag.StringVar(&smtpHost, "smtp-host", internal.DefaultSMTPHost, "SMTP Host to use for email sending")
flag.IntVar(&smtpPort, "smtp-port", internal.DefaultSMTPPort, "SMTP Port to use for email sending")
flag.StringVar(&smtpUser, "smtp-user", internal.DefaultSMTPUser, "SMTP User to use for email sending")
flag.StringVar(&smtpPass, "smtp-pass", internal.DefaultSMTPPass, "SMTP Pass to use for email sending")
flag.StringVar(&smtpFrom, "smtp-from", internal.DefaultSMTPFrom, "SMTP From to use for email sending")
// Timeouts
flag.DurationVar(
&sessionExpiry, "session-expiry", internal.DefaultSessionExpiry,
"timeout for sessions to expire",
)
flag.DurationVar(
&sessionCacheTTL, "session-cache-ttl", internal.DefaultSessionCacheTTL,
"time-to-live for cached sessions",
)
flag.DurationVar(
&apiSessionTime, "api-session-time", internal.DefaultAPISessionTime,
"timeout for api tokens to expire",
)
}
func flagNameFromEnvironmentName(s string) string {
s = strings.ToLower(s)
s = strings.Replace(s, "_", "-", -1)
return s
}
func parseArgs() error {
for _, v := range os.Environ() {
vals := strings.SplitN(v, "=", 2)
flagName := flagNameFromEnvironmentName(vals[0])
fn := flag.CommandLine.Lookup(flagName)
if fn == nil || fn.Changed {
continue
}
if err := fn.Value.Set(vals[1]); err != nil {
return err
}
}
flag.Parse()
return nil
}
func extraServiceInfoFactory(svr *internal.Server) profiler.ExtraServiceInfoRetriever {
return func() map[string]interface{} {
extraInfo := make(map[string]interface{})
expvar.Get("stats").(*expvar.Map).Do(func(kv expvar.KeyValue) {
extraInfo[kv.Key] = kv.Value.String()
})
return extraInfo
}
}
func main() {
parseArgs()
if version {
fmt.Printf("spyda v%s", spyda.FullVersion())
os.Exit(0)
}
if debug {
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.InfoLevel)
}
svr, err := internal.NewServer(bind,
// Debug mode
internal.WithDebug(debug),
// Basic options
internal.WithName(name),
internal.WithDescription(description),
internal.WithData(data),
internal.WithStore(store),
internal.WithTheme(theme),
internal.WithBaseURL(baseURL),
// Administration
internal.WithAdminUser(adminUser),
internal.WithAdminPass(adminPass),
internal.WithAdminName(adminName),
internal.WithAdminEmail(adminEmail),
// Limits
internal.WithResultsPerPage(resultsPerPage),
// Secrets
internal.WithAPISigningKey(apiSigningKey),
internal.WithCookieSecret(cookieSecret),
internal.WithMagicLinkSecret(magiclinkSecret),
// Email Setitngs
internal.WithSMTPHost(smtpHost),
internal.WithSMTPPort(smtpPort),
internal.WithSMTPUser(smtpUser),
internal.WithSMTPPass(smtpPass),
internal.WithSMTPFrom(smtpFrom),
// Timeouts
internal.WithSessionExpiry(sessionExpiry),
internal.WithSessionCacheTTL(sessionCacheTTL),
internal.WithAPISessionTime(apiSessionTime),
)
if err != nil {
log.WithError(err).Fatal("error creating server")
}
if debug {
log.Info("starting memory profiler (debug mode) ...")
go func() {
// add the profiler handler endpoints
profiler.AddMemoryProfilingHandlers()
// add realtime extra key/value diagnostic info (optional)
profiler.RegisterExtraServiceInfoRetriever(extraServiceInfoFactory(svr))
// start the profiler on service start (optional)
profiler.StartProfiling()
// Add pprof handlers
http.Handle("/debug/pprof/block", pprof.Handler("block"))
http.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
http.Handle("/debug/pprof/heap", pprof.Handler("heap"))
http.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
// listen on port 6060 (pick a port)
http.ListenAndServe(":6060", nil)
}()
}
log.Infof("%s v%s listening on http://%s", path.Base(os.Args[0]), spyda.FullVersion(), bind)
if err := svr.Run(); err != nil {
log.WithError(err).Fatal("error running or shutting down server")
}
}