Source

gowebexp / web / app.go

package web

import (
	"fmt"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"html/template"
	"net/http"
	//"os"
	"crypto/rand"
	"encoding/base64"
	"gowebexp/pages"
	"path/filepath"
)

// TODO: Use flag to set parameter: IP, PORT, path

// Implement a sigleton Pattern
type WebApp struct {
	Router      *mux.Router
	Storage     Storage
	Path        string
	CookieStore *sessions.CookieStore
}

func newWebApp() *WebApp {
	storage, err := NewMemStorage()
	if err != nil {
		fmt.Println("An error occured while creating the storage : ", err)
	}
	//path, err := os.Getwd()
	//if err != nil {
	//	fmt.Println("An error occured while trying to retireve the current path", err)
	//}
	path := "/home/yml/Dropbox/Devs/golang/gorilla_on_web/src/gowebexp/"
	app := &WebApp{
		Router:      mux.NewRouter(),
		Storage:     storage,
		Path:        path,
		CookieStore: sessions.NewCookieStore([]byte("something-very-secret"))}
	return app
}

func (app *WebApp) Template(name string) string {
	return filepath.Join(app.Path, "web", "templates", name)
}

func (app *WebApp) GenerateCsrfToken() string {
	// Arbitrary length of 80
	token := make([]byte, 80)
	rand.Read(token)
	return base64.StdEncoding.EncodeToString(token)
}

func (app *WebApp) SetRoute() {
	// request multiplexer
	app.Router.HandleFunc("/inspect/{slug}/", RequestInspector).Name("inspector")
	app.Router.HandleFunc("/pages/{slug}/", PageDetail).Name("page_detail")
	app.Router.HandleFunc("/pages/", PageList).Name("page_list")
	app.Router.HandleFunc("/", Index).Name("index")
}

func (app *WebApp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Get the session and write a DummyKey
	session, _ := App.CookieStore.Get(r, "gowebexp")
	csrf_token, ok := session.Values["csrf_token"]
	if !ok {
		csrf_token = App.GenerateCsrfToken()
		session.Values["csrf_token"] = csrf_token
		App.CookieStore.Save(r, w, session)
	}

	context.Set(r, "csrf_token", csrf_token)
	if r.Method == "POST" && r.FormValue("csrf_token") != csrf_token {
		http.Error(w, "Fobidden", http.StatusForbidden)
	} else {
		app.Router.ServeHTTP(w, r)
	}
}

// Initialize my app object
var App = newWebApp()

func RequestInspector(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "URL: %s, method: %s, vars: %s", r.URL, r.Method, mux.Vars(r))
}

func Index(w http.ResponseWriter, r *http.Request) {
	pages_url, err := App.Router.Get("page_list").URL()
	if err != nil {
		panic(err)
	}
	tmpl := template.Must(template.ParseFiles(
		App.Template("index.html"), App.Template("base.html")))
	ctx := make(map[string]interface{})
	ctx["pages_url"] = pages_url
	ctx["title"] = "Web experiment with GO"
	err = tmpl.ExecuteTemplate(w, "base", ctx)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func PageList(w http.ResponseWriter, r *http.Request) {
	ctx := make(map[string]interface{})
	ctx["csrf_token"] = context.Get(r, "csrf_token")
	if r.Method == "POST" {
		page := pages.Page{r.FormValue("name"),
			r.FormValue("slug"),
			r.FormValue("content")}

		validationErrors, err := page.Validate()
		if err != nil {
			ctx["validationErrors"] = validationErrors
		} else {
			App.Storage.AddPage(page)
			// Redirect to / after the creation of the page
			http.Redirect(w, r, "/pages/", 302)
		}
	}

	// DISCUSS template inheritance it feels weird
	t, err := template.ParseFiles(App.Template("pages.html"), App.Template("base.html"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	ctx["storage"] = App.Storage
	err = t.ExecuteTemplate(w, "base", ctx)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func PageDetail(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles(
		App.Template("detail_page.html"), App.Template("base.html"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	ctx := make(map[string]interface{})
	ctx["page"], err = App.Storage.GetPageBySlug(mux.Vars(r)["slug"])
	if err != nil {
		http.NotFound(w, r)
		return
	}
	err = t.ExecuteTemplate(w, "base", ctx)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}