work on porting routers to more gorilla/mux style and making all actions POST and csrf trackable
This commit is contained in:
parent
e07b20b531
commit
df5dca3a52
|
@ -29,3 +29,5 @@ _testmain.go
|
||||||
*.exe
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.prof
|
*.prof
|
||||||
|
|
||||||
|
.*.swp
|
||||||
|
|
13
js/funcs.js
13
js/funcs.js
|
@ -3,14 +3,12 @@ $(document).ready( function () {
|
||||||
|
|
||||||
$(".confirm-export").confirm({
|
$(".confirm-export").confirm({
|
||||||
"text": "Mark current batch exported?",
|
"text": "Mark current batch exported?",
|
||||||
"title": "Export confrimation",
|
"title": "Export confirmation",
|
||||||
confirm: function() {
|
"confirm": function(form) {
|
||||||
window.location = "/export-commit";
|
form.submit();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if( $('.addedLink').length > 0) {
|
if( $('.addedLink').length > 0) {
|
||||||
setTimeout(function (){
|
setTimeout(function (){
|
||||||
window.close();
|
window.close();
|
||||||
|
@ -35,10 +33,13 @@ $(document).ready( function () {
|
||||||
cancelButton: "No",
|
cancelButton: "No",
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".confirm-delete").confirm({
|
$(".confirm-news-delete").confirm({
|
||||||
"text": "Delete news item?",
|
"text": "Delete news item?",
|
||||||
"title": "Delete confirmation",
|
"title": "Delete confirmation",
|
||||||
confirmButton: "Yes",
|
confirmButton: "Yes",
|
||||||
cancelButton: "No",
|
cancelButton: "No",
|
||||||
|
"confirm": function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
11
main.go
11
main.go
|
@ -102,14 +102,15 @@ func main() {
|
||||||
loadConfig(*envFlag)
|
loadConfig(*envFlag)
|
||||||
dbConnect()
|
dbConnect()
|
||||||
initTemplates()
|
initTemplates()
|
||||||
//CSRF := csrf.Protect([]byte(csrfSecret()));
|
muxRouter := init_route_handlers()
|
||||||
r := init_route_handlers()
|
//errHandler := csrf.ErrorHandler( CSRFErrorHandler{} )
|
||||||
errHandle := csrf.ErrorHandler( CSRFErrorHandler{} )
|
|
||||||
sec := csrf.Secure(false)
|
// Terrible. TODO: Get SSL for prod, and then wrap in if(dev) { {
|
||||||
|
csrfSecurityOption := csrf.Secure(false)
|
||||||
|
|
||||||
fmt.Println("Listening on", config.Port, "...")
|
fmt.Println("Listening on", config.Port, "...")
|
||||||
|
|
||||||
err := http.ListenAndServe(":"+config.Port, csrf.Protect([]byte("12345678901234567890123456789012"), errHandle, sec)(r)) //csrfSecret()))(r))
|
err := http.ListenAndServe(":"+config.Port, csrf.Protect([]byte(csrfSecret()), /*errHandler,*/ csrfSecurityOption)(muxRouter))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Fatal Error: ", err)
|
fmt.Println("Fatal Error: ", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,10 @@ type News struct {
|
||||||
Category_id int
|
Category_id int
|
||||||
Date time.Time
|
Date time.Time
|
||||||
Notes string
|
Notes string
|
||||||
Expoerted bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SQL_NEWS_FIELDS = "id, url, title, category_id, timestamp, notes, exported"
|
SQL_NEWS_FIELDS = "id, url, title, category_id, timestamp, notes"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Storage Node containing:
|
/* Storage Node containing:
|
||||||
|
@ -95,7 +94,7 @@ func Get(db *sql.DB, id int) (*News, error) {
|
||||||
func LoadPage(db *sql.DB, offset, amount int) ([]*News, int, error) {
|
func LoadPage(db *sql.DB, offset, amount int) ([]*News, int, error) {
|
||||||
categories.LoadCategories(db) // required by addContainer
|
categories.LoadCategories(db) // required by addContainer
|
||||||
|
|
||||||
rows, err := db.Query("SELECT "+SQL_NEWS_FIELDS+" FROM news WHERE exported is null order by timestamp DESC")
|
rows, err := db.Query("SELECT "+SQL_NEWS_FIELDS+" FROM news order by timestamp DESC")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("DB errpr reading LoadPage news: ", err)
|
fmt.Println("DB errpr reading LoadPage news: ", err)
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
@ -174,8 +173,7 @@ func scanNews(rows *sql.Rows) (*News, error) {
|
||||||
news := &News{}
|
news := &News{}
|
||||||
var url, title, notes sql.NullString
|
var url, title, notes sql.NullString
|
||||||
var category_id sql.NullInt64
|
var category_id sql.NullInt64
|
||||||
var exported sql.NullBool
|
err := rows.Scan(&news.id, &url, &title, &category_id, &news.Date, ¬es)
|
||||||
err := rows.Scan(&news.id, &url, &title, &category_id, &news.Date, ¬es, &exported)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error reading news from DB: " + err.Error())
|
fmt.Println("Error reading news from DB: " + err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -184,7 +182,6 @@ func scanNews(rows *sql.Rows) (*News, error) {
|
||||||
news.Url = nullStringToString(&url)
|
news.Url = nullStringToString(&url)
|
||||||
news.Title = nullStringToString(&title)
|
news.Title = nullStringToString(&title)
|
||||||
news.Notes = nullStringToString(¬es)
|
news.Notes = nullStringToString(¬es)
|
||||||
news.Expoerted = nullBoolToBool(&exported)
|
|
||||||
|
|
||||||
if category_id.Valid {
|
if category_id.Valid {
|
||||||
news.Category_id = int(category_id.Int64)
|
news.Category_id = int(category_id.Int64)
|
||||||
|
|
|
@ -176,7 +176,7 @@ func addPostHandler(w http.ResponseWriter, r *http.Request, user *user.User, ses
|
||||||
session.AddFlash("Added news \""+news.Title+"\"", flash_info)
|
session.AddFlash("Added news \""+news.Title+"\"", flash_info)
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
if popup == "1" {
|
if popup == "1" {
|
||||||
http.Redirect(w, r, "/added", http.StatusFound)
|
http.Redirect(w, r, "/news/added", http.StatusFound)
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ func exportHandler(w http.ResponseWriter, r *http.Request, user *user.User, sess
|
||||||
session.AddFlash("Last batch of news marked exported", flash_info)
|
session.AddFlash("Last batch of news marked exported", flash_info)
|
||||||
}
|
}
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/export", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addedHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
|
func addedHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
|
||||||
|
@ -294,7 +294,7 @@ func addedHandler(w http.ResponseWriter, r *http.Request, user *user.User, sessi
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
|
func deleteHandler(w http.ResponseWriter, r *http.Request, user *user.User, session *sessions.Session) {
|
||||||
id, idErr := strconv.Atoi(r.FormValue("id"))
|
id, idErr := strconv.Atoi(mux.Vars(r)["id"])
|
||||||
|
|
||||||
if idErr != nil {
|
if idErr != nil {
|
||||||
session.AddFlash("Invalid news to delete", flash_err)
|
session.AddFlash("Invalid news to delete", flash_err)
|
||||||
|
@ -412,7 +412,6 @@ func newsFormHandler(w http.ResponseWriter, r *http.Request, user *user.User, se
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowTemplate("news", w, r, map[string]interface{}{"user": user, "flashes": flashes, "news": news, "count": count, "categories": categories.CategoriesFlat})
|
ShowTemplate("news", w, r, map[string]interface{}{"user": user, "flashes": flashes, "news": news, "count": count, "categories": categories.CategoriesFlat})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServeFileHandler(res http.ResponseWriter, req *http.Request) {
|
func ServeFileHandler(res http.ResponseWriter, req *http.Request) {
|
||||||
|
@ -421,41 +420,46 @@ func ServeFileHandler(res http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init_route_handlers() *mux.Router {
|
func init_route_handlers() *mux.Router {
|
||||||
|
|
||||||
// Mux + CSRF
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
|
|
||||||
// Basic Handle - static files - no CSRF wrapper
|
// Basic Handle - static files - no CSRF wrapper
|
||||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir("js/"))))
|
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir("js/"))))
|
||||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("css/"))))
|
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("css/"))))
|
||||||
r.PathPrefix("/fonts/").Handler(http.StripPrefix("/fonts", http.FileServer(http.Dir("fonts/"))))
|
r.PathPrefix("/fonts/").Handler(http.StripPrefix("/fonts", http.FileServer(http.Dir("fonts/"))))
|
||||||
r.HandleFunc("/favicon.ico", ServeFileHandler)
|
r.HandleFunc("/favicon.ico", ServeFileHandler)
|
||||||
|
|
||||||
|
|
||||||
rGet := r.Methods("GET").Subrouter()
|
rGet := r.Methods("GET").Subrouter()
|
||||||
rPost := r.Methods("POST").Subrouter()
|
rPost := r.Methods("POST").Subrouter()
|
||||||
|
|
||||||
rGet.HandleFunc("/login", LoginFormHandler)
|
rGet.HandleFunc("/login", LoginFormHandler)
|
||||||
rPost.HandleFunc("/login", LoginPostHandler)
|
rPost.HandleFunc("/login", LoginPostHandler)
|
||||||
|
|
||||||
r.HandleFunc("/logout", userHandler(LogoutHandler))
|
rPost.HandleFunc("/logout", userHandler(LogoutHandler))
|
||||||
|
|
||||||
r.HandleFunc("/add", getPostHandler(userHandler(addFormHandler), userHandler(addPostHandler)))
|
rGet.HandleFunc("/news/add", userHandler(addFormHandler))
|
||||||
r.HandleFunc("/", userHandler(newsFormHandler))
|
rPost.HandleFunc("/news/add", userHandler(addPostHandler))
|
||||||
r.HandleFunc("/news", userHandler(newsFormHandler))
|
|
||||||
r.HandleFunc("/export", userHandler(templateFormHandler))
|
|
||||||
r.HandleFunc("/export-commit", userHandler(exportHandler))
|
|
||||||
r.HandleFunc("/added", userHandler(addedHandler))
|
|
||||||
r.HandleFunc("/delete", userHandler(deleteHandler))
|
|
||||||
r.HandleFunc("/edit", getPostHandler(userHandler(editFormHandler), userHandler(editPostHandler)))
|
|
||||||
|
|
||||||
r.HandleFunc("/categories", getPostHandler(userHandler(categoriesFormHandler), userHandler(categoriesPostHandler)))
|
rGet.HandleFunc("/", userHandler(newsFormHandler))
|
||||||
r.HandleFunc("/categories/change-parent", userHandler(categoryChangeParentHandler))
|
rGet.HandleFunc("/news", userHandler(newsFormHandler))
|
||||||
r.HandleFunc("/categories/add", userHandler(categoryAddHandler))
|
|
||||||
r.HandleFunc("/categories/delete", userHandler(categoryDeleteHandler))
|
|
||||||
|
|
||||||
//http.Handle("/", r)
|
rGet.HandleFunc("/news/export", userHandler(templateFormHandler))
|
||||||
|
rPost.HandleFunc("/news/export", userHandler(exportHandler))
|
||||||
|
|
||||||
|
rGet.HandleFunc("/news/added", userHandler(addedHandler))
|
||||||
|
|
||||||
|
// TODO to post and {id}
|
||||||
|
rPost.HandleFunc("/news/{id:[0-9]+}/delete", userHandler(deleteHandler))
|
||||||
|
// TODO post {id} ?
|
||||||
|
rGet.HandleFunc("/news/{id:[0-9]+}/edit", userHandler(editFormHandler))
|
||||||
|
rPost.HandleFunc("/news/{id:[0-9]+}/edit", userHandler(editPostHandler))
|
||||||
|
|
||||||
|
rGet.HandleFunc("/categories", userHandler(categoriesFormHandler))
|
||||||
|
rPost.HandleFunc("/caegories", userHandler(categoriesPostHandler))
|
||||||
|
|
||||||
|
// TODO post, add {id}
|
||||||
|
rPost.HandleFunc("/categories/change-parent", userHandler(categoryChangeParentHandler))
|
||||||
|
rPost.HandleFunc("/categories/add", userHandler(categoryAddHandler))
|
||||||
|
rPost.HandleFunc("/categories/delete", userHandler(categoryDeleteHandler))
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,15 +32,15 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse navbar-collapse">
|
<div class="collapse navbar-collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li><a href="/add">add</a></li>
|
<li><a href="/news/add">add</a></li>
|
||||||
<li><a href="/export">export</a></li>
|
<li><a href="/news/export">export</a></li>
|
||||||
<li><a href="/categories">categories</a></li>
|
<li><a href="/categories">categories</a></li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
{{if .user}}
|
{{if .user}}
|
||||||
<li>{{.user.Username}}</li>
|
<li>{{.user.Username}}</li>
|
||||||
<li><a href="/logout">Logout</a></li>
|
<li><form method="POST" action="/logout">{{ .csrfField }}<input type="submit" value="Logout" class="btn btn-sm btn-primary btn-block" /></form></li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li><a href="/login">Log in</a></li>
|
<li><a href="/login">Log in</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-2">
|
||||||
<button class="confirm-export btn btn-lg btn-primary btn-block" type="submit">Export</button>
|
<form method="POST" action="/news/export" class="confirm-export">
|
||||||
|
{{ .csrfField }}
|
||||||
|
<input class="btn btn-lg btn-primary btn-block" type="submit" value="Export" />
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
|
|
|
@ -44,12 +44,12 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-2"> </div>
|
<div class="col-xs-2"> </div>
|
||||||
<div class="col-xs-8 post-preview">{{truncate .post.Notes 500}}</div>
|
<div class="col-xs-8 post-preview">{{truncate .post.Notes 500}}</div>
|
||||||
<div class="col-xs-2 post-delete"><a class="confirm-delete" href="/delete?id={{.post.Id}}">Delete</a></div>
|
<div class="col-xs-2 post-delete"><form method="POST" action="/news/{{.post.Id}}/delete" class="confirm-news-delete">{{ .csrfField }}<input type="submit" class="btn btn-primary btn-block" value="Delete" /></form></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<!-- JS for the launcher of the add bookmarklet -->
|
<!-- JS for the launcher of the add bookmarklet -->
|
||||||
{{define "launch-add"}}
|
{{define "launch-add"}}
|
||||||
javascript:var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='{{.url}}add',l=d.location,e=encodeURIComponent,u=f+'?popup=1&url='+e(l.href)+'&title='+e(d.title);a=function(){if(!w.open(u,'t','toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=410'))l.href=u;};if (/Firefox/.test(navigator.userAgent)) setTimeout(a, 0); else a();void(0)
|
javascript:var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='{{.url}}news/add',l=d.location,e=encodeURIComponent,u=f+'?popup=1&url='+e(l.href)+'&title='+e(d.title);a=function(){if(!w.open(u,'t','toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=410'))l.href=u;};if (/Firefox/.test(navigator.userAgent)) setTimeout(a, 0); else a();void(0)
|
||||||
{{end}}
|
{{end}}
|
|
@ -1,9 +1,8 @@
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<h2 class="form-add-heading">{{if eq .mode "add"}}Add Link{{else}}Edit News{{end}}</h2>
|
<h2 class="form-add-heading">{{if eq .mode "add"}}Add Link{{else}}Edit News{{end}}</h2>
|
||||||
{{template "flashes" .}}
|
{{template "flashes" .}}
|
||||||
<form class="form-add" action="{{if eq "add" .mode}}/add{{else}}/edit{{end}}" method="post" role="form" class="container col-form">
|
<form class="form-add" action="{{if eq "add" .mode}}/news/add{{else}}/news/{{.id}}/edit{{end}}" method="post" role="form" class="container col-form">
|
||||||
<input type="hidden" name="popup" value="{{.popup}}" />
|
<input type="hidden" name="popup" value="{{.popup}}" />
|
||||||
<input type="hidden" name="id" value="{{.id}}" />
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-2">Link:</div><div class="col-xs-10"><input type="text" class="form-control" name="link" placeholder="Link" value="{{.link}}"/></div>
|
<div class="col-xs-2">Link:</div><div class="col-xs-10"><input type="text" class="form-control" name="link" placeholder="Link" value="{{.link}}"/></div>
|
||||||
<div class="col-xs-2">Title:</div><div class="col-xs-10"><input type="text" class="form-control" name="title" placeholder="Title" value="{{.title}}"/></div>
|
<div class="col-xs-2">Title:</div><div class="col-xs-10"><input type="text" class="form-control" name="title" placeholder="Title" value="{{.title}}"/></div>
|
||||||
|
|
Loading…
Reference in New Issue