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
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
.*.swp
|
||||
|
|
23
js/funcs.js
23
js/funcs.js
|
@ -3,42 +3,43 @@ $(document).ready( function () {
|
|||
|
||||
$(".confirm-export").confirm({
|
||||
"text": "Mark current batch exported?",
|
||||
"title": "Export confrimation",
|
||||
confirm: function() {
|
||||
window.location = "/export-commit";
|
||||
"title": "Export confirmation",
|
||||
"confirm": function(form) {
|
||||
form.submit();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
if( $('.addedLink').length > 0) {
|
||||
setTimeout(function (){
|
||||
window.close();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
|
||||
$("form.form-add").submit( function(e) {
|
||||
if ( $(".add-category").val() == -1) {
|
||||
e.preventDefault();
|
||||
$(".add-category-col").addClass("has-error");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$(".category-change-parent").change(function (e) {
|
||||
e.target.parentElement.submit();
|
||||
});
|
||||
|
||||
|
||||
$('.cat-delete').confirm({
|
||||
text: "Are you sure you want to delete this category?",
|
||||
title: "Confirmation required",
|
||||
confirmButton: "Yes",
|
||||
cancelButton: "No",
|
||||
});
|
||||
|
||||
$(".confirm-delete").confirm({
|
||||
|
||||
$(".confirm-news-delete").confirm({
|
||||
"text": "Delete news item?",
|
||||
"title": "Delete confirmation",
|
||||
confirmButton: "Yes",
|
||||
cancelButton: "No",
|
||||
"confirm": function(form) {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
11
main.go
11
main.go
|
@ -102,14 +102,15 @@ func main() {
|
|||
loadConfig(*envFlag)
|
||||
dbConnect()
|
||||
initTemplates()
|
||||
//CSRF := csrf.Protect([]byte(csrfSecret()));
|
||||
r := init_route_handlers()
|
||||
errHandle := csrf.ErrorHandler( CSRFErrorHandler{} )
|
||||
sec := csrf.Secure(false)
|
||||
muxRouter := init_route_handlers()
|
||||
//errHandler := csrf.ErrorHandler( CSRFErrorHandler{} )
|
||||
|
||||
// Terrible. TODO: Get SSL for prod, and then wrap in if(dev) { {
|
||||
csrfSecurityOption := csrf.Secure(false)
|
||||
|
||||
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 {
|
||||
fmt.Println("Fatal Error: ", err)
|
||||
}
|
||||
|
|
|
@ -17,11 +17,10 @@ type News struct {
|
|||
Category_id int
|
||||
Date time.Time
|
||||
Notes string
|
||||
Expoerted bool
|
||||
}
|
||||
|
||||
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:
|
||||
|
@ -95,7 +94,7 @@ func Get(db *sql.DB, id int) (*News, error) {
|
|||
func LoadPage(db *sql.DB, offset, amount int) ([]*News, int, error) {
|
||||
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 {
|
||||
fmt.Println("DB errpr reading LoadPage news: ", err)
|
||||
return nil, 0, err
|
||||
|
@ -174,8 +173,7 @@ func scanNews(rows *sql.Rows) (*News, error) {
|
|||
news := &News{}
|
||||
var url, title, notes sql.NullString
|
||||
var category_id sql.NullInt64
|
||||
var exported sql.NullBool
|
||||
err := rows.Scan(&news.id, &url, &title, &category_id, &news.Date, ¬es, &exported)
|
||||
err := rows.Scan(&news.id, &url, &title, &category_id, &news.Date, ¬es)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading news from DB: " + err.Error())
|
||||
return nil, err
|
||||
|
@ -184,7 +182,6 @@ func scanNews(rows *sql.Rows) (*News, error) {
|
|||
news.Url = nullStringToString(&url)
|
||||
news.Title = nullStringToString(&title)
|
||||
news.Notes = nullStringToString(¬es)
|
||||
news.Expoerted = nullBoolToBool(&exported)
|
||||
|
||||
if category_id.Valid {
|
||||
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.Save(r, w)
|
||||
if popup == "1" {
|
||||
http.Redirect(w, r, "/added", http.StatusFound)
|
||||
http.Redirect(w, r, "/news/added", http.StatusFound)
|
||||
} else {
|
||||
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.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) {
|
||||
|
@ -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) {
|
||||
id, idErr := strconv.Atoi(r.FormValue("id"))
|
||||
id, idErr := strconv.Atoi(mux.Vars(r)["id"])
|
||||
|
||||
if idErr != nil {
|
||||
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})
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
// Mux + CSRF
|
||||
r := mux.NewRouter()
|
||||
|
||||
|
||||
// Basic Handle - static files - no CSRF wrapper
|
||||
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("/fonts/").Handler(http.StripPrefix("/fonts", http.FileServer(http.Dir("fonts/"))))
|
||||
r.HandleFunc("/favicon.ico", ServeFileHandler)
|
||||
|
||||
|
||||
rGet := r.Methods("GET").Subrouter()
|
||||
rPost := r.Methods("POST").Subrouter()
|
||||
|
||||
rGet.HandleFunc("/login", LoginFormHandler)
|
||||
rPost.HandleFunc("/login", LoginPostHandler)
|
||||
|
||||
r.HandleFunc("/logout", userHandler(LogoutHandler))
|
||||
rPost.HandleFunc("/logout", userHandler(LogoutHandler))
|
||||
|
||||
r.HandleFunc("/add", getPostHandler(userHandler(addFormHandler), userHandler(addPostHandler)))
|
||||
r.HandleFunc("/", userHandler(newsFormHandler))
|
||||
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)))
|
||||
rGet.HandleFunc("/news/add", userHandler(addFormHandler))
|
||||
rPost.HandleFunc("/news/add", userHandler(addPostHandler))
|
||||
|
||||
rGet.HandleFunc("/", userHandler(newsFormHandler))
|
||||
rGet.HandleFunc("/news", userHandler(newsFormHandler))
|
||||
|
||||
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))
|
||||
|
||||
r.HandleFunc("/categories", getPostHandler(userHandler(categoriesFormHandler), userHandler(categoriesPostHandler)))
|
||||
r.HandleFunc("/categories/change-parent", userHandler(categoryChangeParentHandler))
|
||||
r.HandleFunc("/categories/add", userHandler(categoryAddHandler))
|
||||
r.HandleFunc("/categories/delete", userHandler(categoryDeleteHandler))
|
||||
|
||||
//http.Handle("/", r)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -32,15 +32,15 @@
|
|||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/add">add</a></li>
|
||||
<li><a href="/export">export</a></li>
|
||||
<li><a href="/news/add">add</a></li>
|
||||
<li><a href="/news/export">export</a></li>
|
||||
<li><a href="/categories">categories</a></li>
|
||||
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{{if .user}}
|
||||
<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}}
|
||||
<li><a href="/login">Log in</a></li>
|
||||
{{end}}
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
|
||||
<div class="row">
|
||||
<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 class="col-xs-6">
|
||||
|
|
|
@ -44,12 +44,12 @@
|
|||
<div class="row">
|
||||
<div class="col-xs-2"> </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>
|
||||
{{end}}
|
||||
|
||||
<!-- JS for the launcher of the add bookmarklet -->
|
||||
{{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}}
|
|
@ -1,9 +1,8 @@
|
|||
{{define "body"}}
|
||||
<h2 class="form-add-heading">{{if eq .mode "add"}}Add Link{{else}}Edit News{{end}}</h2>
|
||||
{{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="id" value="{{.id}}" />
|
||||
<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">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