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]: 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") } }