package main //go:generate esc -o assets.go assets templates import ( "bufio" "fmt" "html/template" "log" "net/http" "os" "strconv" "strings" "golang.org/x/crypto/ssh/terminal" "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/gorilla/sessions" "github.com/justinas/alice" ) const AppName = "gosscms" const DbName = AppName + ".db" type siteData struct { Title string Port int SessionName string ServerDir string DevMode bool } type pageData struct { Site *siteData Title string SubTitle string Stylesheets []string HeaderScripts []string Scripts []string FlashMessage string FlashClass string LoggedIn bool Menu []menuItem BottomMenu []menuItem session *pageSession TemplateData interface{} } type menuItem struct { Label string Location string Icon string } type Routes map[string]http.HandlerFunc var sessionSecret = "JCOP5e8ohkTcOzcSMe74" var sessionStore = sessions.NewCookieStore([]byte(sessionSecret)) var site *siteData var r *mux.Router func main() { loadConfig() dbSaveSiteConfig(site) initialize() if site.DevMode { fmt.Println("Operating in Development Mode") } r = mux.NewRouter() r.StrictSlash(true) r.PathPrefix("/admin/assets").Handler(http.StripPrefix("/admin/", http.FileServer(FS(site.DevMode)))) admin := r.PathPrefix("/admin").Subrouter() admin.HandleFunc("/", handleAdmin) admin.HandleFunc("/dologin", handleAdminDoLogin) admin.HandleFunc("/dologout", handleAdminDoLogout) admin.HandleFunc("/{category}", handleAdmin) admin.HandleFunc("/{category}/{id}", handleAdmin) admin.HandleFunc("/{category}/{id}/{function}", handleAdmin) r.PathPrefix("/").Handler(http.FileServer(http.Dir("public"))) http.Handle("/", r) chain := alice.New(loggingHandler).Then(r) fmt.Printf("Listening on port %d\n", site.Port) log.Fatal(http.ListenAndServe("127.0.0.1:"+strconv.Itoa(site.Port), chain)) } func PublicHandler(w http.ResponseWriter, r *http.Request) { fmt.Println(r.URL.Path) http.ServeFile(w, r, "./public") } func loadConfig() { site = dbGetSiteConfig() if len(os.Args) > 1 { for _, v := range os.Args { key := v val := "" eqInd := strings.Index(v, "=") if eqInd > 0 { // It's a key/val argument key = v[:eqInd] val = v[eqInd+1:] } switch key { case "-title": site.Title = val fmt.Print("Set site title: ", site.Title, "\n") case "-port": var tryPort int var err error if tryPort, err = strconv.Atoi(val); err != nil { fmt.Print("Invalid port given: ", val, " (Must be an integer)\n") tryPort = site.Port } // TODO: Make sure a valid port number is given site.Port = tryPort case "-session-name": site.SessionName = val case "-server-dir": // TODO: Probably check if the given directory is valid site.ServerDir = val case "-help", "-h", "-?": printHelp() done() case "-dev": site.DevMode = true case "-reset-defaults": resetToDefaults() done() } } } } func initialize() { // Check if the database has been created assertError(initDatabase()) if !dbHasUser() { reader := bufio.NewReader(os.Stdin) fmt.Println("Create new Admin user") fmt.Print("Email: ") email, _ := reader.ReadString('\n') email = strings.TrimSpace(email) var pw1, pw2 []byte for string(pw1) != string(pw2) || string(pw1) == "" { fmt.Print("Password: ") pw1, _ = terminal.ReadPassword(0) fmt.Println("") fmt.Print("Repeat Password: ") pw2, _ = terminal.ReadPassword(0) fmt.Println("") if string(pw1) != string(pw2) { fmt.Println("Entered Passwords don't match!") } } assertError(dbUpdateUserPassword(email, string(pw1))) } } func loggingHandler(h http.Handler) http.Handler { return handlers.LoggingHandler(os.Stdout, h) } func resetToDefaults() { def := GetDefaultSiteConfig() fmt.Println("Reset settings to defaults?") fmt.Print(site.Title, " -> ", def.Title, "\n") fmt.Print(site.Port, " -> ", def.Port, "\n") fmt.Print(site.SessionName, " -> ", def.SessionName, "\n") fmt.Print(site.ServerDir, " -> ", def.ServerDir, "\n") fmt.Println("Are you sure? (y/N): ") reader := bufio.NewReader(os.Stdin) conf, _ := reader.ReadString('\n') conf = strings.ToUpper(strings.TrimSpace(conf)) if strings.HasPrefix(conf, "Y") { if dbSaveSiteConfig(def) != nil { errorExit("Error resetting to defaults") } fmt.Println("Reset to defaults") } } func printHelp() { help := []string{ "Game Jam Voting Help", " -help, -h, -? Print this message", " -dev Development mode, load assets from file system", " -port= Set the site port", " -session-name= Set the name of the session to be used", " -server-dir= Set the server directory", " This designates where the database will be saved", " and where the app will look for files if you're", " operating in 'development' mode (-dev)", " -title= Set the site title", " -reset-defaults Reset all configuration options to defaults", "", } for _, v := range help { fmt.Println(v) } } func done() { os.Exit(0) } func errorExit(msg string) { fmt.Println(msg) os.Exit(1) } func assertError(err error) { if err != nil { panic(err) } } // redirect can be used only for GET redirects func redirect(url string, w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, url, 303) } // outputTemplate // Spit out a template func outputTemplate(tmplName string, tmplData interface{}, w http.ResponseWriter) error { n := "/templates/" + tmplName l := template.Must(template.New("layout").Parse(FSMustString(site.DevMode, n))) t := template.Must(l.Parse(FSMustString(site.DevMode, n))) return t.Execute(w, tmplData) }