200 lines
4.4 KiB
Go
200 lines
4.4 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"html/template"
|
||
|
"io/ioutil"
|
||
|
"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 = "gjvote"
|
||
|
|
||
|
// SiteData is stuff that stays the same
|
||
|
type siteData struct {
|
||
|
Title string `json:"title"`
|
||
|
Port int `json:"port"`
|
||
|
SessionName string `json:"session"`
|
||
|
ServerDir string `json:"dir"`
|
||
|
DevMode bool `json:"devmode"`
|
||
|
DB string `json:"db"`
|
||
|
}
|
||
|
|
||
|
// pageData is stuff that changes per request
|
||
|
type pageData struct {
|
||
|
Site *siteData
|
||
|
Title string
|
||
|
HideTitleImage bool
|
||
|
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
|
||
|
}
|
||
|
|
||
|
var sessionSecret = "JCOP5e8ohkTcOzcSMe74"
|
||
|
|
||
|
var sessionStore = sessions.NewCookieStore([]byte(sessionSecret))
|
||
|
var site *siteData
|
||
|
var r *mux.Router
|
||
|
var configFile string
|
||
|
|
||
|
func main() {
|
||
|
configFile = "./config.json"
|
||
|
loadConfig()
|
||
|
saveConfig()
|
||
|
initialize()
|
||
|
|
||
|
r = mux.NewRouter()
|
||
|
r.StrictSlash(true)
|
||
|
|
||
|
s := http.StripPrefix("/assets/", http.FileServer(http.Dir(site.ServerDir+"assets/")))
|
||
|
r.PathPrefix("/assets/").Handler(s)
|
||
|
|
||
|
// Public Subrouter
|
||
|
pub := r.PathPrefix("/").Subrouter()
|
||
|
pub.HandleFunc("/", handleMain)
|
||
|
|
||
|
// API Subrouter
|
||
|
//api := r.PathPrefix("/api").Subtrouter()
|
||
|
|
||
|
// Admin Subrouter
|
||
|
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)
|
||
|
|
||
|
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 loadConfig() {
|
||
|
site = new(siteData)
|
||
|
// Defaults:
|
||
|
site.Title = "ICT GameJam"
|
||
|
site.Port = 8080
|
||
|
site.SessionName = "ict-gamejam"
|
||
|
site.ServerDir = "./"
|
||
|
site.DevMode = false
|
||
|
site.DB = AppName + ".db"
|
||
|
|
||
|
jsonInp, err := ioutil.ReadFile(configFile)
|
||
|
if err == nil {
|
||
|
assertError(json.Unmarshal(jsonInp, &site))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func saveConfig() {
|
||
|
var jsonInp []byte
|
||
|
jsonInp, _ = json.Marshal(site)
|
||
|
assertError(ioutil.WriteFile(configFile, jsonInp, 0644))
|
||
|
}
|
||
|
|
||
|
func initialize() {
|
||
|
// Check if the database has been created
|
||
|
assertError(openDatabase())
|
||
|
|
||
|
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 (p *pageData) show(tmplName string, w http.ResponseWriter) error {
|
||
|
for _, tmpl := range []string{
|
||
|
"htmlheader.html",
|
||
|
"admin-menu.html",
|
||
|
"header.html",
|
||
|
tmplName,
|
||
|
"footer.html",
|
||
|
"htmlfooter.html",
|
||
|
} {
|
||
|
if err := outputTemplate(tmpl, p, w); err != nil {
|
||
|
fmt.Printf("%s\n", err)
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// outputTemplate
|
||
|
// Spit out a template
|
||
|
func outputTemplate(tmplName string, tmplData interface{}, w http.ResponseWriter) error {
|
||
|
_, err := os.Stat("templates/" + tmplName)
|
||
|
if err == nil {
|
||
|
t := template.New(tmplName)
|
||
|
t, _ = t.ParseFiles("templates/" + tmplName)
|
||
|
return t.Execute(w, tmplData)
|
||
|
}
|
||
|
return fmt.Errorf("WebServer: Cannot load template (templates/%s): File not found", tmplName)
|
||
|
}
|
||
|
|
||
|
// redirect can be used only for GET redirects
|
||
|
func redirect(url string, w http.ResponseWriter, req *http.Request) {
|
||
|
http.Redirect(w, req, url, 303)
|
||
|
}
|
||
|
|
||
|
func printOutput(out string) {
|
||
|
if site.DevMode {
|
||
|
fmt.Print(out)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func assertError(err error) {
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|