diff --git a/admin.go b/admin.go index f5e2d78..4dc3749 100644 --- a/admin.go +++ b/admin.go @@ -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) } diff --git a/cover.go b/cover.go index b2f1bc0..a2ff26d 100644 --- a/cover.go +++ b/cover.go @@ -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") } diff --git a/database.go b/database.go index deadfcf..ea1b65d 100644 --- a/database.go +++ b/database.go @@ -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)) diff --git a/news.go b/news.go index 9f62939..3f37cc3 100644 --- a/news.go +++ b/news.go @@ -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 { diff --git a/reader.go b/reader.go index fa9dc49..902ac9a 100644 --- a/reader.go +++ b/reader.go @@ -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) } diff --git a/search.go b/search.go index 08888c8..9f94543 100644 --- a/search.go +++ b/search.go @@ -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) } } diff --git a/session.go b/session.go index 4db5a5d..e958cdc 100644 --- a/session.go +++ b/session.go @@ -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") diff --git a/stats.go b/stats.go index ff14419..c502de5 100644 --- a/stats.go +++ b/stats.go @@ -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 diff --git a/store.go b/store.go index ea638fb..5b0ee8c 100644 --- a/store.go +++ b/store.go @@ -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 { diff --git a/template.go b/template.go index eb40b9b..cc97cd3 100644 --- a/template.go +++ b/template.go @@ -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 } diff --git a/trantor.go b/trantor.go index 89b1d67..4b7f8fe 100644 --- a/trantor.go +++ b/trantor.go @@ -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)) diff --git a/upload.go b/upload.go index 35a0bcc..fed15ed 100644 --- a/upload.go +++ b/upload.go @@ -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