func diag(w http.ResponseWriter, r *http.Request) {
	d := map[string]string{
		"Cookie Domain":                      parsedAppYaml.EnvVars.CookieDomain,
		"Deployment Type":                    deploymentType,
		"Session Lifetime (days)":            strconv.Itoa(parsedAppYaml.EnvVars.SessionLifetime),
		"Static File Cache Lifetime (days)":  strconv.Itoa(parsedAppYaml.EnvVars.CacheDays),
		"Use Development Database/Datastore": strconv.FormatBool(useDevDatastore),
		"Use Local Files":                    parsedAppYaml.EnvVars.UseLocalFiles,
		"Project ID":                 parsedAppYaml.EnvVars.ProjectID,
		"App Engine Service Name":    os.Getenv("GAE_SERVICE"),
		"App Engine Service Version": os.Getenv("GAE_VERSION"),
		"App Engine Instance ID":     os.Getenv("GAE_INSTANCE"),
		"Path to Datastore Credentials": pathToDatastoreCredentials,
		"Path to Static Files":          parsedAppYaml.EnvVars.StaticFilePath,
		"Path to Templates":             parsedAppYaml.EnvVars.TemplatesPath,
		"Path to app.yaml":              pathToAppYaml,
		"Path to SQLite db file:":       sqliteutils.Config.PathToDatabaseFile,
	}
	templates.Load(w, "diagnostics", d)
	return
}
diag shows a diagnostic page with info on this app
package main
import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/appsettings"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/card"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/company"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/datastoreutils"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/middleware"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/pages"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/receipt"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/sessionutils"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/sqliteutils"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/templates"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/users"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	yaml "gopkg.in/yaml.v2"
)
const defaultPort = "8005"
const staticWebDir = "/static"
var staticLocalDir = ""
var cacheDays = 0
type appYaml struct {
	Runtime string `yaml:"runtime"`  
	EnvVars struct {
		ProjectID            string `yaml:"PROJECT_ID"`                  
		SessionAuthKey       string `yaml:"SESSION_AUTH_KEY"`            
		SessionEncryptKey    string `yaml:"SESSION_ENCRYPT_KEY"`         
		SessionLifetime      int    `yaml:"SESSION_LIFETIME"`            
		CookieDomain         string `yaml:"COOKIE_DOMAINCOOKIE_DOMAIN"`  
		StripeSecretKey      string `yaml:"STRIPE_SECRET_KEY"`           
		StripePublishableKey string `yaml:"STRIPE_PUBLISHABLE_KEY"`      
		CacheDays            int    `yaml:"CACHE_DAYS"`                  
		StaticFilePath       string `yaml:"PATH_TO_STATIC_FILES"`        
		TemplatesPath        string `yaml:"PATH_TO_TEMPLATES"`           
		UseLocalFiles        string `yaml:"USE_LOCAL_FILES"`             
		PathToSqliteFile     string `yaml:"PATH_TO_SQLITE_FILE"`         
	} `yaml:"env_variables"`
	Handlers []struct {
		URL       string `yaml:"url"`
		StaticDir string `yaml:"static_dir"`
		Upload    string `yaml:"upload"`
		Script    string `yaml:"script"`
	} `yaml:"handlers"`
	ErrorHandlers []struct {
		File      string `yaml:"file"`
		ErrorCode string `yaml:"error_code"`
	} `yaml:"error_handlers"`
}
var parsedAppYaml appYaml
var (
	deploymentType             string
	pathToAppYaml              string
	pathToDatastoreCredentials string
	useDevDatastore            bool
)
const (
	deploymentTypeAppengine    = "appengine"
	deploymentTypeAppengineDev = "appengine-dev"
	deploymentTypeSqlite       = "sqlite"
)
func init() {
	flag.StringVar(&deploymentType, "type", "appengine", "Set to appengine or appengine-dev.  In development mode the app.yaml file will be parsed to read the set environmental variables.")
	flag.StringVar(&pathToAppYaml, "path-to-app-yaml", "./app.yaml", "The path to the app.yaml file.")
	flag.StringVar(&pathToDatastoreCredentials, "path-to-datastore-credentials", "./datastore-service-account.json", "The path to your datastore service account file.  A JSON file.")
	flag.BoolVar(&useDevDatastore, "use-dev-datastore", true, "Not used for non -dev deployment types. Set to false to use live datastore data in development deployment types.")
	flag.Parse()
	switch deploymentType {
	case deploymentTypeAppengine:
		c := sessionutils.Config
		c.SessionAuthKey = os.Getenv("SESSION_AUTH_KEY")
		c.SessionEncryptKey = os.Getenv("SESSION_ENCRYPT_KEY")
		c.SessionLifetime, _ = strconv.Atoi(os.Getenv("SESSION_LIFETIME"))
		c.CookieDomain = os.Getenv("COOKIE_DOMAIN")
		err := sessionutils.SetConfig(c)
		if err != nil {
			log.Fatalln("Could not set configuration for sessionutils.", err)
			return
		}
		cc := card.Config
		cc.StripeSecretKey = os.Getenv("STRIPE_SECRET_KEY")
		cc.StripePublishableKey = os.Getenv("STRIPE_PUBLISHABLE_KEY")
		err = card.SetConfig(cc)
		if err != nil {
			log.Fatalln("Could not set configuration for card.", err)
			return
		}
		ccc := datastoreutils.Config
		ccc.ProjectID = os.Getenv("PROJECT_ID")
		err = datastoreutils.SetConfig(ccc)
		if err != nil {
			log.Fatalln("Could not set configuration for datastore.", err)
			return
		}
		cccc := templates.Config
		cccc.PathToTemplates = "./services/process-cards/website/templates/"
		cccc.Development = false
		cccc.UseLocalFiles, _ = strconv.ParseBool(os.Getenv("USE_LOCAL_FILES"))
		templates.SetConfig(cccc)
		staticLocalDir = "./services/process-cards/website/static/"
		cacheDays, _ = strconv.Atoi(os.Getenv("CACHE_DAYS"))
		parsedAppYaml.EnvVars.ProjectID = os.Getenv("PROJECT_ID")
		parsedAppYaml.EnvVars.SessionLifetime, _ = strconv.Atoi(os.Getenv("SESSION_LIFETIME"))
		parsedAppYaml.EnvVars.CacheDays, _ = strconv.Atoi(os.Getenv("CACHE_DAYS"))
		parsedAppYaml.EnvVars.UseLocalFiles = os.Getenv("USE_LOCAL_FILES")
		parsedAppYaml.EnvVars.CookieDomain = os.Getenv("COOKIE_DOMAIN")
		useDevDatastore = false
	case deploymentTypeAppengineDev:
		yamlData, err := parseAppYaml(pathToAppYaml)
		if err != nil {
			log.Fatalln("Error while parsing app.yaml.", err)
			return
		}
		parsedAppYaml = yamlData
		c := sessionutils.Config
		c.SessionAuthKey = yamlData.EnvVars.SessionAuthKey
		c.SessionEncryptKey = yamlData.EnvVars.SessionEncryptKey
		c.SessionLifetime = yamlData.EnvVars.SessionLifetime
		c.CookieDomain = yamlData.EnvVars.CookieDomain
		err = sessionutils.SetConfig(c)
		if err != nil {
			log.Fatalln("Could not set configuration for sessionutils.", err)
			return
		}
		cc := card.Config
		cc.StripeSecretKey = yamlData.EnvVars.StripeSecretKey
		cc.StripePublishableKey = yamlData.EnvVars.StripePublishableKey
		err = card.SetConfig(cc)
		if err != nil {
			log.Fatalln("Could not set configuration for card.", err)
			return
		}
		err = os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", pathToDatastoreCredentials)
		if err != nil {
			log.Fatalln("Could not set Google Datastore credentials environmental variable.", err)
			return
		}
		ccc := datastoreutils.Config
		ccc.ProjectID = yamlData.EnvVars.ProjectID
		ccc.Development = useDevDatastore
		err = datastoreutils.SetConfig(ccc)
		if err != nil {
			log.Fatalln("Could not set configuration for datastore.", err)
			return
		}
		cccc := templates.Config
		cccc.PathToTemplates = yamlData.EnvVars.TemplatesPath
		cccc.Development = true
		cccc.UseLocalFiles, _ = strconv.ParseBool(yamlData.EnvVars.UseLocalFiles)
		templates.SetConfig(cccc)
		staticLocalDir = yamlData.EnvVars.StaticFilePath
		cacheDays = yamlData.EnvVars.CacheDays
	case deploymentTypeSqlite:
		yamlData, err := parseAppYaml(pathToAppYaml)
		if err != nil {
			log.Fatalln("Error while parsing app.yaml.", err)
			return
		}
		parsedAppYaml = yamlData
		c := sessionutils.Config
		c.SessionAuthKey = yamlData.EnvVars.SessionAuthKey
		c.SessionEncryptKey = yamlData.EnvVars.SessionEncryptKey
		c.SessionLifetime = yamlData.EnvVars.SessionLifetime
		c.CookieDomain = yamlData.EnvVars.CookieDomain
		err = sessionutils.SetConfig(c)
		if err != nil {
			log.Fatalln("Could not set configuration for sessionutils.", err)
			return
		}
		cc := card.Config
		cc.StripeSecretKey = yamlData.EnvVars.StripeSecretKey
		cc.StripePublishableKey = yamlData.EnvVars.StripePublishableKey
		err = card.SetConfig(cc)
		if err != nil {
			log.Fatalln("Could not set configuration for card.", err)
			return
		}
		ccc := sqliteutils.Config
		ccc.PathToDatabaseFile = yamlData.EnvVars.PathToSqliteFile
		sqliteutils.SetConfig(ccc)
		sqliteutils.Connect()
		cccc := templates.Config
		cccc.PathToTemplates = yamlData.EnvVars.TemplatesPath
		cccc.Development = true
		cccc.UseLocalFiles, _ = strconv.ParseBool(yamlData.EnvVars.UseLocalFiles)
		templates.SetConfig(cccc)
		staticLocalDir = yamlData.EnvVars.StaticFilePath
		cacheDays = yamlData.EnvVars.CacheDays
	default:
		log.Fatalln("An invalid deployment type was given as a flag.")
		return
	}
	if cacheDays < 0 {
		log.Fatalln("Cache days value should be a value greater than or equal to zero.")
		return
	}
	return
}
func main() {
	a := alice.New(middleware.Auth)
	admin := a.Append(middleware.Administrator)
	add := a.Append(middleware.AddCards)
	remove := a.Append(middleware.RemoveCards)
	charge := a.Append(middleware.ChargeCards)
	reports := a.Append(middleware.ViewReports)
	r := mux.NewRouter()
	r.StrictSlash(true)
	r.HandleFunc("/", pages.Login)
	r.HandleFunc("/setup/", pages.CreateAdminShow)
	r.HandleFunc("/create-admin/", users.CreateAdmin).Methods("POST")
	r.HandleFunc("/login/", users.Login)
	r.HandleFunc("/logout/", users.Logout)
	r.HandleFunc("/cron/remove-expired-cards/", http.HandlerFunc(card.RemoveExpiredCards))
	r.HandleFunc("/cron/remove-unused-cards/", http.HandlerFunc(card.RemoveUnusedCards))
	r.Handle("/main/", a.Then(http.HandlerFunc(pages.Main)))
	r.Handle("/diag/", http.HandlerFunc(diag))
	u := r.PathPrefix("/users").Subrouter()
	u.Handle("/add/", admin.Then(http.HandlerFunc(users.Add))).Methods("POST")
	u.Handle("/get/", a.Then(http.HandlerFunc(users.GetOne))).Methods("GET")
	u.Handle("/get/all/", admin.Then(http.HandlerFunc(users.GetAll))).Methods("GET")
	u.Handle("/change-pwd/", admin.Then(http.HandlerFunc(users.ChangePwd))).Methods("POST")
	u.Handle("/update/", admin.Then(http.HandlerFunc(users.UpdatePermissions))).Methods("POST")
	c := r.PathPrefix("/card").Subrouter()
	c.Handle("/add/", add.Then(http.HandlerFunc(card.Add))).Methods("POST")
	c.Handle("/get/", a.Then(http.HandlerFunc(card.GetOne))).Methods("GET")
	c.Handle("/get/all/", a.Then(http.HandlerFunc(card.GetAll))).Methods("GET")
	c.Handle("/remove/", remove.Then(http.HandlerFunc(card.RemoveAPI))).Methods("POST")
	c.Handle("/charge/", charge.Then(http.HandlerFunc(card.ManualCharge))).Methods("POST")
	c.Handle("/receipt/", a.Then(http.HandlerFunc(receipt.Show))).Methods("GET")
	c.Handle("/report/", reports.Then(http.HandlerFunc(card.Report))).Methods("GET")
	c.Handle("/refund/", charge.Then(http.HandlerFunc(card.Refund))).Methods("POST")
	c.Handle("/capture/", charge.Then(http.HandlerFunc(card.Capture))).Methods("POST")
	c.Handle("/auto-charge/", http.HandlerFunc(card.AutoCharge)).Methods("POST")
	comp := r.PathPrefix("/company").Subrouter()
	comp.Handle("/get/", a.Then(http.HandlerFunc(company.GetAPI))).Methods("GET")
	comp.Handle("/set/", admin.Then(http.HandlerFunc(company.SaveAPI))).Methods("POST")
	comp.Handle("/preview-receipt/", admin.Then(http.HandlerFunc(receipt.Preview))).Methods("GET")
	as := r.PathPrefix("/app-settings").Subrouter()
	as.Handle("/get/", a.Then(http.HandlerFunc(appsettings.GetAPI))).Methods("GET")
	as.Handle("/set/", admin.Then(http.HandlerFunc(appsettings.SaveAPI))).Methods("POST")
	as.Handle("/generate-api-key", admin.Then(http.HandlerFunc(appsettings.GenerateAPIKey))).Methods("GET")
	r.PathPrefix(staticWebDir).Handler(setStaticFileHeaders(http.StripPrefix(staticWebDir, http.FileServer(http.Dir(staticLocalDir)))))
	r.PathPrefix("/").Handler(http.FileServer(http.Dir(staticLocalDir + "root-files/")))
	log.Println("Starting stripe-appengine-frontend...")
	port := os.Getenv("PORT")
	if port == "" {
		port = defaultPort
	}
	log.Println("Listening on port:", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), r))
}
func parseAppYaml(path string) (yamlData appYaml, err error) {
	if len(path) < 1 {
		return appYaml{}, errors.New("no path was given for the app.yaml file")
	}
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return appYaml{}, err
	}
	fileData, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}
	err = yaml.Unmarshal(fileData, &yamlData)
	if err != nil {
		return
	}
	return
}
func setStaticFileHeaders(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		maxAge := cacheDays * 24 * 60 * 60
		w.Header().Set("Cache-Control", "no-transform,public,max-age="+strconv.Itoa(maxAge))
		h.ServeHTTP(w, r)
	})
}