Database stops being a global variable

With it now every handler creates it's own copy of the session.
This commit is contained in:
Las Zenow 2013-09-23 16:27:31 +02:00
parent cef68a8f6e
commit 41b376992a
12 changed files with 318 additions and 299 deletions

150
admin.go
View file

@ -13,54 +13,54 @@ type settingsData struct {
S Status
}
func settingsHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if sess.User == "" {
notFound(w, r)
func settingsHandler(h handler) {
if h.sess.User == "" {
notFound(h)
return
}
if r.Method == "POST" {
current_pass := r.FormValue("currpass")
pass1 := r.FormValue("password1")
pass2 := r.FormValue("password2")
if h.r.Method == "POST" {
current_pass := h.r.FormValue("currpass")
pass1 := h.r.FormValue("password1")
pass2 := h.r.FormValue("password2")
switch {
case !db.UserValid(sess.User, current_pass):
sess.Notify("Password error!", "The current password given don't match with the user password. Try again", "error")
case !h.db.UserValid(h.sess.User, current_pass):
h.sess.Notify("Password error!", "The current password given don't match with the user password. Try again", "error")
case pass1 != pass2:
sess.Notify("Passwords don't match!", "The new password and the confirmation password don't match. Try again", "error")
h.sess.Notify("Passwords don't match!", "The new password and the confirmation password don't match. Try again", "error")
default:
db.SetPassword(sess.User, pass1)
sess.Notify("Password updated!", "Your new password is correctly set.", "success")
h.db.SetPassword(h.sess.User, pass1)
h.sess.Notify("Password updated!", "Your new password is correctly set.", "success")
}
}
var data settingsData
data.S = GetStatus(w, r)
loadTemplate(w, "settings", data)
data.S = GetStatus(h)
loadTemplate(h.w, "settings", data)
}
func deleteHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if !sess.IsAdmin() {
notFound(w, r)
func deleteHandler(h handler) {
if !h.sess.IsAdmin() {
notFound(h)
return
}
var titles []string
var isNew bool
ids := strings.Split(mux.Vars(r)["ids"], "/")
ids := strings.Split(mux.Vars(h.r)["ids"], "/")
for _, idStr := range ids {
if !bson.IsObjectIdHex(idStr) {
continue
}
id := bson.ObjectIdHex(idStr)
books, _, err := db.GetBooks(bson.M{"_id": id})
books, _, err := h.db.GetBooks(bson.M{"_id": id})
if err != nil {
sess.Notify("Book not found!", "The book with id '"+idStr+"' is not there", "error")
h.sess.Notify("Book not found!", "The book with id '"+idStr+"' is not there", "error")
continue
}
book := books[0]
DeleteBook(book)
db.RemoveBook(id)
DeleteBook(book, h.db)
h.db.RemoveBook(id)
if !book.Active {
isNew = true
@ -68,33 +68,33 @@ func deleteHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
titles = append(titles, book.Title)
}
if titles != nil {
sess.Notify("Removed books!", "The books "+strings.Join(titles, ", ")+" are completly removed", "success")
h.sess.Notify("Removed books!", "The books "+strings.Join(titles, ", ")+" are completly removed", "success")
}
sess.Save(w, r)
h.sess.Save(h.w, h.r)
if isNew {
http.Redirect(w, r, "/new/", http.StatusFound)
http.Redirect(h.w, h.r, "/new/", http.StatusFound)
} else {
http.Redirect(w, r, "/", http.StatusFound)
http.Redirect(h.w, h.r, "/", http.StatusFound)
}
}
func editHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
idStr := mux.Vars(r)["id"]
if !sess.IsAdmin() || !bson.IsObjectIdHex(idStr) {
notFound(w, r)
func editHandler(h handler) {
idStr := mux.Vars(h.r)["id"]
if !h.sess.IsAdmin() || !bson.IsObjectIdHex(idStr) {
notFound(h)
return
}
id := bson.ObjectIdHex(idStr)
books, _, err := db.GetBooks(bson.M{"_id": id})
books, _, err := h.db.GetBooks(bson.M{"_id": id})
if err != nil {
notFound(w, r)
notFound(h)
return
}
var data bookData
data.Book = books[0]
data.S = GetStatus(w, r)
loadTemplate(w, "edit", data)
data.S = GetStatus(h)
loadTemplate(h.w, "edit", data)
}
func cleanEmptyStr(s []string) []string {
@ -107,21 +107,21 @@ func cleanEmptyStr(s []string) []string {
return res
}
func saveHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
idStr := mux.Vars(r)["id"]
if !sess.IsAdmin() || !bson.IsObjectIdHex(idStr) {
notFound(w, r)
func saveHandler(h handler) {
idStr := mux.Vars(h.r)["id"]
if !h.sess.IsAdmin() || !bson.IsObjectIdHex(idStr) {
notFound(h)
return
}
id := bson.ObjectIdHex(idStr)
title := r.FormValue("title")
publisher := r.FormValue("publisher")
date := r.FormValue("date")
description := r.FormValue("description")
author := cleanEmptyStr(r.Form["author"])
subject := cleanEmptyStr(r.Form["subject"])
lang := cleanEmptyStr(r.Form["lang"])
title := h.r.FormValue("title")
publisher := h.r.FormValue("publisher")
date := h.r.FormValue("date")
description := h.r.FormValue("description")
author := cleanEmptyStr(h.r.Form["author"])
subject := cleanEmptyStr(h.r.Form["subject"])
lang := cleanEmptyStr(h.r.Form["lang"])
book := map[string]interface{}{"title": title,
"publisher": publisher,
"date": date,
@ -130,18 +130,18 @@ func saveHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
"subject": subject,
"lang": lang}
book["keywords"] = keywords(book)
err := db.UpdateBook(id, book)
err := h.db.UpdateBook(id, book)
if err != nil {
notFound(w, r)
notFound(h)
return
}
sess.Notify("Book Modified!", "", "success")
sess.Save(w, r)
if db.BookActive(id) {
http.Redirect(w, r, "/book/"+idStr, http.StatusFound)
h.sess.Notify("Book Modified!", "", "success")
h.sess.Save(h.w, h.r)
if h.db.BookActive(id) {
http.Redirect(h.w, h.r, "/book/"+idStr, http.StatusFound)
} else {
http.Redirect(w, r, "/new/", http.StatusFound)
http.Redirect(h.w, h.r, "/new/", http.StatusFound)
}
}
@ -159,28 +159,28 @@ type newData struct {
Prev string
}
func newHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if !sess.IsAdmin() {
notFound(w, r)
func newHandler(h handler) {
if !h.sess.IsAdmin() {
notFound(h)
return
}
err := r.ParseForm()
err := h.r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(h.w, err.Error(), http.StatusInternalServerError)
return
}
page := 0
if len(r.Form["p"]) != 0 {
page, err = strconv.Atoi(r.Form["p"][0])
if len(h.r.Form["p"]) != 0 {
page, err = strconv.Atoi(h.r.Form["p"][0])
if err != nil {
page = 0
}
}
res, num, _ := db.GetNewBooks(NEW_ITEMS_PAGE, page*NEW_ITEMS_PAGE)
res, num, _ := h.db.GetNewBooks(NEW_ITEMS_PAGE, page*NEW_ITEMS_PAGE)
var data newData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.Found = num
if num-NEW_ITEMS_PAGE*page < NEW_ITEMS_PAGE {
data.Books = make([]newBook, num-NEW_ITEMS_PAGE*page)
@ -189,8 +189,8 @@ func newHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
}
for i, b := range res {
data.Books[i].B = b
_, data.Books[i].TitleFound, _ = db.GetBooks(buildQuery("title:"+b.Title), 1)
_, data.Books[i].AuthorFound, _ = db.GetBooks(buildQuery("author:"+strings.Join(b.Author, " author:")), 1)
_, data.Books[i].TitleFound, _ = h.db.GetBooks(buildQuery("title:"+b.Title), 1)
_, data.Books[i].AuthorFound, _ = h.db.GetBooks(buildQuery("author:"+strings.Join(b.Author, " author:")), 1)
}
data.Page = page + 1
if num > (page+1)*NEW_ITEMS_PAGE {
@ -199,40 +199,40 @@ func newHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if page > 0 {
data.Prev = "/new/?p=" + strconv.Itoa(page-1)
}
loadTemplate(w, "new", data)
loadTemplate(h.w, "new", data)
}
func storeHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if !sess.IsAdmin() {
notFound(w, r)
func storeHandler(h handler) {
if !h.sess.IsAdmin() {
notFound(h)
return
}
var titles []string
ids := strings.Split(mux.Vars(r)["ids"], "/")
ids := strings.Split(mux.Vars(h.r)["ids"], "/")
for _, idStr := range ids {
if !bson.IsObjectIdHex(idStr) {
continue
}
id := bson.ObjectIdHex(idStr)
books, _, err := db.GetBooks(bson.M{"_id": id})
books, _, err := h.db.GetBooks(bson.M{"_id": id})
if err != nil {
sess.Notify("Book not found!", "The book with id '"+idStr+"' is not there", "error")
h.sess.Notify("Book not found!", "The book with id '"+idStr+"' is not there", "error")
continue
}
book := books[0]
if err != nil {
sess.Notify("An error ocurred!", err.Error(), "error")
h.sess.Notify("An error ocurred!", err.Error(), "error")
log.Println("Error storing book '", book.Title, "': ", err.Error())
continue
}
db.UpdateBook(id, bson.M{"active": true})
h.db.UpdateBook(id, bson.M{"active": true})
titles = append(titles, book.Title)
}
if titles != nil {
sess.Notify("Store books!", "The books '"+strings.Join(titles, ", ")+"' are stored for public download", "success")
h.sess.Notify("Store books!", "The books '"+strings.Join(titles, ", ")+"' are stored for public download", "success")
}
sess.Save(w, r)
http.Redirect(w, r, "/new/", http.StatusFound)
h.sess.Save(h.w, h.r)
http.Redirect(h.w, h.r, "/new/", http.StatusFound)
}

View file

@ -16,34 +16,32 @@ import (
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"log"
"net/http"
"regexp"
"strings"
)
func coverHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
func coverHandler(h handler) {
vars := mux.Vars(h.r)
if !bson.IsObjectIdHex(vars["id"]) {
notFound(w, r)
notFound(h)
return
}
id := bson.ObjectIdHex(vars["id"])
books, _, err := db.GetBooks(bson.M{"_id": id})
books, _, err := h.db.GetBooks(bson.M{"_id": id})
if err != nil || len(books) == 0 {
notFound(w, r)
notFound(h)
return
}
book := books[0]
if !book.Active {
sess := GetSession(r)
if !sess.IsAdmin() {
notFound(w, r)
if !h.sess.IsAdmin() {
notFound(h)
return
}
}
fs := db.GetFS(FS_IMGS)
fs := h.db.GetFS(FS_IMGS)
var f *mgo.GridFile
if vars["size"] == "small" {
f, err = fs.OpenId(book.CoverSmall)
@ -52,24 +50,24 @@ func coverHandler(w http.ResponseWriter, r *http.Request) {
}
if err != nil {
log.Println("Error while opening image:", err)
notFound(w, r)
notFound(h)
return
}
defer f.Close()
headers := w.Header()
headers := h.w.Header()
headers["Content-Type"] = []string{"image/jpeg"}
io.Copy(w, f)
io.Copy(h.w, f)
}
func GetCover(e *epubgo.Epub, title string) (bson.ObjectId, bson.ObjectId) {
imgId, smallId := coverFromMetadata(e, title)
func GetCover(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bson.ObjectId) {
imgId, smallId := coverFromMetadata(e, title, db)
if imgId != "" {
return imgId, smallId
}
imgId, smallId = searchCommonCoverNames(e, title)
imgId, smallId = searchCommonCoverNames(e, title, db)
if imgId != "" {
return imgId, smallId
}
@ -110,7 +108,7 @@ func GetCover(e *epubgo.Epub, title string) (bson.ObjectId, bson.ObjectId) {
img, err := e.OpenFile(url)
if err == nil {
defer img.Close()
return storeImg(img, title)
return storeImg(img, title, db)
}
}
errNext = it.Next()
@ -118,41 +116,41 @@ func GetCover(e *epubgo.Epub, title string) (bson.ObjectId, bson.ObjectId) {
return "", ""
}
func coverFromMetadata(e *epubgo.Epub, title string) (bson.ObjectId, bson.ObjectId) {
func coverFromMetadata(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bson.ObjectId) {
metaList, _ := e.MetadataAttr("meta")
for _, meta := range metaList {
if meta["name"] == "cover" {
img, err := e.OpenFileId(meta["content"])
if err == nil {
defer img.Close()
return storeImg(img, title)
return storeImg(img, title, db)
}
}
}
return "", ""
}
func searchCommonCoverNames(e *epubgo.Epub, title string) (bson.ObjectId, bson.ObjectId) {
func searchCommonCoverNames(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bson.ObjectId) {
for _, p := range []string{"cover.jpg", "Images/cover.jpg", "images/cover.jpg", "cover.jpeg", "cover1.jpg", "cover1.jpeg"} {
img, err := e.OpenFile(p)
if err == nil {
defer img.Close()
return storeImg(img, title)
return storeImg(img, title, db)
}
}
return "", ""
}
func storeImg(img io.Reader, title string) (bson.ObjectId, bson.ObjectId) {
func storeImg(img io.Reader, title string, db *DB) (bson.ObjectId, bson.ObjectId) {
/* open the files */
fBig, err := createCoverFile(title)
fBig, err := createCoverFile(title, db)
if err != nil {
log.Println("Error creating", title, ":", err.Error())
return "", ""
}
defer fBig.Close()
fSmall, err := createCoverFile(title + "_small")
fSmall, err := createCoverFile(title+"_small", db)
if err != nil {
log.Println("Error creating", title+"_small", ":", err.Error())
return "", ""
@ -189,7 +187,7 @@ func storeImg(img io.Reader, title string) (bson.ObjectId, bson.ObjectId) {
return idBig, idSmall
}
func createCoverFile(title string) (*mgo.GridFile, error) {
func createCoverFile(title string, db *DB) (*mgo.GridFile, error) {
fs := db.GetFS(FS_IMGS)
return fs.Create(title + ".jpg")
}

View file

@ -7,8 +7,6 @@ import (
"time"
)
var db *DB
type Book struct {
Id string `bson:"_id"`
Title string
@ -58,6 +56,12 @@ func (d *DB) Close() {
d.session.Close()
}
func (d *DB) Copy() *DB {
dbCopy := new(DB)
dbCopy.session = d.session.Copy()
return dbCopy
}
func md5Pass(pass string) []byte {
h := md5.New()
hash := h.Sum(([]byte)(PASS_SALT + pass))

40
news.go
View file

@ -14,50 +14,50 @@ type newsEntry struct {
Text string
}
func newsHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
err := r.ParseForm()
func newsHandler(h handler) {
err := h.r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(h.w, err.Error(), http.StatusInternalServerError)
return
}
var data newsData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.S.News = true
data.News = getNews(NUM_NEWS, 0)
data.News = getNews(NUM_NEWS, 0, h.db)
format := r.Form["fmt"]
format := h.r.Form["fmt"]
if (len(format) > 0) && (format[0] == "rss") {
loadTxtTemplate(w, "news_rss.xml", data)
loadTxtTemplate(h.w, "news_rss.xml", data)
} else {
loadTemplate(w, "news", data)
loadTemplate(h.w, "news", data)
}
}
func editNewsHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if !sess.IsAdmin() {
notFound(w, r)
func editNewsHandler(h handler) {
if !h.sess.IsAdmin() {
notFound(h)
return
}
var data statusData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.S.News = true
loadTemplate(w, "edit_news", data)
loadTemplate(h.w, "edit_news", data)
}
func postNewsHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if !sess.IsAdmin() {
notFound(w, r)
func postNewsHandler(h handler) {
if !h.sess.IsAdmin() {
notFound(h)
return
}
text := r.FormValue("text")
db.AddNews(text)
http.Redirect(w, r, "/news/", http.StatusFound)
text := h.r.FormValue("text")
h.db.AddNews(text)
http.Redirect(h.w, h.r, "/news/", http.StatusFound)
}
func getNews(num int, days int) []newsEntry {
func getNews(num int, days int, db *DB) []newsEntry {
dbnews, _ := db.GetNews(num, days)
news := make([]newsEntry, len(dbnews))
for i, n := range dbnews {

View file

@ -129,35 +129,35 @@ func listChapters(nav *epubgo.NavigationIterator, depth int) []chapter {
return chapters
}
func readStartHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
id := mux.Vars(r)["id"]
e, _ := openReadEpub(w, r, sess)
func readStartHandler(h handler) {
id := mux.Vars(h.r)["id"]
e, _ := openReadEpub(h)
if e == nil {
notFound(w, r)
notFound(h)
return
}
defer e.Close()
it, err := e.Spine()
if err != nil {
notFound(w, r)
notFound(h)
return
}
http.Redirect(w, r, "/read/"+id+"/"+it.URL(), http.StatusTemporaryRedirect)
http.Redirect(h.w, h.r, "/read/"+id+"/"+it.URL(), http.StatusTemporaryRedirect)
}
func readHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
id := mux.Vars(r)["id"]
file := mux.Vars(r)["file"]
e, book := openReadEpub(w, r, sess)
func readHandler(h handler) {
id := mux.Vars(h.r)["id"]
file := mux.Vars(h.r)["file"]
e, book := openReadEpub(h)
if e == nil {
notFound(w, r)
notFound(h)
return
}
defer e.Close()
var data readData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.Book = book
if !book.Active {
data.Back = "/new/"
@ -168,66 +168,66 @@ func readHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
data.Next, data.Prev = getNextPrev(e, file, id, "/read/")
data.Chapters = getChapters(e, file, id, "/read/")
data.Content = genLink(id, "/content/", file)
loadTemplate(w, "read", data)
loadTemplate(h.w, "read", data)
}
func openReadEpub(w http.ResponseWriter, r *http.Request, sess *Session) (*epubgo.Epub, Book) {
func openReadEpub(h handler) (*epubgo.Epub, Book) {
var book Book
id := mux.Vars(r)["id"]
id := mux.Vars(h.r)["id"]
if !bson.IsObjectIdHex(id) {
return nil, book
}
books, _, err := db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)})
books, _, err := h.db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)})
if err != nil || len(books) == 0 {
return nil, book
}
book = books[0]
if !book.Active {
if !sess.IsAdmin() {
if !h.sess.IsAdmin() {
return nil, book
}
}
e, err := OpenBook(book.File)
e, err := OpenBook(book.File, h.db)
if err != nil {
return nil, book
}
return e, book
}
func contentHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
vars := mux.Vars(r)
func contentHandler(h handler) {
vars := mux.Vars(h.r)
id := vars["id"]
file := vars["file"]
if file == "" || !bson.IsObjectIdHex(id) {
notFound(w, r)
notFound(h)
return
}
books, _, err := db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)})
books, _, err := h.db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)})
if err != nil || len(books) == 0 {
notFound(w, r)
notFound(h)
return
}
book := books[0]
if !book.Active {
if !sess.IsAdmin() {
notFound(w, r)
if !h.sess.IsAdmin() {
notFound(h)
return
}
}
e, err := OpenBook(book.File)
e, err := OpenBook(book.File, h.db)
if err != nil {
notFound(w, r)
notFound(h)
return
}
defer e.Close()
html, err := e.OpenFile(file)
if err != nil {
notFound(w, r)
notFound(h)
return
}
defer html.Close()
io.Copy(w, html)
io.Copy(h.w, html)
}

View file

@ -35,25 +35,25 @@ type searchData struct {
Prev string
}
func searchHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
err := r.ParseForm()
func searchHandler(h handler) {
err := h.r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(h.w, err.Error(), http.StatusInternalServerError)
return
}
req := strings.Join(r.Form["q"], " ")
req := strings.Join(h.r.Form["q"], " ")
page := 0
if len(r.Form["p"]) != 0 {
page, err = strconv.Atoi(r.Form["p"][0])
if len(h.r.Form["p"]) != 0 {
page, err = strconv.Atoi(h.r.Form["p"][0])
if err != nil {
page = 0
}
}
items_page := itemsPage(r)
res, num, _ := db.GetBooks(buildQuery(req), items_page, page*items_page)
items_page := itemsPage(h.r)
res, num, _ := h.db.GetBooks(buildQuery(req), items_page, page*items_page)
var data searchData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.S.Search = req
data.Books = res
data.ItemsPage = items_page
@ -66,11 +66,11 @@ func searchHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
data.Prev = "/search/?q=" + req + "&p=" + strconv.Itoa(page-1) + "&num=" + strconv.Itoa(items_page)
}
format := r.Form["fmt"]
format := h.r.Form["fmt"]
if (len(format) > 0) && (format[0] == "rss") {
loadTxtTemplate(w, "search_rss.xml", data)
loadTxtTemplate(h.w, "search_rss.xml", data)
} else {
loadTemplate(w, "search", data)
loadTemplate(h.w, "search", data)
}
}

View file

@ -21,7 +21,7 @@ type Session struct {
S *sessions.Session
}
func GetSession(r *http.Request) (s *Session) {
func GetSession(r *http.Request, db *DB) (s *Session) {
s = new(Session)
var err error
s.S, err = sesStore.Get(r, "session")

View file

@ -9,16 +9,30 @@ import (
"time"
)
func InitStats() {
statsChannel = make(chan statsRequest, CHAN_SIZE)
go statsWorker()
type handler struct {
w http.ResponseWriter
r *http.Request
sess *Session
db *DB
}
func GatherStats(function func(http.ResponseWriter, *http.Request, *Session)) func(http.ResponseWriter, *http.Request) {
func InitStats(database *DB) {
statsChannel = make(chan statsRequest, CHAN_SIZE)
go statsWorker(database)
}
func GatherStats(function func(handler), database *DB) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
sess := GetSession(r)
function(w, r, sess)
statsChannel <- statsRequest{bson.Now(), mux.Vars(r), sess, r}
var h handler
h.db = database.Copy()
defer h.db.Close()
h.w = w
h.r = r
h.sess = GetSession(r, h.db)
function(h)
statsChannel <- statsRequest{bson.Now(), mux.Vars(r), h.sess, r}
}
}
@ -31,7 +45,10 @@ type statsRequest struct {
r *http.Request
}
func statsWorker() {
func statsWorker(database *DB) {
db := database.Copy()
defer db.Close()
for req := range statsChannel {
stats := make(map[string]interface{})
appendFiles(req.r, stats)
@ -44,15 +61,15 @@ func statsWorker() {
}
}
func statsHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
func statsHandler(h handler) {
var data statsData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.S.Stats = true
data.Hourly = getHourlyVisits()
data.Daily = getDailyVisits()
data.Monthly = getMonthlyVisits()
data.Hourly = getHourlyVisits(h.db)
data.Daily = getDailyVisits(h.db)
data.Monthly = getMonthlyVisits(h.db)
loadTemplate(w, "stats", data)
loadTemplate(h.w, "stats", data)
}
type statsData struct {
@ -67,7 +84,7 @@ type visitData struct {
Count int
}
func getHourlyVisits() []visitData {
func getHourlyVisits(db *DB) []visitData {
const numDays = 2
var visits []visitData
@ -84,7 +101,7 @@ func getHourlyVisits() []visitData {
return visits
}
func getDailyVisits() []visitData {
func getDailyVisits(db *DB) []visitData {
const numDays = 30
var visits []visitData
@ -101,7 +118,7 @@ func getDailyVisits() []visitData {
return visits
}
func getMonthlyVisits() []visitData {
func getMonthlyVisits(db *DB) []visitData {
const numDays = 365
var visits []visitData

View file

@ -10,7 +10,7 @@ import (
"strings"
)
func OpenBook(id bson.ObjectId) (*epubgo.Epub, error) {
func OpenBook(id bson.ObjectId, db *DB) (*epubgo.Epub, error) {
fs := db.GetFS(FS_BOOKS)
f, err := fs.OpenId(id)
if err != nil {
@ -24,7 +24,7 @@ func OpenBook(id bson.ObjectId) (*epubgo.Epub, error) {
return epubgo.Load(reader, int64(len(buff)))
}
func StoreNewFile(name string, file io.Reader) (bson.ObjectId, int64, error) {
func StoreNewFile(name string, file io.Reader, db *DB) (bson.ObjectId, int64, error) {
fs := db.GetFS(FS_BOOKS)
fw, err := fs.Create(name)
if err != nil {
@ -37,24 +37,24 @@ func StoreNewFile(name string, file io.Reader) (bson.ObjectId, int64, error) {
return id, size, err
}
func DeleteFile(id bson.ObjectId) error {
func DeleteFile(id bson.ObjectId, db *DB) error {
fs := db.GetFS(FS_BOOKS)
return fs.RemoveId(id)
}
func DeleteCover(id bson.ObjectId) error {
func DeleteCover(id bson.ObjectId, db *DB) error {
fs := db.GetFS(FS_IMGS)
return fs.RemoveId(id)
}
func DeleteBook(book Book) {
func DeleteBook(book Book, db *DB) {
if book.Cover != "" {
DeleteCover(book.Cover)
DeleteCover(book.Cover, db)
}
if book.CoverSmall != "" {
DeleteCover(book.CoverSmall)
DeleteCover(book.CoverSmall, db)
}
DeleteFile(book.File)
DeleteFile(book.File, db)
}
func cleanStr(str string) string {

View file

@ -22,15 +22,14 @@ type Status struct {
Help bool
}
func GetStatus(w http.ResponseWriter, r *http.Request) Status {
func GetStatus(h handler) Status {
var s Status
sess := GetSession(r)
s.BaseURL = "http://" + r.Host
s.FullURL = s.BaseURL + r.RequestURI
s.User = sess.User
s.IsAdmin = sess.IsAdmin()
s.Notif = sess.GetNotif()
sess.Save(w, r)
s.BaseURL = "http://" + h.r.Host
s.FullURL = s.BaseURL + h.r.RequestURI
s.User = h.sess.User
s.IsAdmin = h.sess.IsAdmin()
s.Notif = h.sess.GetNotif()
h.sess.Save(h.w, h.r)
return s
}

View file

@ -13,41 +13,41 @@ type statusData struct {
S Status
}
func aboutHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
func aboutHandler(h handler) {
var data statusData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.S.About = true
loadTemplate(w, "about", data)
loadTemplate(h.w, "about", data)
}
func helpHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
func helpHandler(h handler) {
var data statusData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.S.Help = true
loadTemplate(w, "help", data)
loadTemplate(h.w, "help", data)
}
func logoutHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
sess.LogOut()
sess.Notify("Log out!", "Bye bye "+sess.User, "success")
sess.Save(w, r)
log.Println("User", sess.User, "log out")
http.Redirect(w, r, "/", http.StatusFound)
func logoutHandler(h handler) {
h.sess.LogOut()
h.sess.Notify("Log out!", "Bye bye "+h.sess.User, "success")
h.sess.Save(h.w, h.r)
log.Println("User", h.sess.User, "log out")
http.Redirect(h.w, h.r, "/", http.StatusFound)
}
func loginHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
user := r.FormValue("user")
pass := r.FormValue("pass")
if db.UserValid(user, pass) {
func loginHandler(h handler) {
user := h.r.FormValue("user")
pass := h.r.FormValue("pass")
if h.db.UserValid(user, pass) {
log.Println("User", user, "log in")
sess.LogIn(user)
sess.Notify("Successful login!", "Welcome "+user, "success")
h.sess.LogIn(user)
h.sess.Notify("Successful login!", "Welcome "+user, "success")
} else {
log.Println("User", user, "bad user or password")
sess.Notify("Invalid login!", "user or password invalid", "error")
h.sess.Notify("Invalid login!", "user or password invalid", "error")
}
sess.Save(w, r)
http.Redirect(w, r, r.Referer(), http.StatusFound)
h.sess.Save(h.w, h.r)
http.Redirect(h.w, h.r, h.r.Referer(), http.StatusFound)
}
type bookData struct {
@ -56,62 +56,61 @@ type bookData struct {
Description []string
}
func bookHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
idStr := mux.Vars(r)["id"]
func bookHandler(h handler) {
idStr := mux.Vars(h.r)["id"]
if !bson.IsObjectIdHex(idStr) {
notFound(w, r)
notFound(h)
return
}
var data bookData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
id := bson.ObjectIdHex(idStr)
books, _, err := db.GetBooks(bson.M{"_id": id})
books, _, err := h.db.GetBooks(bson.M{"_id": id})
if err != nil || len(books) == 0 {
notFound(w, r)
notFound(h)
return
}
data.Book = books[0]
data.Description = strings.Split(data.Book.Description, "\n")
loadTemplate(w, "book", data)
loadTemplate(h.w, "book", data)
}
func downloadHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
idStr := mux.Vars(r)["id"]
func downloadHandler(h handler) {
idStr := mux.Vars(h.r)["id"]
if !bson.IsObjectIdHex(idStr) {
notFound(w, r)
notFound(h)
return
}
id := bson.ObjectIdHex(idStr)
books, _, err := db.GetBooks(bson.M{"_id": id})
books, _, err := h.db.GetBooks(bson.M{"_id": id})
if err != nil || len(books) == 0 {
notFound(w, r)
notFound(h)
return
}
book := books[0]
if !book.Active {
sess := GetSession(r)
if !sess.IsAdmin() {
notFound(w, r)
if !h.sess.IsAdmin() {
notFound(h)
return
}
}
fs := db.GetFS(FS_BOOKS)
fs := h.db.GetFS(FS_BOOKS)
f, err := fs.OpenId(book.File)
if err != nil {
notFound(w, r)
notFound(h)
return
}
defer f.Close()
headers := w.Header()
headers := h.w.Header()
headers["Content-Type"] = []string{"application/epub+zip"}
headers["Content-Disposition"] = []string{"attachment; filename=\"" + f.Name() + "\""}
io.Copy(w, f)
io.Copy(h.w, f)
}
type indexData struct {
@ -124,68 +123,68 @@ type indexData struct {
News []newsEntry
}
func indexHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
func indexHandler(h handler) {
var data indexData
data.Tags, _ = db.GetTags(TAGS_DISPLAY)
data.S = GetStatus(w, r)
data.Tags, _ = h.db.GetTags(TAGS_DISPLAY)
data.S = GetStatus(h)
data.S.Home = true
data.Books, data.Count, _ = db.GetBooks(bson.M{"active": true}, 6)
data.VisitedBooks, _ = db.GetVisitedBooks(6)
data.DownloadedBooks, _ = db.GetDownloadedBooks(6)
data.News = getNews(1, DAYS_NEWS_INDEXPAGE)
loadTemplate(w, "index", data)
data.Books, data.Count, _ = h.db.GetBooks(bson.M{"active": true}, 6)
data.VisitedBooks, _ = h.db.GetVisitedBooks(6)
data.DownloadedBooks, _ = h.db.GetDownloadedBooks(6)
data.News = getNews(1, DAYS_NEWS_INDEXPAGE, h.db)
loadTemplate(h.w, "index", data)
}
func notFound(w http.ResponseWriter, r *http.Request) {
func notFound(h handler) {
var data statusData
data.S = GetStatus(w, r)
w.WriteHeader(http.StatusNotFound)
loadTemplate(w, "404", data)
data.S = GetStatus(h)
h.w.WriteHeader(http.StatusNotFound)
loadTemplate(h.w, "404", data)
}
func main() {
db = initDB()
db := initDB()
defer db.Close()
InitStats()
InitUpload()
InitStats(db)
InitUpload(db)
setUpRouter()
setUpRouter(db)
panic(http.ListenAndServe(":"+PORT, nil))
}
func setUpRouter() {
func setUpRouter(db *DB) {
r := mux.NewRouter()
var notFoundHandler http.HandlerFunc
notFoundHandler = GatherStats(func(w http.ResponseWriter, r *http.Request, sess *Session) { notFound(w, r) })
notFoundHandler = GatherStats(notFound, db)
r.NotFoundHandler = notFoundHandler
r.HandleFunc("/", GatherStats(indexHandler))
r.HandleFunc("/book/{id:[0-9a-fA-F]+}", GatherStats(bookHandler))
r.HandleFunc("/search/", GatherStats(searchHandler))
r.HandleFunc("/upload/", GatherStats(uploadHandler)).Methods("GET")
r.HandleFunc("/upload/", GatherStats(uploadPostHandler)).Methods("POST")
r.HandleFunc("/login/", GatherStats(loginHandler)).Methods("POST")
r.HandleFunc("/logout/", GatherStats(logoutHandler))
r.HandleFunc("/new/", GatherStats(newHandler))
r.HandleFunc("/store/{ids:([0-9a-fA-F]+/)+}", GatherStats(storeHandler))
r.HandleFunc("/delete/{ids:([0-9a-fA-F]+/)+}", GatherStats(deleteHandler))
r.HandleFunc("/read/{id:[0-9a-fA-F]+}", GatherStats(readStartHandler))
r.HandleFunc("/read/{id:[0-9a-fA-F]+}/{file:.*}", GatherStats(readHandler))
r.HandleFunc("/content/{id:[0-9a-fA-F]+}/{file:.*}", GatherStats(contentHandler))
r.HandleFunc("/edit/{id:[0-9a-fA-F]+}", GatherStats(editHandler))
r.HandleFunc("/save/{id:[0-9a-fA-F]+}", GatherStats(saveHandler)).Methods("POST")
r.HandleFunc("/about/", GatherStats(aboutHandler))
r.HandleFunc("/help/", GatherStats(helpHandler))
r.HandleFunc("/download/{id:[0-9a-fA-F]+}/{epub:.*}", GatherStats(downloadHandler))
r.HandleFunc("/cover/{id:[0-9a-fA-F]+}/{size}/{img:.*}", coverHandler)
r.HandleFunc("/settings/", GatherStats(settingsHandler))
r.HandleFunc("/stats/", GatherStats(statsHandler))
r.HandleFunc("/news/", GatherStats(newsHandler))
r.HandleFunc("/news/edit", GatherStats(editNewsHandler)).Methods("GET")
r.HandleFunc("/news/edit", GatherStats(postNewsHandler)).Methods("POST")
r.HandleFunc("/", GatherStats(indexHandler, db))
r.HandleFunc("/book/{id:[0-9a-fA-F]+}", GatherStats(bookHandler, db))
r.HandleFunc("/search/", GatherStats(searchHandler, db))
r.HandleFunc("/upload/", GatherStats(uploadHandler, db)).Methods("GET")
r.HandleFunc("/upload/", GatherStats(uploadPostHandler, db)).Methods("POST")
r.HandleFunc("/login/", GatherStats(loginHandler, db)).Methods("POST")
r.HandleFunc("/logout/", GatherStats(logoutHandler, db))
r.HandleFunc("/new/", GatherStats(newHandler, db))
r.HandleFunc("/store/{ids:([0-9a-fA-F]+/)+}", GatherStats(storeHandler, db))
r.HandleFunc("/delete/{ids:([0-9a-fA-F]+/)+}", GatherStats(deleteHandler, db))
r.HandleFunc("/read/{id:[0-9a-fA-F]+}", GatherStats(readStartHandler, db))
r.HandleFunc("/read/{id:[0-9a-fA-F]+}/{file:.*}", GatherStats(readHandler, db))
r.HandleFunc("/content/{id:[0-9a-fA-F]+}/{file:.*}", GatherStats(contentHandler, db))
r.HandleFunc("/edit/{id:[0-9a-fA-F]+}", GatherStats(editHandler, db))
r.HandleFunc("/save/{id:[0-9a-fA-F]+}", GatherStats(saveHandler, db)).Methods("POST")
r.HandleFunc("/about/", GatherStats(aboutHandler, db))
r.HandleFunc("/help/", GatherStats(helpHandler, db))
r.HandleFunc("/download/{id:[0-9a-fA-F]+}/{epub:.*}", GatherStats(downloadHandler, db))
r.HandleFunc("/cover/{id:[0-9a-fA-F]+}/{size}/{img:.*}", GatherStats(coverHandler, db))
r.HandleFunc("/settings/", GatherStats(settingsHandler, db))
r.HandleFunc("/stats/", GatherStats(statsHandler, db))
r.HandleFunc("/news/", GatherStats(newsHandler, db))
r.HandleFunc("/news/edit", GatherStats(editNewsHandler, db)).Methods("GET")
r.HandleFunc("/news/edit", GatherStats(postNewsHandler, db)).Methods("POST")
h := http.FileServer(http.Dir(IMG_PATH))
r.Handle("/img/{img}", http.StripPrefix("/img/", h))
h = http.FileServer(http.Dir(CSS_PATH))

View file

@ -6,13 +6,12 @@ import (
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"strings"
)
func InitUpload() {
func InitUpload(database *DB) {
uploadChannel = make(chan uploadRequest, CHAN_SIZE)
go uploadWorker()
go uploadWorker(database)
}
var uploadChannel chan uploadRequest
@ -22,13 +21,16 @@ type uploadRequest struct {
filename string
}
func uploadWorker() {
func uploadWorker(database *DB) {
db := database.Copy()
defer db.Close()
for req := range uploadChannel {
processFile(req)
processFile(req, db)
}
}
func processFile(req uploadRequest) {
func processFile(req uploadRequest, db *DB) {
defer req.file.Close()
epub, err := openMultipartEpub(req.file)
@ -38,10 +40,10 @@ func processFile(req uploadRequest) {
}
defer epub.Close()
book := parseFile(epub)
book := parseFile(epub, db)
title, _ := book["title"].(string)
req.file.Seek(0, 0)
id, size, err := StoreNewFile(title+".epub", req.file)
id, size, err := StoreNewFile(title+".epub", req.file, db)
if err != nil {
log.Println("Error storing book (", title, "):", err)
return
@ -57,16 +59,16 @@ func processFile(req uploadRequest) {
log.Println("File uploaded:", req.filename)
}
func uploadPostHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
func uploadPostHandler(h handler) {
problem := false
r.ParseMultipartForm(20000000)
filesForm := r.MultipartForm.File["epub"]
h.r.ParseMultipartForm(20000000)
filesForm := h.r.MultipartForm.File["epub"]
for _, f := range filesForm {
file, err := f.Open()
if err != nil {
log.Println("Can not open uploaded file", f.Filename, ":", err)
sess.Notify("Upload problem!", "There was a problem with book "+f.Filename, "error")
h.sess.Notify("Upload problem!", "There was a problem with book "+f.Filename, "error")
problem = true
continue
}
@ -75,19 +77,19 @@ func uploadPostHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
if !problem {
if len(filesForm) > 0 {
sess.Notify("Upload successful!", "Thank you for your contribution", "success")
h.sess.Notify("Upload successful!", "Thank you for your contribution", "success")
} else {
sess.Notify("Upload problem!", "No books where uploaded.", "error")
h.sess.Notify("Upload problem!", "No books where uploaded.", "error")
}
}
uploadHandler(w, r, sess)
uploadHandler(h)
}
func uploadHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
func uploadHandler(h handler) {
var data uploadData
data.S = GetStatus(w, r)
data.S = GetStatus(h)
data.S.Upload = true
loadTemplate(w, "upload", data)
loadTemplate(h.w, "upload", data)
}
type uploadData struct {
@ -100,7 +102,7 @@ func openMultipartEpub(file multipart.File) (*epubgo.Epub, error) {
return epubgo.Load(reader, int64(len(buff)))
}
func parseFile(epub *epubgo.Epub) map[string]interface{} {
func parseFile(epub *epubgo.Epub, db *DB) map[string]interface{} {
book := map[string]interface{}{}
for _, m := range epub.MetadataFields() {
data, err := epub.Metadata(m)
@ -133,7 +135,7 @@ func parseFile(epub *epubgo.Epub) map[string]interface{} {
}
title, _ := book["title"].(string)
book["file"] = nil
cover, coverSmall := GetCover(epub, title)
cover, coverSmall := GetCover(epub, title, db)
if cover != "" {
book["cover"] = cover
book["coversmall"] = coverSmall